Skip to content

Commit 1775563

Browse files
Merge branch 'trunk' into task/19812-handle-simplified-ui
2 parents 1af613b + 7b7fcb6 commit 1775563

File tree

66 files changed

+4808
-172
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+4808
-172
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
-----
33
* [***] [internal] A significant refactor to the app’s architecture was made to allow for the new simplified UI. Regression testing on the app’s main flows is needed. [#19817]
44

5+
56
21.4
67
-----
78
* [*] Fixed an issue where publishing Posts and Pages could fail under certain conditions. [#19717]

WordPress/Classes/Stores/StatsPeriodStore.swift

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,12 @@ private extension StatsPeriodStore {
384384
return
385385
}
386386

387+
let group = DispatchGroup()
388+
389+
if FeatureFlag.statsNewAppearance.enabled {
390+
group.enter()
391+
DDLogInfo("Stats Period: Enter group fetching likes summary.")
392+
}
387393
let likesOperation = PeriodOperation(service: service, for: period, date: date, limit: 14) { [weak self] (likes: StatsLikesSummaryTimeIntervalData?, error: Error?) in
388394
if error != nil {
389395
DDLogError("Stats Period: Error fetching likes summary: \(String(describing: error?.localizedDescription))")
@@ -392,9 +398,17 @@ private extension StatsPeriodStore {
392398
DDLogInfo("Stats Period: Finished fetching likes summary.")
393399
DispatchQueue.main.async {
394400
self?.receivedLikesSummary(likes, error)
401+
if FeatureFlag.statsNewAppearance.enabled {
402+
DDLogInfo("Stats Period: Leave group fetching likes summary.")
403+
group.leave()
404+
}
395405
}
396406
}
397407

408+
if FeatureFlag.statsNewAppearance.enabled {
409+
group.enter()
410+
DDLogInfo("Stats Period: Enter group fetching posts.")
411+
}
398412
let topPostsOperation = PeriodOperation(service: service, for: period, date: date) { [weak self] (posts: StatsTopPostsTimeIntervalData?, error: Error?) in
399413
if error != nil {
400414
DDLogError("Stats Period: Error fetching posts: \(String(describing: error?.localizedDescription))")
@@ -404,9 +418,17 @@ private extension StatsPeriodStore {
404418

405419
DispatchQueue.main.async {
406420
self?.receivedPostsAndPages(posts, error)
421+
if FeatureFlag.statsNewAppearance.enabled {
422+
DDLogInfo("Stats Period: Leave group fetching posts.")
423+
group.leave()
424+
}
407425
}
408426
}
409427

428+
if FeatureFlag.statsNewAppearance.enabled {
429+
group.enter()
430+
DDLogInfo("Stats Period: Enter group fetching referrers.")
431+
}
410432
let topReferrers = PeriodOperation(service: service, for: period, date: date) { [weak self] (referrers: StatsTopReferrersTimeIntervalData?, error: Error?) in
411433
if error != nil {
412434
DDLogError("Stats Period: Error fetching referrers: \(String(describing: error?.localizedDescription))")
@@ -416,9 +438,17 @@ private extension StatsPeriodStore {
416438

417439
DispatchQueue.main.async {
418440
self?.receivedReferrers(referrers, error)
441+
if FeatureFlag.statsNewAppearance.enabled {
442+
DDLogInfo("Stats Period: Leave group fetching referrers.")
443+
group.leave()
444+
}
419445
}
420446
}
421447

448+
if FeatureFlag.statsNewAppearance.enabled {
449+
group.enter()
450+
DDLogInfo("Stats Period: Enter group fetching published.")
451+
}
422452
let topPublished = PublishedPostOperation(service: service, for: period, date: date) { [weak self] (published: StatsPublishedPostsTimeIntervalData?, error: Error?) in
423453
if error != nil {
424454
DDLogError("Stats Period: Error fetching published: \(String(describing: error?.localizedDescription))")
@@ -428,9 +458,17 @@ private extension StatsPeriodStore {
428458

429459
DispatchQueue.main.async {
430460
self?.receivedPublished(published, error)
461+
if FeatureFlag.statsNewAppearance.enabled {
462+
DDLogInfo("Stats Period: Leave group fetching published.")
463+
group.leave()
464+
}
431465
}
432466
}
433467

468+
if FeatureFlag.statsNewAppearance.enabled {
469+
group.enter()
470+
DDLogInfo("Stats Period: Enter group fetching clicks.")
471+
}
434472
let topClicks = PeriodOperation(service: service, for: period, date: date) { [weak self] (clicks: StatsTopClicksTimeIntervalData?, error: Error?) in
435473
if error != nil {
436474
DDLogError("Stats Period: Error fetching clicks: \(String(describing: error?.localizedDescription))")
@@ -440,9 +478,17 @@ private extension StatsPeriodStore {
440478

441479
DispatchQueue.main.async {
442480
self?.receivedClicks(clicks, error)
481+
if FeatureFlag.statsNewAppearance.enabled {
482+
DDLogInfo("Stats Period: Leave group fetching clicks.")
483+
group.leave()
484+
}
443485
}
444486
}
445487

488+
if FeatureFlag.statsNewAppearance.enabled {
489+
group.enter()
490+
DDLogInfo("Stats Period: Enter group fetching authors.")
491+
}
446492
let topAuthors = PeriodOperation(service: service, for: period, date: date) { [weak self] (authors: StatsTopAuthorsTimeIntervalData?, error: Error?) in
447493
if error != nil {
448494
DDLogError("Stats Period: Error fetching authors: \(String(describing: error?.localizedDescription))")
@@ -452,9 +498,17 @@ private extension StatsPeriodStore {
452498

453499
DispatchQueue.main.async {
454500
self?.receivedAuthors(authors, error)
501+
if FeatureFlag.statsNewAppearance.enabled {
502+
DDLogInfo("Stats Period: Leave group fetching authors.")
503+
group.leave()
504+
}
455505
}
456506
}
457507

508+
if FeatureFlag.statsNewAppearance.enabled {
509+
group.enter()
510+
DDLogInfo("Stats Period: Enter group fetching search terms.")
511+
}
458512
let topSearchTerms = PeriodOperation(service: service, for: period, date: date) { [weak self] (searchTerms: StatsSearchTermTimeIntervalData?, error: Error?) in
459513
if error != nil {
460514
DDLogError("Stats Period: Error fetching search terms: \(String(describing: error?.localizedDescription))")
@@ -464,9 +518,17 @@ private extension StatsPeriodStore {
464518

465519
DispatchQueue.main.async {
466520
self?.receivedSearchTerms(searchTerms, error)
521+
if FeatureFlag.statsNewAppearance.enabled {
522+
DDLogInfo("Stats Period: Leave group fetching search terms.")
523+
group.leave()
524+
}
467525
}
468526
}
469527

528+
if FeatureFlag.statsNewAppearance.enabled {
529+
group.enter()
530+
DDLogInfo("Stats Period: Enter group fetching countries.")
531+
}
470532
let topCountries = PeriodOperation(service: service, for: period, date: date, limit: 0) { [weak self] (countries: StatsTopCountryTimeIntervalData?, error: Error?) in
471533
if error != nil {
472534
DDLogError("Stats Period: Error fetching countries: \(String(describing: error?.localizedDescription))")
@@ -476,9 +538,17 @@ private extension StatsPeriodStore {
476538

477539
DispatchQueue.main.async {
478540
self?.receivedCountries(countries, error)
541+
if FeatureFlag.statsNewAppearance.enabled {
542+
DDLogInfo("Stats Period: Leave group fetching countries.")
543+
group.leave()
544+
}
479545
}
480546
}
481547

548+
if FeatureFlag.statsNewAppearance.enabled {
549+
group.enter()
550+
DDLogInfo("Stats Period: Enter group fetching videos.")
551+
}
482552
let topVideos = PeriodOperation(service: service, for: period, date: date) { [weak self] (videos: StatsTopVideosTimeIntervalData?, error: Error?) in
483553
if error != nil {
484554
DDLogError("Stats Period: Error fetching videos: \(String(describing: error?.localizedDescription))")
@@ -488,11 +558,19 @@ private extension StatsPeriodStore {
488558

489559
DispatchQueue.main.async {
490560
self?.receivedVideos(videos, error)
561+
if FeatureFlag.statsNewAppearance.enabled {
562+
DDLogInfo("Stats Period: Leave group fetching videos.")
563+
group.leave()
564+
}
491565
}
492566
}
493567

494568
// 'limit' in this context is used for the 'num' parameter for the 'file-downloads' endpoint.
495569
// 'num' relates to the "number of periods to include in the query".
570+
if FeatureFlag.statsNewAppearance.enabled {
571+
group.enter()
572+
DDLogInfo("Stats Period: Enter group fetching file downloads.")
573+
}
496574
let topFileDownloads = PeriodOperation(service: service, for: period, date: date, limit: 1) { [weak self] (downloads: StatsFileDownloadsTimeIntervalData?, error: Error?) in
497575
if error != nil {
498576
DDLogError("Stats Period: Error file downloads: \(String(describing: error?.localizedDescription))")
@@ -502,6 +580,10 @@ private extension StatsPeriodStore {
502580

503581
DispatchQueue.main.async {
504582
self?.receivedFileDownloads(downloads, error)
583+
if FeatureFlag.statsNewAppearance.enabled {
584+
DDLogInfo("Stats Period: Leave group fetching file downloads.")
585+
group.leave()
586+
}
505587
}
506588
}
507589

@@ -516,6 +598,13 @@ private extension StatsPeriodStore {
516598
topVideos,
517599
topFileDownloads],
518600
waitUntilFinished: false)
601+
602+
if FeatureFlag.statsNewAppearance.enabled {
603+
group.notify(queue: .main) { [weak self] in
604+
DDLogInfo("Stats Period: Finished fetchAsyncData.")
605+
self?.persistToCoreData()
606+
}
607+
}
519608
}
520609

521610
func fetchSummaryLikesData(date: Date, period: StatsPeriodUnit) {

WordPress/Classes/System/WordPressAppDelegate.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -329,14 +329,19 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate {
329329

330330
setupWordPressExtensions()
331331

332-
if FeatureFlag.contentMigration.enabled {
332+
if AppConfiguration.isWordPress,
333+
FeatureFlag.contentMigration.enabled {
333334
// To prevent race condition, initialize the shared instance synchronously so it can listen to account change notifications.
334335
let _ = ContentMigrationCoordinator.shared
335336

336-
// Start proactively exporting WP data in the background if the conditions are fulfilled.
337-
// This needs to be called after `setupWordPressExtensions` because it updates the stored data.
338-
DispatchQueue.global().async {
339-
ContentMigrationCoordinator.shared.startOnceIfNeeded()
337+
let launchUrl = launchOptions[.url] as? URL
338+
let exportUrl = URL(string: "\(AppScheme.wordpressMigrationV1.rawValue)\(WordPressExportRoute().path.removingPrefix("/"))")
339+
if launchUrl != exportUrl {
340+
// Start proactively exporting WP data in the background if the conditions are fulfilled.
341+
// This needs to be called after `setupWordPressExtensions` because it updates the stored data.
342+
DispatchQueue.global().async {
343+
ContentMigrationCoordinator.shared.startOnceIfNeeded()
344+
}
340345
}
341346
}
342347

WordPress/Classes/Utility/Migration/ContentMigrationCoordinator.swift

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,36 @@
66
.init()
77
}()
88

9+
var previousMigrationError: MigrationError? {
10+
guard let storedErrorValue = sharedPersistentRepository?.string(forKey: .exportErrorSharedKey) else {
11+
return nil
12+
}
13+
14+
return .init(rawValue: storedErrorValue)
15+
}
16+
917
// MARK: Dependencies
1018

1119
private let coreDataStack: CoreDataStack
1220
private let dataMigrator: ContentDataMigrating
1321
private let notificationCenter: NotificationCenter
1422
private let userPersistentRepository: UserPersistentRepository
23+
private let sharedPersistentRepository: UserPersistentRepository?
1524
private let eligibilityProvider: ContentMigrationEligibilityProvider
1625
private let tracker: MigrationAnalyticsTracker
1726

1827
init(coreDataStack: CoreDataStack = ContextManager.shared,
1928
dataMigrator: ContentDataMigrating = DataMigrator(),
2029
notificationCenter: NotificationCenter = .default,
2130
userPersistentRepository: UserPersistentRepository = UserDefaults.standard,
31+
sharedPersistentRepository: UserPersistentRepository? = UserDefaults(suiteName: WPAppGroupName),
2232
eligibilityProvider: ContentMigrationEligibilityProvider = AppConfiguration(),
2333
tracker: MigrationAnalyticsTracker = .init()) {
2434
self.coreDataStack = coreDataStack
2535
self.dataMigrator = dataMigrator
2636
self.notificationCenter = notificationCenter
2737
self.userPersistentRepository = userPersistentRepository
38+
self.sharedPersistentRepository = sharedPersistentRepository
2839
self.eligibilityProvider = eligibilityProvider
2940
self.tracker = tracker
3041

@@ -34,7 +45,7 @@
3445
ensureBackupDataDeletedOnLogout()
3546
}
3647

37-
enum ContentMigrationCoordinatorError: LocalizedError {
48+
enum MigrationError: String, LocalizedError {
3849
case ineligible
3950
case exportFailure
4051
case localDraftsNotSynced
@@ -58,30 +69,30 @@
5869
/// just let the user continue with the original intent in case of failure.
5970
///
6071
/// - Parameter completion: Closure called after the export process completes.
61-
func startAndDo(completion: ((Result<Void, ContentMigrationCoordinatorError>) -> Void)? = nil) {
72+
func startAndDo(completion: ((Result<Void, MigrationError>) -> Void)? = nil) {
6273
guard eligibilityProvider.isEligibleForMigration else {
6374
tracker.trackContentExportEligibility(eligible: false)
64-
completion?(.failure(.ineligible))
75+
processResult(.failure(.ineligible), completion: completion)
6576
return
6677
}
6778

6879
guard isLocalPostsSynced() else {
69-
let error = ContentMigrationCoordinatorError.localDraftsNotSynced
80+
let error = MigrationError.localDraftsNotSynced
7081
tracker.trackContentExportFailed(reason: error.localizedDescription)
71-
completion?(.failure(error))
82+
processResult(.failure(error), completion: completion)
7283
return
7384
}
7485

7586
dataMigrator.exportData { [weak self] result in
7687
switch result {
7788
case .success:
7889
self?.tracker.trackContentExportSucceeded()
79-
completion?(.success(()))
90+
self?.processResult(.success(()), completion: completion)
8091

8192
case .failure(let error):
8293
DDLogError("[Jetpack Migration] Error exporting data: \(error)")
8394
self?.tracker.trackContentExportFailed(reason: error.localizedDescription)
84-
completion?(.failure(.exportFailure))
95+
self?.processResult(.failure(.exportFailure), completion: completion)
8596
}
8697
}
8798
}
@@ -147,6 +158,11 @@ private extension ContentMigrationCoordinator {
147158
/// This prevents the user from entering the migration flow and immediately gets shown with a login pop-up (since we couldn't migrate the authToken anymore).
148159
///
149160
func ensureBackupDataDeletedOnLogout() {
161+
// we only need to listen to changes from the WordPress side.
162+
guard AppConfiguration.isWordPress else {
163+
return
164+
}
165+
150166
notificationCenter.addObserver(forName: .WPAccountDefaultWordPressComAccountChanged, object: nil, queue: nil) { [weak self] notification in
151167
// nil notification object means it's a logout event.
152168
guard let self,
@@ -157,6 +173,30 @@ private extension ContentMigrationCoordinator {
157173
self.cleanupExportedDataIfNeeded()
158174
}
159175
}
176+
177+
/// A "middleware" logic that attempts to record (or clear) any migration error to the App Group space
178+
/// before calling the completion block.
179+
///
180+
/// - Parameters:
181+
/// - result: The `Result` object from the export process.
182+
/// - completion: Closure that'll be executed after the process completes.
183+
func processResult(_ result: Result<Void, MigrationError>, completion: ((Result<Void, MigrationError>) -> Void)?) {
184+
// make sure that we're only intercepting from the WordPress side.
185+
guard AppConfiguration.isWordPress else {
186+
completion?(result)
187+
return
188+
}
189+
190+
switch result {
191+
case .success:
192+
sharedPersistentRepository?.removeObject(forKey: .exportErrorSharedKey)
193+
194+
case .failure(let error):
195+
sharedPersistentRepository?.set(error.rawValue, forKey: .exportErrorSharedKey)
196+
}
197+
198+
completion?(result)
199+
}
160200
}
161201

162202
// MARK: - Content Migration Eligibility Provider
@@ -176,4 +216,5 @@ extension AppConfiguration: ContentMigrationEligibilityProvider {
176216

177217
private extension String {
178218
static let oneOffMigrationKey = "wordpress_one_off_export"
219+
static let exportErrorSharedKey = "wordpress_shared_export_error"
179220
}

WordPress/Classes/ViewRelated/Stats/Helpers/SiteStatsImmuTableRows.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class SiteStatsImmuTableRows {
88
/// Helper method to create the rows for the Views and Visitors section
99
///
1010
static func viewVisitorsImmuTableRows(_ statsSummaryTimeIntervalData: StatsSummaryTimeIntervalData?,
11+
selectedSegment: StatsSegmentedControlData.Segment,
1112
periodDate: Date,
1213
periodEndDate: Date? = nil,
1314
statsLineChartViewDelegate: StatsLineChartViewDelegate?,
@@ -62,12 +63,13 @@ class SiteStatsImmuTableRows {
6263
}
6364

6465
let row = ViewsVisitorsRow(
65-
segmentsData: [viewsSegmentData, visitorsSegmentData],
66-
chartData: lineChartData,
67-
chartStyling: lineChartStyling,
68-
period: StatsPeriodUnit.day,
69-
statsLineChartViewDelegate: statsLineChartViewDelegate,
70-
siteStatsInsightsDelegate: siteStatsInsightsDelegate, xAxisDates: xAxisDates
66+
segmentsData: [viewsSegmentData, visitorsSegmentData],
67+
selectedSegment: selectedSegment,
68+
chartData: lineChartData,
69+
chartStyling: lineChartStyling,
70+
period: StatsPeriodUnit.day,
71+
statsLineChartViewDelegate: statsLineChartViewDelegate,
72+
siteStatsInsightsDelegate: siteStatsInsightsDelegate, xAxisDates: xAxisDates
7173
)
7274
tableRows.append(row)
7375
}

0 commit comments

Comments
 (0)