diff --git a/Podfile b/Podfile index a5dda3b97bfc..93dd406677a9 100644 --- a/Podfile +++ b/Podfile @@ -56,7 +56,7 @@ end def wordpress_kit # pod 'WordPressKit', '~> 17.0.0' - pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '14aa53a2e1cfa764e3e9e3e91d1f39f4ef09e098' + pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '77aee91d607cb8b86d4356c0aebfb3977ff1fcc7' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', path: '../WordPressKit-iOS' diff --git a/Podfile.lock b/Podfile.lock index 84ba5bea8268..fa52f0297ff6 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -121,7 +121,7 @@ DEPENDENCIES: - SwiftLint (= 0.54.0) - WordPress-Editor-iOS (~> 1.19.11) - WordPressAuthenticator (>= 9.0.8, ~> 9.0) - - WordPressKit (from `https://github.com/wordpress-mobile/WordPressKit-iOS.git`, commit `14aa53a2e1cfa764e3e9e3e91d1f39f4ef09e098`) + - WordPressKit (from `https://github.com/wordpress-mobile/WordPressKit-iOS.git`, commit `77aee91d607cb8b86d4356c0aebfb3977ff1fcc7`) - WordPressShared (from `https://github.com/wordpress-mobile/WordPress-iOS-Shared.git`, commit `688ee5e4efddc1fc23626626ef17b7e929bdafb0`) - WordPressUI (~> 1.16) - ZendeskSupportSDK (= 5.3.0) @@ -177,7 +177,7 @@ EXTERNAL SOURCES: Gutenberg: :podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.117.0.podspec WordPressKit: - :commit: 14aa53a2e1cfa764e3e9e3e91d1f39f4ef09e098 + :commit: 77aee91d607cb8b86d4356c0aebfb3977ff1fcc7 :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git WordPressShared: :commit: 688ee5e4efddc1fc23626626ef17b7e929bdafb0 @@ -188,7 +188,7 @@ CHECKOUT OPTIONS: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 WordPressKit: - :commit: 14aa53a2e1cfa764e3e9e3e91d1f39f4ef09e098 + :commit: 77aee91d607cb8b86d4356c0aebfb3977ff1fcc7 :git: https://github.com/wordpress-mobile/WordPressKit-iOS.git WordPressShared: :commit: 688ee5e4efddc1fc23626626ef17b7e929bdafb0 @@ -239,6 +239,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: d170fa8e270b2a32bef9dcdcabff5b8f1a5deced -PODFILE CHECKSUM: 4ac1d35f8415bdc8d4c8e39d6aa7a9f6dab5933d +PODFILE CHECKSUM: 4bbf2ae7c80a5f39db237e7c3514872e9f7eb3ca COCOAPODS: 1.15.2 diff --git a/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift b/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift index 6c0c00c7ab24..5ae03b926140 100644 --- a/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift +++ b/WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift @@ -34,6 +34,7 @@ case postStatsMonthsYears case postStatsAverageViews case postStatsRecentWeeks + case subscribersChart case subscribersEmailsSummary case subscribersList @@ -144,6 +145,8 @@ return PostStatsHeaders.averageViewsPerDay case .postStatsRecentWeeks: return PostStatsHeaders.recentWeeks + case .subscribersChart: + return SubscribersHeaders.chart case .subscribersEmailsSummary: return SubscribersHeaders.emailsSummaryStats case .subscribersList: @@ -433,6 +436,7 @@ } struct SubscribersHeaders { + static let chart = NSLocalizedString("stats.subscribers.chart.title", value: "Subscribers", comment: "Stats 'Subscribers' card header, contains chart") static let emailsSummaryStats = NSLocalizedString("stats.subscribers.emailsSummaryCard.title", value: "Emails", comment: "Stats 'Emails' card header") static let subscribersList = NSLocalizedString("stats.subscribers.subscribersListCard.title", value: "Subscribers", comment: "Stats 'Subscribers' card header") } diff --git a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift index 8c494c8c3052..0a1fa9fd74c1 100644 --- a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift +++ b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift @@ -77,6 +77,35 @@ struct ViewsVisitorsRow: StatsHashableImmuTableRow { } } +struct SubscriberChartRow: StatsHashableImmuTableRow { + typealias CellType = StatsSubscribersChartCell + + static let cell: ImmuTableCell = { + return ImmuTableCell.nib(CellType.defaultNib, CellType.self) + }() + + let action: ImmuTableAction? = nil + let history: [StatsSubscribersSummaryData.SubscriberData] + let chartData: LineChartDataConvertible + let chartStyling: LineChartStyling + let xAxisDates: [Date] + let statSection: StatSection? + + static func == (lhs: SubscriberChartRow, rhs: SubscriberChartRow) -> Bool { + return lhs.xAxisDates == rhs.xAxisDates && + lhs.history == rhs.history + } + + func configureCell(_ cell: UITableViewCell) { + + guard let cell = cell as? CellType else { + return + } + + cell.configure(row: self) + } +} + struct CellHeaderRow: StatsHashableImmuTableRow { typealias CellType = StatsCellHeader diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersCache.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersCache.swift index 850b04af9197..c313a21efe7b 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersCache.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersCache.swift @@ -27,6 +27,10 @@ final class StatsSubscribersCache { return .init(record: .subscribersEmailsSummary, key: "\(quantity) \(sortField) \(sortOrder)", siteID: siteId) } + static func chartSummary(unit: String, siteId: NSNumber) -> CacheKey { + return .init(record: .subscribersChart, key: unit, siteID: siteId) + } + static func subscribersList(quantity: Int, siteId: NSNumber) -> CacheKey { return .init(record: .subscribersList, key: "\(quantity)", siteID: siteId) } diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersChartCell.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersChartCell.swift new file mode 100644 index 000000000000..728d1cf49cd2 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersChartCell.swift @@ -0,0 +1,57 @@ + +import UIKit + +class StatsSubscribersChartCell: StatsBaseCell, NibLoadable { + private typealias Style = WPStyleGuide.Stats + + @IBOutlet weak var chartView: UIView! + + private var chartData: LineChartDataConvertible! + private var chartStyling: LineChartStyling! + private var xAxisDates: [Date]! + + override func awakeFromNib() { + super.awakeFromNib() + + Style.configureCell(self) + } + + func configure(row: SubscriberChartRow) { + statSection = row.statSection + + self.chartData = row.chartData + self.chartStyling = row.chartStyling + self.xAxisDates = row.xAxisDates + + configureChartView() + } +} + +private extension StatsSubscribersChartCell { + + func configureChartView() { + let configuration = StatsLineChartConfiguration(data: chartData, + styling: chartStyling, + analyticsGranularity: .days, + indexToHighlight: 0, + xAxisDates: xAxisDates) + let lineChartView = StatsLineChartView(configuration: configuration) + + resetChartContainerView() + chartView.addSubview(lineChartView) + chartView.accessibilityElements = [lineChartView] + + NSLayoutConstraint.activate([ + lineChartView.leadingAnchor.constraint(equalTo: chartView.leadingAnchor), + lineChartView.trailingAnchor.constraint(equalTo: chartView.trailingAnchor), + lineChartView.topAnchor.constraint(equalTo: chartView.topAnchor), + lineChartView.bottomAnchor.constraint(equalTo: chartView.bottomAnchor) + ]) + } + + func resetChartContainerView() { + for subview in chartView.subviews { + subview.removeFromSuperview() + } + } +} diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersChartCell.xib b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersChartCell.xib new file mode 100644 index 000000000000..255292322f4a --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersChartCell.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersLineChart.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersLineChart.swift new file mode 100644 index 000000000000..5a0eb66dcdda --- /dev/null +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersLineChart.swift @@ -0,0 +1,36 @@ +import Foundation +import DGCharts + +private struct SubscriberLineChartData: LineChartDataConvertible { + let accessibilityDescription: String + let lineChartData: LineChartData +} + +class StatsSubscribersLineChart { + + let lineChartData: LineChartDataConvertible + let lineChartStyling: LineChartStyling + + init(counts: [Int]) { + let chartEntries = counts.enumerated().map { index, count in + ChartDataEntry(x: Double(index), y: Double(count)) + } + let dataSet = LineChartDataSet(entries: chartEntries) + let chartData = LineChartData(dataSets: [dataSet]) + lineChartData = SubscriberLineChartData(accessibilityDescription: "Subscriber Charts", lineChartData: chartData) + lineChartStyling = SubscribersLineChartStyling() + } +} + +// MARK: - StatsSubscribersLineChartStyling + +private struct SubscribersLineChartStyling: LineChartStyling { + let primaryLineColor: UIColor = UIColor(light: .muriel(name: .blue, .shade50), dark: .muriel(name: .blue, .shade50)) + let secondaryLineColor: UIColor? = nil + let primaryHighlightColor: UIColor? = UIColor(red: 209.0/255.0, green: 209.0/255.0, blue: 214.0/255.0, alpha: 1.0) + let labelColor: UIColor = UIColor(light: .secondaryLabel, dark: .tertiaryLabel) + let legendColor: UIColor? = nil + let legendTitle: String? = nil + let lineColor: UIColor = .neutral(.shade5) + let yAxisValueFormatter: AxisValueFormatter = VerticalAxisFormatter() +} diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersStore.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersStore.swift index 119e6b9da308..f6eb8c0c521a 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersStore.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersStore.swift @@ -4,9 +4,11 @@ import WordPressKit protocol StatsSubscribersStoreProtocol { var emailsSummary: CurrentValueSubject, Never> { get } + var chartSummary: CurrentValueSubject, Never> { get } var subscribersList: CurrentValueSubject, Never> { get } func updateEmailsSummary(quantity: Int, sortField: StatsEmailsSummaryData.SortField) + func updateChartSummary() func updateSubscribersList(quantity: Int) } @@ -16,6 +18,7 @@ struct StatsSubscribersStore: StatsSubscribersStoreProtocol { private let statsService: StatsServiceRemoteV2 var emailsSummary: CurrentValueSubject, Never> = .init(.idle) + var chartSummary: CurrentValueSubject, Never> = .init(.idle) var subscribersList: CurrentValueSubject, Never> = .init(.idle) init() { @@ -55,6 +58,34 @@ struct StatsSubscribersStore: StatsSubscribersStoreProtocol { } } + func updateChartSummary() { + guard chartSummary.value != .loading else { return } + + let unit = StatsPeriodUnit.day + let cacheKey = StatsSubscribersCache.CacheKey.chartSummary(unit: unit.stringValue, siteId: siteID) + let cachedData: StatsSubscribersSummaryData? = cache.getValue(key: cacheKey) + + if let cachedData = cachedData { + self.chartSummary.send(.success(cachedData)) + } else { + chartSummary.send(.loading) + } + + statsService.getData(for: unit, endingOn: StatsDataHelper.currentDateForSite(), limit: 30) { (data: StatsSubscribersSummaryData?, error: Error?) in + DispatchQueue.main.async { + if let data = data { + cache.setValue(data, key: cacheKey) + self.chartSummary.send(.success(data)) + } + else { + if cachedData == nil { + self.chartSummary.send(.error) + } + } + } + } + } + // MARK: - Subscribers List func updateSubscribersList(quantity: Int) { diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift index ef38c88f60a6..d3dcef8bb80b 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift @@ -69,6 +69,7 @@ final class StatsSubscribersViewController: SiteStatsBaseTableViewController { func tableRowTypes() -> [ImmuTableRow.Type] { return [ + SubscriberChartRow.self, TopTotalsPeriodStatsRow.self, StatsGhostTopImmutableRow.self, StatsErrorRow.self diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift index bfd93b4a5a36..59b3a9330319 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift @@ -14,6 +14,7 @@ final class StatsSubscribersViewModel { } func refreshData() { + store.updateChartSummary() store.updateEmailsSummary(quantity: 10, sortField: .postId) store.updateSubscribersList(quantity: 10) } @@ -21,7 +22,8 @@ final class StatsSubscribersViewModel { // MARK: - Lifecycle func addObservers() { - Publishers.CombineLatest( + Publishers.CombineLatest3( + store.chartSummary.removeDuplicates(), store.emailsSummary.removeDuplicates(), store.subscribersList.removeDuplicates() ) @@ -41,6 +43,7 @@ final class StatsSubscribersViewModel { private extension StatsSubscribersViewModel { func updateTableViewSnapshot() { var snapshot = ImmuTableDiffableDataSourceSnapshot() + snapshot.addSection(chartRows()) snapshot.addSection(subscribersListRows()) snapshot.addSection(emailsSummaryRows()) tableViewSnapshot.send(snapshot) @@ -55,6 +58,31 @@ private extension StatsSubscribersViewModel { } } +// MARK: - Chart + +private extension StatsSubscribersViewModel { + func chartRows() -> [any StatsHashableImmuTableRow] { + switch store.chartSummary.value { + case .loading, .idle: + return loadingRows(.subscribersChart) + case .success(let chartSummary): + let xAxisDates = chartSummary.history.map { $0.date } + let viewsChart = StatsSubscribersLineChart(counts: chartSummary.history.map { $0.count }) + return [ + SubscriberChartRow( + history: chartSummary.history, + chartData: viewsChart.lineChartData, + chartStyling: viewsChart.lineChartStyling, + xAxisDates: xAxisDates, + statSection: .subscribersChart + ) + ] + case .error: + return errorRows(.subscribersChart) + } + } +} + // MARK: - Emails Summary private extension StatsSubscribersViewModel { diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 92ef494bfe41..3b6955648276 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -2801,6 +2801,12 @@ B026DAB02A96D9E900995410 /* support_chat_error_handler.js in Resources */ = {isa = PBXBuildFile; fileRef = B026DAAF2A96D9E900995410 /* support_chat_error_handler.js */; }; B026DAB12A96D9E900995410 /* support_chat_error_handler.js in Resources */ = {isa = PBXBuildFile; fileRef = B026DAAF2A96D9E900995410 /* support_chat_error_handler.js */; }; B030FE0A27EBF0BC000F6F5E /* SiteCreationIntentTracksEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B030FE0927EBF0BC000F6F5E /* SiteCreationIntentTracksEventTests.swift */; }; + B038A81C2BD70FCA00763731 /* StatsSubscribersChartCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B038A81B2BD70FCA00763731 /* StatsSubscribersChartCell.xib */; }; + B038A81D2BD70FCA00763731 /* StatsSubscribersChartCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B038A81A2BD70FCA00763731 /* StatsSubscribersChartCell.swift */; }; + B038A81E2BD70FCA00763731 /* StatsSubscribersChartCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B038A81A2BD70FCA00763731 /* StatsSubscribersChartCell.swift */; }; + B038A81F2BD70FCA00763731 /* StatsSubscribersChartCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B038A81B2BD70FCA00763731 /* StatsSubscribersChartCell.xib */; }; + B038A8212BD7164F00763731 /* StatsSubscribersLineChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = B038A8202BD7164F00763731 /* StatsSubscribersLineChart.swift */; }; + B038A8222BD7164F00763731 /* StatsSubscribersLineChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = B038A8202BD7164F00763731 /* StatsSubscribersLineChart.swift */; }; B03B9234250BC593000A40AF /* SuggestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03B9233250BC593000A40AF /* SuggestionService.swift */; }; B03B9236250BC5FD000A40AF /* Suggestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03B9235250BC5FD000A40AF /* Suggestion.swift */; }; B0637527253E7CEC00FD45D2 /* SuggestionsTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0637526253E7CEB00FD45D2 /* SuggestionsTableView.swift */; }; @@ -8177,6 +8183,9 @@ AEE082892681C23C00DCF54B /* GutenbergRefactoredGalleryUploadProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergRefactoredGalleryUploadProcessorTests.swift; sourceTree = ""; }; B026DAAF2A96D9E900995410 /* support_chat_error_handler.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = support_chat_error_handler.js; sourceTree = ""; }; B030FE0927EBF0BC000F6F5E /* SiteCreationIntentTracksEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteCreationIntentTracksEventTests.swift; sourceTree = ""; }; + B038A81A2BD70FCA00763731 /* StatsSubscribersChartCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSubscribersChartCell.swift; sourceTree = ""; }; + B038A81B2BD70FCA00763731 /* StatsSubscribersChartCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsSubscribersChartCell.xib; sourceTree = ""; }; + B038A8202BD7164F00763731 /* StatsSubscribersLineChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSubscribersLineChart.swift; sourceTree = ""; }; B03B9233250BC593000A40AF /* SuggestionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionService.swift; sourceTree = ""; }; B03B9235250BC5FD000A40AF /* Suggestion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Suggestion.swift; sourceTree = ""; }; B0637526253E7CEB00FD45D2 /* SuggestionsTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SuggestionsTableView.swift; path = Suggestions/SuggestionsTableView.swift; sourceTree = ""; }; @@ -9927,6 +9936,9 @@ 01011E842BD27E47003B5C2B /* StatsSubscribersViewModel.swift */, 019C5B8C2BD6570D00A69DB0 /* StatsSubscribersStore.swift */, 019C5B932BD6917600A69DB0 /* StatsSubscribersCache.swift */, + B038A81A2BD70FCA00763731 /* StatsSubscribersChartCell.swift */, + B038A81B2BD70FCA00763731 /* StatsSubscribersChartCell.xib */, + B038A8202BD7164F00763731 /* StatsSubscribersLineChart.swift */, ); path = Subscribers; sourceTree = ""; @@ -19827,6 +19839,7 @@ 1761F18226209AEE000815EF /* jetpack-green-icon-app-60x60@3x.png in Resources */, 83317ED92BC71CEB001AD2F4 /* ReaderTagCardCell.xib in Resources */, FE3E83E626A58646008CE851 /* ListSimpleOverlayView.xib in Resources */, + B038A81C2BD70FCA00763731 /* StatsSubscribersChartCell.xib in Resources */, 401A3D022027DBD80099A127 /* PluginListCell.xib in Resources */, 17222D83261DDDF90047B163 /* celadon-classic-icon-app-60x60@2x.png in Resources */, 981C34912183871200FC2683 /* SiteStatsDashboard.storyboard in Resources */, @@ -20309,6 +20322,7 @@ F46597B128E6605E00D5F49A /* neu-green-icon-app-76@2x.png in Resources */, F41E4E9728F20802001880C6 /* white-on-pink-icon-app-76@2x.png in Resources */, F41E4EB828F225DB001880C6 /* stroke-dark-icon-app-60@3x.png in Resources */, + B038A81F2BD70FCA00763731 /* StatsSubscribersChartCell.xib in Resources */, F46597EA28E6698D00D5F49A /* spectrum-on-black-icon-app-76.png in Resources */, 98A047762821D069001B4E2D /* BloggingPromptsViewController.storyboard in Resources */, F41E4ECF28F23E00001880C6 /* green-on-white-icon-app-83.5@2x.png in Resources */, @@ -21955,6 +21969,7 @@ C7AFF87C283D5CF4000E01DF /* QRLoginVerifyCoordinator.swift in Sources */, FA25FA212609AA9C0005E08F /* AppConfiguration.swift in Sources */, 83EF3D7B2937D703000AF9BF /* SharedDataIssueSolver.swift in Sources */, + B038A81D2BD70FCA00763731 /* StatsSubscribersChartCell.swift in Sources */, 436D55DB210F862A00CEAA33 /* NibReusable.swift in Sources */, F49B9A09293A3243000CEFCE /* MigrationEvent.swift in Sources */, 98563DDD21BF30C40006F5E9 /* TabbedTotalsCell.swift in Sources */, @@ -23288,6 +23303,7 @@ 98E14A3C27C9712D007B0896 /* NotificationCommentDetailViewController.swift in Sources */, 9A8ECE122254A3260043C8DA /* JetpackRemoteInstallState.swift in Sources */, 40D7823A206AEA880015A3A1 /* Scheduler.swift in Sources */, + B038A8212BD7164F00763731 /* StatsSubscribersLineChart.swift in Sources */, 436D562E2117347C00CEAA33 /* RegisterDomainDetailsViewModel+RowDefinitions.swift in Sources */, E6FACB1E1EC675E300284AC7 /* GravatarProfile.swift in Sources */, D8212CB720AA7703008E8AE8 /* ReaderShareAction.swift in Sources */, @@ -25017,6 +25033,7 @@ FABB22CF2602FC2C00C8785C /* AssembledSiteView.swift in Sources */, FABB22D02602FC2C00C8785C /* ReaderTopicService+FollowedInterests.swift in Sources */, FABB22D12602FC2C00C8785C /* ReaderRecommendedSiteCardCell.swift in Sources */, + B038A8222BD7164F00763731 /* StatsSubscribersLineChart.swift in Sources */, FABB22D22602FC2C00C8785C /* PostEditorState.swift in Sources */, FABB22D32602FC2C00C8785C /* UICollectionViewCell+Tint.swift in Sources */, FABB22D42602FC2C00C8785C /* ManagedPerson+CoreDataProperties.swift in Sources */, @@ -25637,6 +25654,7 @@ 0CED200D2B68425A00E6DD52 /* WebKitView.swift in Sources */, FABB247F2602FC2C00C8785C /* StockPhotosPageable.swift in Sources */, FABB24802602FC2C00C8785C /* JetpackRestoreStatusViewController.swift in Sources */, + B038A81E2BD70FCA00763731 /* StatsSubscribersChartCell.swift in Sources */, FABB24812602FC2C00C8785C /* BindableTapGestureRecognizer.swift in Sources */, FABB24822602FC2C00C8785C /* ReaderSearchSuggestion.swift in Sources */, DCF892CA282FA37100BB71E1 /* SiteStatsBaseTableViewController.swift in Sources */, diff --git a/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift b/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift index b31ef5ea41db..1d5e02906c62 100644 --- a/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift +++ b/WordPress/WordPressTest/StatsSubscribersViewModelTests.swift @@ -24,17 +24,38 @@ final class StatsSubscribersViewModelTests: XCTestCase { }) .store(in: &cancellables) - store.emailsSummary.send(.loading) + store.chartSummary.send(.loading) wait(for: [expectation], timeout: 1) } + func testTableViewSnapshot_chartSummaryLoaded() throws { + let expectation = expectation(description: "Chart section should be loading") + var subscriberChartRow: SubscriberChartRow? + sut.tableViewSnapshot + .sink(receiveValue: { snapshot in + if let row = snapshot.itemIdentifiers[0].immuTableRow as? SubscriberChartRow { + subscriberChartRow = row + expectation.fulfill() + } + }) + .store(in: &cancellables) + let chartSummary = StatsSubscribersSummaryData(history: [ + .init(date: Date(), count: 1), + .init(date: Date(), count: 2), + ], period: .day, periodEndDate: Date()) + store.chartSummary.send(.success(chartSummary)) + + wait(for: [expectation], timeout: 1) + XCTAssertNotNil(subscriberChartRow?.chartData) + } + func testTableViewSnapshot_subscribersListLoaded() throws { let expectation = expectation(description: "First section should be TopTotalsPeriodStatsRow") var subscribersListRow: TopTotalsPeriodStatsRow? sut.tableViewSnapshot .sink(receiveValue: { snapshot in - if let row = snapshot.itemIdentifiers[0].immuTableRow as? TopTotalsPeriodStatsRow { + if let row = snapshot.itemIdentifiers[1].immuTableRow as? TopTotalsPeriodStatsRow { subscribersListRow = row expectation.fulfill() } @@ -59,7 +80,7 @@ final class StatsSubscribersViewModelTests: XCTestCase { var emailsSummaryRow: TopTotalsPeriodStatsRow? sut.tableViewSnapshot .sink(receiveValue: { snapshot in - if let row = snapshot.itemIdentifiers[1].immuTableRow as? TopTotalsPeriodStatsRow { + if let row = snapshot.itemIdentifiers[2].immuTableRow as? TopTotalsPeriodStatsRow { emailsSummaryRow = row expectation.fulfill() } @@ -82,14 +103,20 @@ final class StatsSubscribersViewModelTests: XCTestCase { private class StatsSubscribersStoreMock: StatsSubscribersStoreProtocol { var emailsSummary: CurrentValueSubject, Never> = .init(.idle) + var chartSummary: CurrentValueSubject, Never> = .init(.idle) var subscribersList: CurrentValueSubject, Never> = .init(.idle) var updateEmailsSummaryCalled = false + var updateChartSummaryCalled = false var updateSubscribersListCalled = false func updateEmailsSummary(quantity: Int, sortField: StatsEmailsSummaryData.SortField) { updateEmailsSummaryCalled = true } + func updateChartSummary() { + updateChartSummaryCalled = false + } + func updateSubscribersList(quantity: Int) { updateSubscribersListCalled = true }