From e3314a0adf200ea95a4133a26a8820bd2c4f30dc Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Tue, 12 Mar 2024 14:54:28 +0200 Subject: [PATCH 1/8] Make PostStatsTitleRow, TopTotalsPostStatsRow, PostStatsEmptyCellHeaderRow Hashable --- .../Stats/SiteStatsTableViewCells.swift | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift index 8781837ed91b..76900f8136a7 100644 --- a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift +++ b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift @@ -5,7 +5,7 @@ import DGCharts // MARK: - Shared Rows // TODO: Remove with SiteStatsPeriodViewModelDeprecated -struct OverviewRow: ImmuTableRow { +struct OverviewRow: StatsHashableImmuTableRow { typealias CellType = OverviewCell @@ -122,8 +122,7 @@ struct CellHeaderRow: StatsHashableImmuTableRow { } } -struct TableFooterRow: ImmuTableRow { - +struct TableFooterRow: HashableImmutableRow { typealias CellType = StatsTableFooter static let cell: ImmuTableCell = { @@ -136,6 +135,10 @@ struct TableFooterRow: ImmuTableRow { // No configuration needed. // This method is needed to satisfy ImmuTableRow protocol requirements. } + + static func == (lhs: TableFooterRow, rhs: TableFooterRow) -> Bool { + return true + } } // MARK: - Insights Rows @@ -582,8 +585,7 @@ struct CountriesMapRow: StatsHashableImmuTableRow { // MARK: - Post Stats Rows -struct PostStatsTitleRow: ImmuTableRow { - +struct PostStatsTitleRow: HashableImmutableRow { typealias CellType = PostStatsTitleCell static let cell: ImmuTableCell = { @@ -603,9 +605,14 @@ struct PostStatsTitleRow: ImmuTableRow { cell.configure(postTitle: postTitle, postURL: postURL, postStatsDelegate: postStatsDelegate) } + + static func == (lhs: PostStatsTitleRow, rhs: PostStatsTitleRow) -> Bool { + return lhs.postTitle == rhs.postTitle && + lhs.postURL == rhs.postURL + } } -struct TopTotalsPostStatsRow: ImmuTableRow { +struct TopTotalsPostStatsRow: HashableImmutableRow { typealias CellType = TopTotalsCell @@ -632,10 +639,16 @@ struct TopTotalsPostStatsRow: ImmuTableRow { postStatsDelegate: postStatsDelegate, limitRowsDisplayed: limitRowsDisplayed) } -} -struct PostStatsEmptyCellHeaderRow: ImmuTableRow { + static func == (lhs: TopTotalsPostStatsRow, rhs: TopTotalsPostStatsRow) -> Bool { + return lhs.itemSubtitle == rhs.itemSubtitle && + lhs.dataSubtitle == rhs.dataSubtitle && + lhs.dataRows == rhs.dataRows && + lhs.limitRowsDisplayed == rhs.limitRowsDisplayed + } +} +struct PostStatsEmptyCellHeaderRow: HashableImmutableRow { typealias CellType = StatsCellHeader static let cell: ImmuTableCell = { @@ -652,6 +665,10 @@ struct PostStatsEmptyCellHeaderRow: ImmuTableRow { cell.configure(statSection: .postStatsGraph) } + + static func == (lhs: PostStatsEmptyCellHeaderRow, rhs: PostStatsEmptyCellHeaderRow) -> Bool { + return true + } } // MARK: - Detail Rows From 87b92c243b2ff62ed757b6e954610a0f1823c3d3 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Tue, 12 Mar 2024 14:54:48 +0200 Subject: [PATCH 2/8] Create HashableImmutableRow for more generic rows --- .../Stats/Period Stats/StatsTrafficModels.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/StatsTrafficModels.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/StatsTrafficModels.swift index 19a81d18cf4d..a68a319b5dff 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/StatsTrafficModels.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/StatsTrafficModels.swift @@ -8,7 +8,15 @@ struct StatsTrafficSection: Hashable { } } -protocol StatsHashableImmuTableRow: ImmuTableRow, Hashable { +protocol HashableImmutableRow: ImmuTableRow, Hashable {} + +extension HashableImmutableRow { + func hash(into hasher: inout Hasher) { + hasher.combine(String(describing: type(of: self))) + } +} + +protocol StatsHashableImmuTableRow: HashableImmutableRow { var statSection: StatSection? { get } } From d75877fbd24d916e31cf4a055c78f1490d0bcca2 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Tue, 12 Mar 2024 14:55:13 +0200 Subject: [PATCH 3/8] Update PostStatsViewModel to make snapshot instead of viewModel --- .../Post Stats/PostStatsViewModel.swift | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift index dc082e4ac20c..9d4c57372911 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift @@ -67,15 +67,15 @@ class PostStatsViewModel: Observable { // MARK: - Table View - func tableViewModel() -> ImmuTable { + func tableViewSnapshot() -> ImmuTableDiffableDataSourceSnapshot { if let postId = postID, store.fetchingFailed(for: .postStats(postID: postId)) { - return ImmuTable.Empty + return ImmuTableDiffableDataSourceSnapshot() } postStats = store.getPostStats(for: postID) return blocks(for: store.postStatsFetchingStatuses(for: postID)) { - var tableRows = [ImmuTableRow]() + var tableRows = [any HashableImmutableRow]() tableRows.append(titleTableRow()) tableRows.append(contentsOf: overviewTableRows()) tableRows.append(contentsOf: yearsTableRows()) @@ -111,14 +111,14 @@ private extension PostStatsViewModel { // MARK: - Create Table Rows - func titleTableRow() -> ImmuTableRow { + func titleTableRow() -> any HashableImmutableRow { return PostStatsTitleRow(postTitle: postTitle ?? StatSection.noPostTitle, postURL: postURL, postStatsDelegate: postStatsDelegate) } - func overviewTableRows() -> [ImmuTableRow] { - var tableRows = [ImmuTableRow]() + func overviewTableRows() -> [any HashableImmutableRow] { + var tableRows = [any HashableImmutableRow]() tableRows.append(PostStatsEmptyCellHeaderRow()) let lastTwoWeeks = postStats?.lastTwoWeeks ?? [] @@ -145,8 +145,8 @@ private extension PostStatsViewModel { return tableRows } - func yearsTableRows(forAverages: Bool = false) -> [ImmuTableRow] { - var tableRows = [ImmuTableRow]() + func yearsTableRows(forAverages: Bool = false) -> [any HashableImmutableRow] { + var tableRows = [any HashableImmutableRow]() let statSection = forAverages ? StatSection.postStatsAverageViews : StatSection.postStatsMonthsYears @@ -199,8 +199,8 @@ private extension PostStatsViewModel { return yearRows } - func recentWeeksTableRows() -> [ImmuTableRow] { - var tableRows = [ImmuTableRow]() + func recentWeeksTableRows() -> [any HashableImmutableRow] { + var tableRows = [any HashableImmutableRow]() tableRows.append(CellHeaderRow(statSection: StatSection.postStatsRecentWeeks)) tableRows.append(TopTotalsPostStatsRow(itemSubtitle: StatSection.postStatsRecentWeeks.itemSubtitle, @@ -289,15 +289,21 @@ private extension PostStatsViewModel { return (currentCount, difference, roundedPercentage) } - func blocks(for state: StoreFetchingStatus, block: () -> [ImmuTableRow]) -> ImmuTable { + func blocks(for state: StoreFetchingStatus, block: () -> [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { + struct PostStatsSection: Hashable { + let rows: [AnyHashableImmuTableRow] + } + if postStats != nil { - return ImmuTable(sections: [ - ImmuTableSection( - rows: block()) - ]) + var snapshot = ImmuTableDiffableDataSourceSnapshot() + let rows = block().map { AnyHashableImmuTableRow(immuTableRow: $0) } + let section = PostStatsSection(rows: rows) + snapshot.appendSections([section]) + snapshot.appendItems(rows, toSection: section) + return snapshot } - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() let sections: [StatSection] = [.postStatsMonthsYears, .postStatsAverageViews, .postStatsRecentWeeks] @@ -311,15 +317,17 @@ private extension PostStatsViewModel { rows.append(StatsGhostChartImmutableRow()) sections.forEach { rows.append(CellHeaderRow(statSection: $0)) - rows.append(StatsGhostTopImmutableRow()) + rows.append(StatsGhostTopImmutableRow(statSection: $0)) } case .error: - return ImmuTable.Empty + return ImmuTableDiffableDataSourceSnapshot() } - return ImmuTable(sections: [ - ImmuTableSection( - rows: rows) - ]) + var snapshot = ImmuTableDiffableDataSourceSnapshot() + let hashableRows = rows.map { AnyHashableImmuTableRow(immuTableRow: $0) } + let section = PostStatsSection(rows: hashableRows) + snapshot.appendSections([section]) + snapshot.appendItems(hashableRows, toSection: section) + return snapshot } } From 4d027d5311c4057a11e5b233d05f973c2bab4023 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Tue, 12 Mar 2024 14:55:31 +0200 Subject: [PATCH 4/8] Update PostStatsTableViewController to support diffable data source --- .../Post Stats/PostStatsTableViewController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift index 0da2ae0bb267..d729e10f0e3f 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift @@ -25,8 +25,8 @@ class PostStatsTableViewController: UITableViewController, StoryboardLoadable { private let store = StatsPeriodStore() private var changeReceipt: Receipt? - private lazy var tableHandler: ImmuTableViewHandler = { - return ImmuTableViewHandler(takeOver: self, with: self) + private lazy var tableHandler: ImmuTableDiffableViewHandler = { + return ImmuTableDiffableViewHandler(takeOver: self, with: self) }() // MARK: - View @@ -152,7 +152,7 @@ private extension PostStatsTableViewController { return } - tableHandler.viewModel = viewModel.tableViewModel() + tableHandler.diffableDataSource.apply(viewModel.tableViewSnapshot(), animatingDifferences: false) refreshControl?.endRefreshing() if viewModel.fetchDataHasFailed() { @@ -173,7 +173,7 @@ private extension PostStatsTableViewController { viewModel.refreshPostStats(postID: postID, selectedDate: selectedDate) if forceUpdate { - tableHandler.viewModel = viewModel.tableViewModel() + tableHandler.diffableDataSource.apply(viewModel.tableViewSnapshot(), animatingDifferences: false) } } From 579e1216f225d6f427d129544e9fd6aff574bc55 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Tue, 12 Mar 2024 16:10:02 +0200 Subject: [PATCH 5/8] Use diffable data source in SiteStatsDetailTableView --- .../Stats/Insights/TabbedTotalsCell.swift | 2 +- .../GhostViews/StatsGhostTableViewRows.swift | 1 + .../SiteStatsDetailTableViewController.swift | 6 +- .../SiteStatsDetailsViewModel.swift | 93 ++++++++++--------- .../Stats/SiteStatsTableViewCells.swift | 50 ++++++++-- 5 files changed, 96 insertions(+), 56 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/TabbedTotalsCell.swift b/WordPress/Classes/ViewRelated/Stats/Insights/TabbedTotalsCell.swift index c19210320a3f..66d704845a73 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/TabbedTotalsCell.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/TabbedTotalsCell.swift @@ -1,6 +1,6 @@ import UIKit -struct TabData: FilterTabBarItem { +struct TabData: FilterTabBarItem, Equatable { var tabTitle: String var itemSubtitle: String var dataSubtitle: String diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift index c9535484b284..1ce5dcc8788d 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift @@ -108,6 +108,7 @@ struct StatsGhostDetailRow: StatsRowGhostable { var hideTopBorder = false var isLastRow = false var enableTopPadding = false + var index: Int = 0 func configureCell(_ cell: UITableViewCell) { DispatchQueue.main.async { diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift index 633d19caee31..e28130785bde 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift @@ -31,8 +31,8 @@ class SiteStatsDetailTableViewController: UITableViewController, StoryboardLoada private let insightsStore = StatsInsightsStore() private let periodStore = StatsPeriodStore() - private lazy var tableHandler: ImmuTableViewHandler = { - return ImmuTableViewHandler(takeOver: self) + private lazy var tableHandler: ImmuTableDiffableViewHandler = { + return ImmuTableDiffableViewHandler(takeOver: self, with: nil) }() private var postID: Int? @@ -174,7 +174,7 @@ private extension SiteStatsDetailTableViewController { return } - tableHandler.viewModel = viewModel.tableViewModel() + tableHandler.diffableDataSource.apply(viewModel.tableViewSnapshot(), animatingDifferences: false) refreshControl?.endRefreshing() if viewModel.fetchDataHasFailed() { diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift index 1982fa6449f4..c7047ef17240 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift @@ -1,6 +1,10 @@ import Foundation import WordPressFlux +struct SiteStatsDetailsSection: Hashable { + let rows: [AnyHashableImmuTableRow] +} + /// The view model used by SiteStatsDetailTableViewController to show /// all data for a selected stat. /// @@ -147,14 +151,14 @@ class SiteStatsDetailsViewModel: Observable { // MARK: - Table Model - func tableViewModel() -> ImmuTable { + func tableViewSnapshot() -> ImmuTableDiffableDataSourceSnapshot { guard let statSection = statSection, let detailsDelegate = detailsDelegate else { - return ImmuTable.Empty + return ImmuTableDiffableDataSourceSnapshot() } if fetchDataHasFailed() { - return ImmuTable.Empty + return ImmuTableDiffableDataSourceSnapshot() } switch statSection { @@ -162,7 +166,7 @@ class SiteStatsDetailsViewModel: Observable { let status = statSection == .insightsFollowersWordPress ? insightsStore.allDotComFollowersStatus : insightsStore.allEmailFollowersStatus let type: InsightType = statSection == .insightsFollowersWordPress ? .allDotComFollowers : .allEmailFollowers return insightsImmuTable(for: (type, status)) { - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() let selectedIndex = statSection == .insightsFollowersWordPress ? 0 : 1 let wpTabData = tabDataForFollowerType(.insightsFollowersWordPress) let emailTabData = tabDataForFollowerType(.insightsFollowersEmail) @@ -180,7 +184,7 @@ class SiteStatsDetailsViewModel: Observable { } case .insightsCommentsAuthors, .insightsCommentsPosts: return insightsImmuTable(for: (.allComments, insightsStore.allCommentsInsightStatus)) { - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() let selectedIndex = statSection == .insightsCommentsAuthors ? 0 : 1 let authorsTabData = tabDataForCommentType(.insightsCommentsAuthors) let postsTabData = tabDataForCommentType(.insightsCommentsPosts) @@ -198,7 +202,7 @@ class SiteStatsDetailsViewModel: Observable { } case .insightsTagsAndCategories: return insightsImmuTable(for: (.allTagsAndCategories, insightsStore.allTagsAndCategoriesStatus)) { - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.insightsTagsAndCategories.itemSubtitle, dataSubtitle: StatSection.insightsTagsAndCategories.dataSubtitle)) rows.append(contentsOf: tagsAndCategoriesRows()) @@ -210,7 +214,7 @@ class SiteStatsDetailsViewModel: Observable { } case .periodPostsAndPages: return periodImmuTable(for: periodStore.topPostsAndPagesStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.periodPostsAndPages.itemSubtitle, dataSubtitle: StatSection.periodPostsAndPages.dataSubtitle)) rows.append(contentsOf: postsAndPagesRows(for: status)) @@ -218,7 +222,7 @@ class SiteStatsDetailsViewModel: Observable { } case .periodSearchTerms: return periodImmuTable(for: periodStore.topSearchTermsStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.periodSearchTerms.itemSubtitle, dataSubtitle: StatSection.periodSearchTerms.dataSubtitle)) rows.append(contentsOf: searchTermsRows(for: status)) @@ -226,7 +230,7 @@ class SiteStatsDetailsViewModel: Observable { } case .periodVideos: return periodImmuTable(for: periodStore.topVideosStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.periodVideos.itemSubtitle, dataSubtitle: StatSection.periodVideos.dataSubtitle)) rows.append(contentsOf: videosRows(for: status)) @@ -234,7 +238,7 @@ class SiteStatsDetailsViewModel: Observable { } case .periodClicks: return periodImmuTable(for: periodStore.topClicksStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.periodClicks.itemSubtitle, dataSubtitle: StatSection.periodClicks.dataSubtitle)) rows.append(contentsOf: clicksRows(for: status)) @@ -242,7 +246,7 @@ class SiteStatsDetailsViewModel: Observable { } case .periodAuthors: return periodImmuTable(for: periodStore.topAuthorsStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.periodAuthors.itemSubtitle, dataSubtitle: StatSection.periodAuthors.dataSubtitle)) rows.append(contentsOf: authorsRows(for: status)) @@ -250,7 +254,7 @@ class SiteStatsDetailsViewModel: Observable { } case .periodReferrers: return periodImmuTable(for: periodStore.topReferrersStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.periodReferrers.itemSubtitle, dataSubtitle: StatSection.periodReferrers.dataSubtitle)) rows.append(contentsOf: referrersRows(for: status)) @@ -258,7 +262,7 @@ class SiteStatsDetailsViewModel: Observable { } case .periodCountries: return periodImmuTable(for: periodStore.topCountriesStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() let map = countriesMap() if !map.data.isEmpty { rows.append(CountriesMapRow(countriesMap: map)) @@ -270,14 +274,14 @@ class SiteStatsDetailsViewModel: Observable { } case .periodPublished: return periodImmuTable(for: periodStore.topPublishedStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: "", dataSubtitle: "")) rows.append(contentsOf: publishedRows(for: status)) return rows } case .periodFileDownloads: return periodImmuTable(for: periodStore.topFileDownloadsStatus) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.periodFileDownloads.itemSubtitle, dataSubtitle: StatSection.periodFileDownloads.dataSubtitle)) rows.append(contentsOf: fileDownloadsRows(for: status)) @@ -285,7 +289,7 @@ class SiteStatsDetailsViewModel: Observable { } case .postStatsMonthsYears: return periodImmuTable(for: periodStore.postStatsFetchingStatuses(for: postID)) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesCountriesHeaderRow(itemSubtitle: StatSection.postStatsMonthsYears.itemSubtitle, dataSubtitle: StatSection.postStatsMonthsYears.dataSubtitle)) rows.append(contentsOf: postStatsRows(status: status)) @@ -293,14 +297,14 @@ class SiteStatsDetailsViewModel: Observable { } case .postStatsAverageViews: return periodImmuTable(for: periodStore.postStatsFetchingStatuses(for: postID)) { status in - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() rows.append(DetailSubtitlesCountriesHeaderRow(itemSubtitle: StatSection.postStatsAverageViews.itemSubtitle, dataSubtitle: StatSection.postStatsAverageViews.dataSubtitle)) rows.append(contentsOf: postStatsRows(forAverages: true, status: status)) return rows } default: - return ImmuTable.Empty + return ImmuTableDiffableDataSourceSnapshot() } } @@ -529,7 +533,7 @@ private extension SiteStatsDetailsViewModel { // MARK: - Tags and Categories - func tagsAndCategoriesRows() -> [ImmuTableRow] { + func tagsAndCategoriesRows() -> [any HashableImmutableRow] { return expandableDataRowsFor(tagsAndCategoriesRowData()) } @@ -670,7 +674,7 @@ private extension SiteStatsDetailsViewModel { // MARK: - Clicks - func clicksRows(for status: StoreFetchingStatus) -> [ImmuTableRow] { + func clicksRows(for status: StoreFetchingStatus) -> [any HashableImmutableRow] { return expandableDataRowsFor(clicksRowData(), status: status) } @@ -690,7 +694,7 @@ private extension SiteStatsDetailsViewModel { // MARK: - Authors - func authorsRows(for status: StoreFetchingStatus) -> [ImmuTableRow] { + func authorsRows(for status: StoreFetchingStatus) -> [any HashableImmutableRow] { return expandableDataRowsFor(authorsRowData(), status: status) } @@ -712,7 +716,7 @@ private extension SiteStatsDetailsViewModel { // MARK: - Referrers - func referrersRows(for status: StoreFetchingStatus) -> [ImmuTableRow] { + func referrersRows(for status: StoreFetchingStatus) -> [any HashableImmutableRow] { return expandableDataRowsFor(referrersRowData(), status: status) } @@ -801,7 +805,7 @@ private extension SiteStatsDetailsViewModel { // MARK: - Post Stats - func postStatsRows(forAverages: Bool = false, status: StoreFetchingStatus) -> [ImmuTableRow] { + func postStatsRows(forAverages: Bool = false, status: StoreFetchingStatus) -> [any HashableImmutableRow] { return expandableDataRowsFor(postStatsRowData(forAverages: forAverages), status: status) } @@ -856,8 +860,8 @@ private extension SiteStatsDetailsViewModel { return detailDataRows } - func expandableDataRowsFor(_ rowsData: [StatsTotalRowData], status: StoreFetchingStatus = .idle) -> [ImmuTableRow] { - var detailDataRows = [ImmuTableRow]() + func expandableDataRowsFor(_ rowsData: [StatsTotalRowData], status: StoreFetchingStatus = .idle) -> [any HashableImmutableRow] { + var detailDataRows = [any HashableImmutableRow]() for (idx, rowData) in rowsData.enumerated() { @@ -977,15 +981,21 @@ private extension SiteStatsDetailsViewModel { return StatsDataHelper.expandedRowLabelsDetails[statSection]?.contains(rowData.name) ?? false } - func insightsImmuTable(for row: (type: InsightType, status: StoreFetchingStatus), rowsBlock: () -> [ImmuTableRow]) -> ImmuTable { + func snapshotFromRows(_ rows: [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { + var snapshot = ImmuTableDiffableDataSourceSnapshot() + let rows = rows.map { AnyHashableImmuTableRow(immuTableRow: $0) } + let section = SiteStatsDetailsSection(rows: rows) + snapshot.appendSections([section]) + snapshot.appendItems(rows, toSection: section) + return snapshot + } + + func insightsImmuTable(for row: (type: InsightType, status: StoreFetchingStatus), rowsBlock: () -> [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { if insightsStore.containsCachedData(for: row.type) { - return ImmuTable(sections: [ - ImmuTableSection( - rows: rowsBlock()) - ]) + return snapshotFromRows(rowsBlock()) } - var rows = [ImmuTableRow]() + var rows = [any HashableImmutableRow]() switch row.status { case .loading, .idle: @@ -996,14 +1006,11 @@ private extension SiteStatsDetailsViewModel { break } - return ImmuTable(sections: [ - ImmuTableSection( - rows: rows) - ]) + return snapshotFromRows(rows) } - func periodImmuTable(for status: StoreFetchingStatus, rowsBlock: (StoreFetchingStatus) -> [ImmuTableRow]) -> ImmuTable { - var rows = [ImmuTableRow]() + func periodImmuTable(for status: StoreFetchingStatus, rowsBlock: (StoreFetchingStatus) -> [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { + var rows = [any HashableImmutableRow]() switch status { case .loading, .idle: @@ -1024,20 +1031,18 @@ private extension SiteStatsDetailsViewModel { break } - return ImmuTable(sections: [ - ImmuTableSection( - rows: rows) - ]) + return snapshotFromRows(rows) } - func getGhostSequence() -> [ImmuTableRow] { - var rows = [ImmuTableRow]() + func getGhostSequence() -> [any HashableImmutableRow] { + var rows = [any HashableImmutableRow]() rows.append(StatsGhostTopHeaderImmutableRow()) rows.append(contentsOf: (Constants.Sequence.rows).map { index in let isLastRow = index == Constants.Sequence.maxRowCount return StatsGhostDetailRow(hideTopBorder: true, isLastRow: isLastRow, - enableTopPadding: true) + enableTopPadding: true, + index: index) }) return rows } diff --git a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift index 76900f8136a7..95f73925c038 100644 --- a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift +++ b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift @@ -673,7 +673,7 @@ struct PostStatsEmptyCellHeaderRow: HashableImmutableRow { // MARK: - Detail Rows -struct DetailDataRow: ImmuTableRow { +struct DetailDataRow: HashableImmutableRow { typealias CellType = DetailDataCell @@ -698,9 +698,15 @@ struct DetailDataRow: ImmuTableRow { hideIndentedSeparator: hideIndentedSeparator, hideFullSeparator: hideFullSeparator) } + + static func == (lhs: DetailDataRow, rhs: DetailDataRow) -> Bool { + return lhs.rowData == rhs.rowData && + lhs.hideIndentedSeparator == rhs.hideIndentedSeparator && + lhs.hideFullSeparator == rhs.hideFullSeparator + } } -struct DetailExpandableRow: ImmuTableRow { +struct DetailExpandableRow: HashableImmutableRow { typealias CellType = DetailDataCell @@ -730,9 +736,16 @@ struct DetailExpandableRow: ImmuTableRow { expanded: expanded) } + + static func == (lhs: DetailExpandableRow, rhs: DetailExpandableRow) -> Bool { + return lhs.rowData == rhs.rowData && + lhs.hideIndentedSeparator == rhs.hideIndentedSeparator && + lhs.hideFullSeparator == rhs.hideFullSeparator && + lhs.expanded == rhs.expanded + } } -struct DetailExpandableChildRow: ImmuTableRow { +struct DetailExpandableChildRow: HashableImmutableRow { typealias CellType = DetailDataCell @@ -760,10 +773,16 @@ struct DetailExpandableChildRow: ImmuTableRow { isChildRow: true, showChildRowImage: showImage) } -} -struct DetailSubtitlesHeaderRow: ImmuTableRow { + static func == (lhs: DetailExpandableChildRow, rhs: DetailExpandableChildRow) -> Bool { + return lhs.rowData == rhs.rowData && + lhs.hideIndentedSeparator == rhs.hideIndentedSeparator && + lhs.hideFullSeparator == rhs.hideFullSeparator && + lhs.showImage == rhs.showImage + } +} +struct DetailSubtitlesHeaderRow: HashableImmutableRow { typealias CellType = TopTotalsCell static let cell: ImmuTableCell = { @@ -782,9 +801,14 @@ struct DetailSubtitlesHeaderRow: ImmuTableRow { cell.configure(itemSubtitle: itemSubtitle, dataSubtitle: dataSubtitle, dataRows: [], forDetails: true) } + + static func == (lhs: DetailSubtitlesHeaderRow, rhs: DetailSubtitlesHeaderRow) -> Bool { + return lhs.itemSubtitle == rhs.itemSubtitle && + lhs.dataSubtitle == rhs.dataSubtitle + } } -struct DetailSubtitlesCountriesHeaderRow: ImmuTableRow { +struct DetailSubtitlesCountriesHeaderRow: HashableImmutableRow { typealias CellType = CountriesCell @@ -804,10 +828,14 @@ struct DetailSubtitlesCountriesHeaderRow: ImmuTableRow { cell.configure(itemSubtitle: itemSubtitle, dataSubtitle: dataSubtitle, dataRows: [], forDetails: true) } -} -struct DetailSubtitlesTabbedHeaderRow: ImmuTableRow { + static func == (lhs: DetailSubtitlesCountriesHeaderRow, rhs: DetailSubtitlesCountriesHeaderRow) -> Bool { + return lhs.itemSubtitle == rhs.itemSubtitle && + lhs.dataSubtitle == rhs.dataSubtitle + } +} +struct DetailSubtitlesTabbedHeaderRow: HashableImmutableRow { typealias CellType = TabbedTotalsCell static let cell: ImmuTableCell = { @@ -832,6 +860,12 @@ struct DetailSubtitlesTabbedHeaderRow: ImmuTableRow { selectedIndex: selectedIndex, forDetails: true) } + + static func == (lhs: DetailSubtitlesTabbedHeaderRow, rhs: DetailSubtitlesTabbedHeaderRow) -> Bool { + return lhs.tabsData == rhs.tabsData && + lhs.showTotalCount == rhs.showTotalCount && + lhs.selectedIndex == rhs.selectedIndex + } } struct StatsErrorRow: StatsHashableImmuTableRow { From b139672ea750f98ed6556d87e103f6126aceb4d0 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Wed, 13 Mar 2024 11:32:42 +0200 Subject: [PATCH 6/8] Rename StatsTrafficModels to HashableImmutableRow --- ...rafficModels.swift => HashableImmutableRow.swift} | 8 -------- .../Period Stats/SiteStatsPeriodViewModel.swift | 8 ++++++++ WordPress/WordPress.xcodeproj/project.pbxproj | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) rename WordPress/Classes/ViewRelated/Stats/Period Stats/{StatsTrafficModels.swift => HashableImmutableRow.swift} (87%) diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/StatsTrafficModels.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift similarity index 87% rename from WordPress/Classes/ViewRelated/Stats/Period Stats/StatsTrafficModels.swift rename to WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift index a68a319b5dff..35cc404d5fbc 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/StatsTrafficModels.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift @@ -1,13 +1,5 @@ import Foundation -struct StatsTrafficSection: Hashable { - let periodType: PeriodType - - init(periodType: PeriodType) { - self.periodType = periodType - } -} - protocol HashableImmutableRow: ImmuTableRow, Hashable {} extension HashableImmutableRow { diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift index c783f2f764d0..427bb8257049 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift @@ -1,6 +1,14 @@ import Foundation import WordPressFlux +struct StatsTrafficSection: Hashable { + let periodType: PeriodType + + init(periodType: PeriodType) { + self.periodType = periodType + } +} + final class SiteStatsPeriodViewModel: Observable { // MARK: - Properties diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index d209168709d2..b04deb6b100b 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -149,7 +149,7 @@ 011F52DB2A1CA53300B04114 /* CheckoutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011F52D92A1CA53300B04114 /* CheckoutViewController.swift */; }; 012041032AAAFE3A00E7C707 /* WidgetCenter+JetpackWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012041022AAAFE3900E7C707 /* WidgetCenter+JetpackWidgets.swift */; }; 012041042AAAFE3A00E7C707 /* WidgetCenter+JetpackWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012041022AAAFE3900E7C707 /* WidgetCenter+JetpackWidgets.swift */; }; - 0127F8242B85EAEC00A8DDEA /* StatsTrafficModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D7EBA32B7A4BBD00F14992 /* StatsTrafficModels.swift */; }; + 0127F8242B85EAEC00A8DDEA /* HashableImmutableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D7EBA32B7A4BBD00F14992 /* HashableImmutableRow.swift */; }; 01281E9A2A0456CB00464F8F /* DomainsSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01281E992A0456CB00464F8F /* DomainsSelectionScreen.swift */; }; 01281E9C2A051EEA00464F8F /* MenuNavigationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01281E9B2A051EEA00464F8F /* MenuNavigationTests.swift */; }; 01281E9D2A051EEA00464F8F /* MenuNavigationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01281E9B2A051EEA00464F8F /* MenuNavigationTests.swift */; }; @@ -232,7 +232,7 @@ 01D2FF652AA77F790038E040 /* LockScreenTodayViewsVisitorsStatWidgetConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D2FF632AA77F790038E040 /* LockScreenTodayViewsVisitorsStatWidgetConfig.swift */; }; 01D2FF682AA780DC0038E040 /* LockScreenAllTimeViewsVisitorsStatWidgetConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D2FF662AA780DC0038E040 /* LockScreenAllTimeViewsVisitorsStatWidgetConfig.swift */; }; 01D2FF6B2AA782720038E040 /* LockScreenAllTimePostsBestViewsStatWidgetConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D2FF692AA782720038E040 /* LockScreenAllTimePostsBestViewsStatWidgetConfig.swift */; }; - 01D7EBA42B7A4BBD00F14992 /* StatsTrafficModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D7EBA32B7A4BBD00F14992 /* StatsTrafficModels.swift */; }; + 01D7EBA42B7A4BBD00F14992 /* HashableImmutableRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D7EBA32B7A4BBD00F14992 /* HashableImmutableRow.swift */; }; 01D7EBA82B7CD07E00F14992 /* SiteStatsPeriodViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D7EBA72B7CD07E00F14992 /* SiteStatsPeriodViewModelTests.swift */; }; 01DBFD8729BDCBF200F3720F /* JetpackNativeConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DBFD8629BDCBF200F3720F /* JetpackNativeConnectionService.swift */; }; 01DBFD8829BDCBF200F3720F /* JetpackNativeConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DBFD8629BDCBF200F3720F /* JetpackNativeConnectionService.swift */; }; @@ -5979,7 +5979,7 @@ 01D2FF632AA77F790038E040 /* LockScreenTodayViewsVisitorsStatWidgetConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenTodayViewsVisitorsStatWidgetConfig.swift; sourceTree = ""; }; 01D2FF662AA780DC0038E040 /* LockScreenAllTimeViewsVisitorsStatWidgetConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenAllTimeViewsVisitorsStatWidgetConfig.swift; sourceTree = ""; }; 01D2FF692AA782720038E040 /* LockScreenAllTimePostsBestViewsStatWidgetConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenAllTimePostsBestViewsStatWidgetConfig.swift; sourceTree = ""; }; - 01D7EBA32B7A4BBD00F14992 /* StatsTrafficModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsTrafficModels.swift; sourceTree = ""; }; + 01D7EBA32B7A4BBD00F14992 /* HashableImmutableRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashableImmutableRow.swift; sourceTree = ""; }; 01D7EBA72B7CD07E00F14992 /* SiteStatsPeriodViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteStatsPeriodViewModelTests.swift; sourceTree = ""; }; 01DBFD8629BDCBF200F3720F /* JetpackNativeConnectionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackNativeConnectionService.swift; sourceTree = ""; }; 01DC4CD72B5FC8CE000110E5 /* SiteStatsPeriodTableViewControllerDeprecated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteStatsPeriodTableViewControllerDeprecated.swift; sourceTree = ""; }; @@ -14572,7 +14572,7 @@ 981676D3221B7A2C00B81C3F /* Countries */, 984B138D21F65F860004B6A2 /* SiteStatsPeriodTableViewController.swift */, 984B139121F66AC50004B6A2 /* SiteStatsPeriodViewModel.swift */, - 01D7EBA32B7A4BBD00F14992 /* StatsTrafficModels.swift */, + 01D7EBA32B7A4BBD00F14992 /* HashableImmutableRow.swift */, ); path = "Period Stats"; sourceTree = ""; @@ -22160,7 +22160,7 @@ 3FCCAA1523F4A1A3004064C0 /* UIBarButtonItem+MeBarButton.swift in Sources */, FFEECFFC2084DE2B009B8CDB /* PostSettingsViewController+FeaturedImageUpload.swift in Sources */, D8212CB120AA64E1008E8AE8 /* ReaderLikeAction.swift in Sources */, - 01D7EBA42B7A4BBD00F14992 /* StatsTrafficModels.swift in Sources */, + 01D7EBA42B7A4BBD00F14992 /* HashableImmutableRow.swift in Sources */, D8212CC920AA87E5008E8AE8 /* ReaderMenuAction.swift in Sources */, 593F26611CAB00CA00F14073 /* PostSharingController.swift in Sources */, 2FA37B1A215724E900C80377 /* LongPressGestureLabel.swift in Sources */, @@ -25652,7 +25652,7 @@ FABB25082602FC2C00C8785C /* ManagedPerson.swift in Sources */, FABB250A2602FC2C00C8785C /* ReplyTextView.swift in Sources */, FABB250C2602FC2C00C8785C /* MenuItemEditingFooterView.m in Sources */, - 0127F8242B85EAEC00A8DDEA /* StatsTrafficModels.swift in Sources */, + 0127F8242B85EAEC00A8DDEA /* HashableImmutableRow.swift in Sources */, FABB250D2602FC2C00C8785C /* ReaderCrossPostCell.swift in Sources */, 93E6336A272AC532009DACF8 /* LoginEpilogueChooseSiteTableViewCell.swift in Sources */, C7234A4F2832C47D0045C63F /* QRLoginVerifyAuthorizationViewController.swift in Sources */, From ca13e4da088a8cc75f5da6e631ec245d8a6338d3 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Wed, 13 Mar 2024 11:59:48 +0200 Subject: [PATCH 7/8] Reduce code duplication by creating a method to build a single section snapshot --- .../Period Stats/HashableImmutableRow.swift | 17 +++++++++++++++++ .../Post Stats/PostStatsViewModel.swift | 18 ++---------------- .../SiteStatsDetailsViewModel.swift | 19 +++---------------- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift index 35cc404d5fbc..20fd9e475b04 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift @@ -24,3 +24,20 @@ extension StatsHashableImmuTableRow { hasher.combine(String(describing: type(of: self))) } } + +// MARK: - Helpers + +struct AnyHashableSectionWithRows: Hashable { + let rows: [AnyHashableImmuTableRow] +} + +extension ImmuTableDiffableDataSourceSnapshot { + static func singleSectionSnapshot(_ rows: [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { + var snapshot = ImmuTableDiffableDataSourceSnapshot() + let rows = rows.map { AnyHashableImmuTableRow(immuTableRow: $0) } + let section = AnyHashableSectionWithRows(rows: rows) + snapshot.appendSections([section]) + snapshot.appendItems(rows, toSection: section) + return snapshot + } +} diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift index 9d4c57372911..25b9519c9513 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift @@ -290,17 +290,8 @@ private extension PostStatsViewModel { } func blocks(for state: StoreFetchingStatus, block: () -> [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { - struct PostStatsSection: Hashable { - let rows: [AnyHashableImmuTableRow] - } - if postStats != nil { - var snapshot = ImmuTableDiffableDataSourceSnapshot() - let rows = block().map { AnyHashableImmuTableRow(immuTableRow: $0) } - let section = PostStatsSection(rows: rows) - snapshot.appendSections([section]) - snapshot.appendItems(rows, toSection: section) - return snapshot + return .singleSectionSnapshot(block()) } var rows = [any HashableImmutableRow]() @@ -323,11 +314,6 @@ private extension PostStatsViewModel { return ImmuTableDiffableDataSourceSnapshot() } - var snapshot = ImmuTableDiffableDataSourceSnapshot() - let hashableRows = rows.map { AnyHashableImmuTableRow(immuTableRow: $0) } - let section = PostStatsSection(rows: hashableRows) - snapshot.appendSections([section]) - snapshot.appendItems(hashableRows, toSection: section) - return snapshot + return .singleSectionSnapshot(rows) } } diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift index c7047ef17240..f37e27625e69 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift @@ -1,10 +1,6 @@ import Foundation import WordPressFlux -struct SiteStatsDetailsSection: Hashable { - let rows: [AnyHashableImmuTableRow] -} - /// The view model used by SiteStatsDetailTableViewController to show /// all data for a selected stat. /// @@ -981,18 +977,9 @@ private extension SiteStatsDetailsViewModel { return StatsDataHelper.expandedRowLabelsDetails[statSection]?.contains(rowData.name) ?? false } - func snapshotFromRows(_ rows: [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { - var snapshot = ImmuTableDiffableDataSourceSnapshot() - let rows = rows.map { AnyHashableImmuTableRow(immuTableRow: $0) } - let section = SiteStatsDetailsSection(rows: rows) - snapshot.appendSections([section]) - snapshot.appendItems(rows, toSection: section) - return snapshot - } - func insightsImmuTable(for row: (type: InsightType, status: StoreFetchingStatus), rowsBlock: () -> [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { if insightsStore.containsCachedData(for: row.type) { - return snapshotFromRows(rowsBlock()) + return .singleSectionSnapshot(rowsBlock()) } var rows = [any HashableImmutableRow]() @@ -1006,7 +993,7 @@ private extension SiteStatsDetailsViewModel { break } - return snapshotFromRows(rows) + return .singleSectionSnapshot(rows) } func periodImmuTable(for status: StoreFetchingStatus, rowsBlock: (StoreFetchingStatus) -> [any HashableImmutableRow]) -> ImmuTableDiffableDataSourceSnapshot { @@ -1031,7 +1018,7 @@ private extension SiteStatsDetailsViewModel { break } - return snapshotFromRows(rows) + return .singleSectionSnapshot(rows) } func getGhostSequence() -> [any HashableImmutableRow] { From 0358075b4b33fba1d885ef6215d9bfadcbe85d68 Mon Sep 17 00:00:00 2001 From: Povilas Staskus Date: Wed, 20 Mar 2024 12:05:00 +0200 Subject: [PATCH 8/8] Update PostStatsTableViewController when toggling using diffable data source apply methods Usage of performBatchUpdates is not allowed if table view depends on a diffable data source and can result in crashes --- .../Post Stats/PostStatsTableViewController.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift index d729e10f0e3f..5504e8ac9357 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift @@ -178,8 +178,11 @@ private extension PostStatsTableViewController { } func applyTableUpdates() { - tableView.performBatchUpdates({ - }) + guard let viewModel = viewModel else { + return + } + + tableHandler.diffableDataSource.apply(viewModel.tableViewSnapshot(), animatingDifferences: false) } }