Skip to content

Commit

Permalink
Stats: Add Subscribers Emails Opens and Clicks Details (#23062)
Browse files Browse the repository at this point in the history
  • Loading branch information
staskus authored Apr 24, 2024
2 parents 893bb0a + bba514a commit f045b25
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 16 deletions.
3 changes: 3 additions & 0 deletions WordPress/Classes/ViewRelated/Stats/Helpers/StatSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@
static let searchTerm = NSLocalizedString("Search Term", comment: "Label for list of search term")
static let period = NSLocalizedString("Period", comment: "Label for date periods.")
static let file = NSLocalizedString("File", comment: "Label for list of file downloads.")
static let emailsSummary = NSLocalizedString("stats.subscribers.emailsSummary.column.title", value: "Latest emails", comment: "A title for table's column that shows a name of an email")
}

struct DataSubtitles {
Expand All @@ -459,6 +460,8 @@
static let since = NSLocalizedString("Since", comment: "Label for time period in list of followers.")
static let clicks = NSLocalizedString("Clicks", comment: "Label for number of clicks.")
static let downloads = NSLocalizedString("Downloads", comment: "Label for number of file downloads.")
static let emailsSummaryOpens = NSLocalizedString("stats.subscribers.emailsSummary.column.opens", value: "Opens", comment: "A title for table's column that shows a number of email openings")
static let emailsSummaryClicks = NSLocalizedString("stats.subscribers.emailsSummary.column.clicks", value: "Clicks", comment: "A title for table's column that shows a number of times a post was opened from an email")
}

struct TabTitles {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import WordPressFlux
import Combine

/// The view model used by SiteStatsDetailTableViewController to show
/// all data for a selected stat.
Expand All @@ -24,6 +25,9 @@ class SiteStatsDetailsViewModel: Observable {
private var periodReceipt: Receipt?
private var periodChangeReceipt: Receipt?

private let subscribersStore: StatsSubscribersStoreProtocol
private var cancellables: Set<AnyCancellable> = []

private var selectedDate: Date?
private var selectedPeriod: StatsPeriodUnit?
private var postID: Int?
Expand All @@ -35,11 +39,13 @@ class SiteStatsDetailsViewModel: Observable {
init(detailsDelegate: SiteStatsDetailsDelegate,
referrerDelegate: SiteStatsReferrerDelegate,
insightsStore: StatsInsightsStore,
periodStore: StatsPeriodStore) {
periodStore: StatsPeriodStore,
subscribersStore: StatsSubscribersStoreProtocol = StatsSubscribersStore()) {
self.detailsDelegate = detailsDelegate
self.referrerDelegate = referrerDelegate
self.insightsStore = insightsStore
self.periodStore = periodStore
self.subscribersStore = subscribersStore
}

// MARK: - Data Fetching
Expand Down Expand Up @@ -72,6 +78,13 @@ class SiteStatsDetailsViewModel: Observable {
self?.emitChange()
}
periodReceipt = periodStore.query(.postStats(postID: postID))
} else if statSection == .subscribersEmailsSummary {
subscribersStore.emailsSummary
.sink { [weak self] _ in
self?.emitChange()
}
.store(in: &cancellables)
refreshEmailsSummary()
} else {
DDLogError("Stats Details cannot be loaded for StatSection: \(statSection)")
}
Expand All @@ -91,6 +104,8 @@ class SiteStatsDetailsViewModel: Observable {
return true
}
return periodStore.fetchingFailed(for: .postStats(postID: postID))
} else if statSection == .subscribersEmailsSummary {
return subscribersStore.emailsSummary.value == .error
} else {
DDLogError("Stats Details cannot be loaded for StatSection: \(statSection)")
return true
Expand Down Expand Up @@ -127,6 +142,8 @@ class SiteStatsDetailsViewModel: Observable {
return periodStore.isFetchingFileDownloads
case .postStatsMonthsYears, .postStatsAverageViews:
return periodStore.isFetchingPostStats(for: postID)
case .subscribersEmailsSummary:
return subscribersStore.emailsSummary.value == .loading
default:
return false
}
Expand Down Expand Up @@ -290,6 +307,15 @@ class SiteStatsDetailsViewModel: Observable {
rows.append(contentsOf: postStatsRows(forAverages: true, status: status))
return rows
}
case .subscribersEmailsSummary:
return periodImmuTable(for: subscribersStore.emailsSummary.value.storeFetchingStatus) { status in
var rows = [any HashableImmutableRow]()
rows.append(DetailSubtitlesHeaderRow(itemSubtitle: StatSection.ItemSubtitles.emailsSummary,
dataSubtitle: StatSection.DataSubtitles.emailsSummaryOpens,
secondDataSubtitle: StatSection.DataSubtitles.emailsSummaryClicks))
rows.append(contentsOf: dataRowsFor(emailsSummaryPosts(), status: status))
return rows
}
default:
return ImmuTableDiffableDataSourceSnapshot()
}
Expand Down Expand Up @@ -392,6 +418,10 @@ class SiteStatsDetailsViewModel: Observable {
}
ActionDispatcher.dispatch(PeriodAction.refreshPeriod(query: .postStats(postID: postID)))
}

func refreshEmailsSummary() {
subscribersStore.updateEmailsSummary(quantity: 30, sortField: .opens)
}
}

// MARK: - Private Extension
Expand Down Expand Up @@ -831,6 +861,20 @@ private extension SiteStatsDetailsViewModel {
return yearRows
}

// MARK: - Emails Summary

func emailsSummaryPosts() -> [StatsTotalRowData] {
let emailsSummaryPosts = subscribersStore.emailsSummary.value.data?.posts ?? []

return emailsSummaryPosts.map {
StatsTotalRowData(name: $0.title,
data: $0.opens.abbreviatedString(),
secondData: $0.clicks.abbreviatedString(),
multiline: false,
statSection: .subscribersEmailsSummary)
}
}

// MARK: - Helpers

func dataRowsFor(_ rowsData: [StatsTotalRowData], status: StoreFetchingStatus = .idle) -> [DetailDataRow] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,7 @@ struct DetailSubtitlesHeaderRow: HashableImmutableRow {

let itemSubtitle: String
let dataSubtitle: String
var secondDataSubtitle: String? = nil
let action: ImmuTableAction? = nil

func configureCell(_ cell: UITableViewCell) {
Expand All @@ -825,12 +826,13 @@ struct DetailSubtitlesHeaderRow: HashableImmutableRow {
return
}

cell.configure(itemSubtitle: itemSubtitle, dataSubtitle: dataSubtitle, dataRows: [], forDetails: true)
cell.configure(itemSubtitle: itemSubtitle, dataSubtitle: dataSubtitle, secondDataSubtitle: secondDataSubtitle, dataRows: [], forDetails: true)
}

static func == (lhs: DetailSubtitlesHeaderRow, rhs: DetailSubtitlesHeaderRow) -> Bool {
return lhs.itemSubtitle == rhs.itemSubtitle &&
lhs.dataSubtitle == rhs.dataSubtitle
lhs.dataSubtitle == rhs.dataSubtitle &&
lhs.secondDataSubtitle == rhs.secondDataSubtitle
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,27 @@ extension StatsSubscribersStore {
case loading
case success(Value)
case error

var data: Value? {
switch self {
case .success(let data):
return data
default:
return nil
}
}

var storeFetchingStatus: StoreFetchingStatus {
switch self {
case .idle:
return .idle
case .loading:
return .loading
case .success:
return .success
case .error:
return .error
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ final class StatsSubscribersViewController: SiteStatsBaseTableViewController {

extension StatsSubscribersViewController: SiteStatsPeriodDelegate {
func viewMoreSelectedForStatSection(_ statSection: StatSection) {
// TODO
DDLogInfo("\(statSection) selected")
let detailTableViewController = SiteStatsDetailTableViewController.loadFromStoryboard()
detailTableViewController.configure(statSection: statSection)
navigationController?.pushViewController(detailTableViewController, animated: true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ private extension StatsSubscribersViewModel {
case .success(let emailsSummary):
return [
TopTotalsPeriodStatsRow(
itemSubtitle: Strings.titleColumn,
dataSubtitle: Strings.opensColumn,
secondDataSubtitle: Strings.clicksColumn,
itemSubtitle: StatSection.ItemSubtitles.emailsSummary,
dataSubtitle: StatSection.DataSubtitles.emailsSummaryOpens,
secondDataSubtitle: StatSection.DataSubtitles.emailsSummaryClicks,
dataRows: emailsSummaryDataRows(emailsSummary),
statSection: .subscribersEmailsSummary,
siteStatsPeriodDelegate: viewMoreDelegate
Expand All @@ -87,11 +87,3 @@ private extension StatsSubscribersViewModel {
}
}
}

private extension StatsSubscribersViewModel {
struct Strings {
static let titleColumn = NSLocalizedString("stats.subscribers.emailsSummary.column.title", value: "Latest emails", comment: "A title for table's column that shows a name of an email")
static let opensColumn = NSLocalizedString("stats.subscribers.emailsSummary.column.opens", value: "Opens", comment: "A title for table's column that shows a number of email openings")
static let clicksColumn = NSLocalizedString("stats.subscribers.emailsSummary.column.clicks", value: "Clicks", comment: "A title for table's column that shows a number of times a post was opened from an email")
}
}

0 comments on commit f045b25

Please sign in to comment.