Skip to content

Commit f378109

Browse files
authored
Merge pull request #7665 from woocommerce/feat/6966-reset-stats-sync-ts-after-orders
Dashboard: reset the last sync timestamp after new orders
2 parents 86a9239 + 04e700e commit f378109

File tree

13 files changed

+269
-15
lines changed

13 files changed

+269
-15
lines changed

WooCommerce/Classes/Notifications/PushNotificationsManager.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ final class PushNotificationsManager: PushNotesManager {
4545
/// Mutable reference to `inactiveNotifications`
4646
private let inactiveNotificationsSubject = PassthroughSubject<PushNotification, Never>()
4747

48+
/// An observable that emits values when a Remote Notification is received while the app is
49+
/// in the background.
50+
///
51+
var backgroundNotifications: AnyPublisher<PushNotification, Never> {
52+
backgroundNotificationsSubject.eraseToAnyPublisher()
53+
}
54+
55+
/// Mutable reference to `backgroundNotifications`
56+
private let backgroundNotificationsSubject = PassthroughSubject<PushNotification, Never>()
57+
4858
/// An observable that emits values when a local notification is received.
4959
///
5060
var localNotificationUserResponses: AnyPublisher<UNNotificationResponse, Never> {
@@ -292,6 +302,10 @@ extension PushNotificationsManager {
292302

293303
handleRemoteNotificationInAllAppStates(userInfo)
294304

305+
if let notification = PushNotification.from(userInfo: userInfo) {
306+
backgroundNotificationsSubject.send(notification)
307+
}
308+
295309
return await synchronizeNotifications()
296310
}
297311

@@ -598,6 +612,7 @@ private extension PushNotificationsManager {
598612
private extension PushNotification {
599613
static func from(userInfo: [AnyHashable: Any]) -> PushNotification? {
600614
guard let noteID = userInfo.integer(forKey: APNSKey.identifier),
615+
let siteID = userInfo.integer(forKey: APNSKey.siteID),
601616
let alert = userInfo.dictionary(forKey: APNSKey.aps)?.dictionary(forKey: APNSKey.alert),
602617
let title = alert.string(forKey: APNSKey.alertTitle),
603618
let type = userInfo.string(forKey: APNSKey.type),
@@ -606,7 +621,7 @@ private extension PushNotification {
606621
}
607622
let subtitle = alert.string(forKey: APNSKey.alertSubtitle)
608623
let message = alert.string(forKey: APNSKey.alertMessage)
609-
return PushNotification(noteID: noteID, kind: noteKind, title: title, subtitle: subtitle, message: message)
624+
return PushNotification(noteID: noteID, siteID: siteID, kind: noteKind, title: title, subtitle: subtitle, message: message)
610625
}
611626
}
612627

WooCommerce/Classes/ServiceLocator/PushNotesManager.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ protocol PushNotesManager {
2020
///
2121
var inactiveNotifications: AnyPublisher<PushNotification, Never> { get }
2222

23+
/// An observable that emits values when a Remote Notification is received while the app is
24+
/// in the background.
25+
///
26+
var backgroundNotifications: AnyPublisher<PushNotification, Never> { get }
27+
2328
/// An observable that emits values when a local notification response is received.
2429
///
2530
var localNotificationUserResponses: AnyPublisher<UNNotificationResponse, Never> { get }

WooCommerce/Classes/ServiceLocator/PushNotification.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ struct PushNotification {
99
/// The `note_id` value received from the Remote Notification's `userInfo`.
1010
///
1111
let noteID: Int
12+
/// The `blog` value received from the Remote Notification's `userInfo`.
13+
///
14+
let siteID: Int
1215
/// The `type` value received from the Remote Notification's `userInfo`.
1316
///
1417
let kind: Note.Kind

WooCommerce/Classes/ViewRelated/Dashboard/Stats v4/StoreStatsAndTopPerformersViewController.swift

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,18 @@ final class StoreStatsAndTopPerformersViewController: ButtonBarPagerTabStripView
4747
private var selectedTimeRangeIndexSubscription: AnyCancellable?
4848
private var reloadDataAfterSelectedTimeRangeSubscriptions: Set<AnyCancellable> = []
4949

50+
private let pushNotificationsManager: PushNotesManager
51+
private var localOrdersSubscription: AnyCancellable?
52+
private var remoteOrdersSubscription: AnyCancellable?
53+
5054
// MARK: - View Lifecycle
5155

52-
init(siteID: Int64, dashboardViewModel: DashboardViewModel) {
56+
init(siteID: Int64,
57+
dashboardViewModel: DashboardViewModel,
58+
pushNotificationsManager: PushNotesManager = ServiceLocator.pushNotesManager) {
5359
self.siteID = siteID
5460
self.dashboardViewModel = dashboardViewModel
61+
self.pushNotificationsManager = pushNotificationsManager
5562
super.init(nibName: nil, bundle: nil)
5663
}
5764

@@ -82,6 +89,8 @@ final class StoreStatsAndTopPerformersViewController: ButtonBarPagerTabStripView
8289
super.viewDidLoad()
8390
configureView()
8491
observeSelectedTimeRangeIndex()
92+
observeRemotelyCreatedOrdersToResetLastSyncTimestamp()
93+
observeLocallyCreatedOrdersToResetLastSyncTimestamp()
8594
}
8695

8796
override func viewWillAppear(_ animated: Bool) {
@@ -322,6 +331,35 @@ private extension StoreStatsAndTopPerformersViewController {
322331
periodViewController.refreshControl.endRefreshing()
323332
}
324333
}
334+
335+
func observeRemotelyCreatedOrdersToResetLastSyncTimestamp() {
336+
let siteID = self.siteID
337+
remoteOrdersSubscription = Publishers
338+
.Merge(pushNotificationsManager.backgroundNotifications, pushNotificationsManager.foregroundNotifications)
339+
.filter { $0.kind == .storeOrder && $0.siteID == siteID }
340+
.sink { [weak self] _ in
341+
self?.resetLastSyncTimestamp()
342+
}
343+
}
344+
345+
func observeLocallyCreatedOrdersToResetLastSyncTimestamp() {
346+
let action = OrderAction.observeInsertedOrders(siteID: siteID) { [weak self] observableInsertedOrders in
347+
guard let self = self else { return }
348+
self.localOrdersSubscription = observableInsertedOrders
349+
.filter { $0.isNotEmpty }
350+
.sink { [weak self] _ in
351+
guard let self = self else { return }
352+
self.resetLastSyncTimestamp()
353+
}
354+
}
355+
ServiceLocator.stores.dispatch(action)
356+
}
357+
358+
func resetLastSyncTimestamp() {
359+
periodVCs.forEach { periodVC in
360+
periodVC.lastFullSyncTimestamp = nil
361+
}
362+
}
325363
}
326364

327365
// MARK: - Placeholders

WooCommerce/WooCommerceTests/Mocks/MockPushNotificationsManager.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ final class MockPushNotificationsManager: PushNotesManager {
2424

2525
private let inactiveNotificationsSubject = PassthroughSubject<PushNotification, Never>()
2626

27+
var backgroundNotifications: AnyPublisher<PushNotification, Never> {
28+
backgroundNotificationsSubject.eraseToAnyPublisher()
29+
}
30+
31+
private let backgroundNotificationsSubject = PassthroughSubject<PushNotification, Never>()
32+
2733
var localNotificationUserResponses: AnyPublisher<UNNotificationResponse, Never> {
2834
localNotificationResponsesSubject.eraseToAnyPublisher()
2935
}

WooCommerce/WooCommerceTests/Notifications/PushNotificationsManagerTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,48 @@ final class PushNotificationsManagerTests: XCTestCase {
470470
XCTAssertNil(emittedNotification.message)
471471
}
472472

473+
// MARK: - Background Notification Observable
474+
475+
func test_it_emits_background_notifications_when_it_receives_a_notification_while_app_is_in_the_background() async throws {
476+
// Given
477+
application.applicationState = .background
478+
479+
manager = {
480+
let configuration = PushNotificationsConfiguration(application: self.application,
481+
defaults: self.defaults,
482+
storesManager: self.storesManager,
483+
supportManager: self.supportManager,
484+
userNotificationsCenter: self.userNotificationCenter)
485+
return PushNotificationsManager(configuration: configuration)
486+
}()
487+
488+
var emittedNotifications = [PushNotification]()
489+
manager.backgroundNotifications.sink { notification in
490+
emittedNotifications.append(notification)
491+
}.store(in: &subscriptions)
492+
493+
let userinfo = notificationPayload(noteID: 9_981,
494+
type: .storeOrder,
495+
siteID: 606,
496+
title: Sample.defaultTitle,
497+
subtitle: Sample.defaultSubtitle,
498+
message: Sample.defaultMessage)
499+
500+
// When
501+
_ = await manager.handleRemoteNotificationInTheBackground(userInfo: userinfo)
502+
503+
// Then
504+
XCTAssertEqual(emittedNotifications.count, 1)
505+
506+
let emittedNotification = try XCTUnwrap(emittedNotifications.first)
507+
XCTAssertEqual(emittedNotification.kind, .storeOrder)
508+
XCTAssertEqual(emittedNotification.noteID, 9_981)
509+
XCTAssertEqual(emittedNotification.siteID, 606)
510+
XCTAssertEqual(emittedNotification.title, Sample.defaultTitle)
511+
XCTAssertEqual(emittedNotification.subtitle, Sample.defaultSubtitle)
512+
XCTAssertEqual(emittedNotification.message, Sample.defaultMessage)
513+
}
514+
473515
// MARK: - App Badge Number
474516

475517
/// Verifies that `handleNotification` updates app badge number to 1 when the notification is from the same site.

WooCommerce/WooCommerceTests/ViewRelated/HubMenu/HubMenuCoordinatorTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ final class HubMenuCoordinatorTests: XCTestCase {
4343
func test_when_receiving_a_non_review_notification_then_it_will_not_do_anything() throws {
4444
// Given
4545
let coordinator = makeHubMenuCoordinator()
46-
let pushNotification = PushNotification(noteID: 1_234, kind: .storeOrder, title: "", subtitle: "", message: "")
46+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .storeOrder, title: "", subtitle: "", message: "")
4747

4848
coordinator.start()
4949
coordinator.activate(siteID: siteID)
@@ -64,7 +64,7 @@ final class HubMenuCoordinatorTests: XCTestCase {
6464
func test_when_receiving_a_notification_while_in_foreground_then_it_will_not_do_anything() throws {
6565
// Given
6666
let coordinator = makeHubMenuCoordinator()
67-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
67+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
6868

6969
coordinator.start()
7070
coordinator.activate(siteID: siteID)
@@ -83,7 +83,7 @@ final class HubMenuCoordinatorTests: XCTestCase {
8383
func test_when_failing_to_retrieve_ProductReview_details_then_it_will_present_a_notice() throws {
8484
// Given
8585
let coordinator = makeHubMenuCoordinator()
86-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
86+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
8787

8888
coordinator.start()
8989
coordinator.activate(siteID: siteID)

WooCommerce/WooCommerceTests/ViewRelated/MainTabBarControllerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ final class MainTabBarControllerTests: XCTestCase {
238238

239239
// Action
240240
// Send push notification in inactive state
241-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
241+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
242242
pushNotificationsManager.sendInactiveNotification(pushNotification)
243243

244244
// Simulate that the network call returns a parcel

WooCommerce/WooCommerceTests/ViewRelated/Notifications/ReviewsCoordinatorTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ final class ReviewsCoordinatorTests: XCTestCase {
4343
func test_when_receiving_a_non_review_notification_then_it_will_not_do_anything() throws {
4444
// Given
4545
let coordinator = makeReviewsCoordinator()
46-
let pushNotification = PushNotification(noteID: 1_234, kind: .storeOrder, title: "", subtitle: "", message: "")
46+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .storeOrder, title: "", subtitle: "", message: "")
4747

4848
coordinator.start()
4949
coordinator.activate(siteID: siteID)
@@ -64,7 +64,7 @@ final class ReviewsCoordinatorTests: XCTestCase {
6464
func test_when_receiving_a_notification_while_in_foreground_then_it_will_not_do_anything() throws {
6565
// Given
6666
let coordinator = makeReviewsCoordinator()
67-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
67+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
6868

6969
coordinator.start()
7070
coordinator.activate(siteID: siteID)
@@ -82,7 +82,7 @@ final class ReviewsCoordinatorTests: XCTestCase {
8282

8383
func test_when_receiving_a_foreground_notification_to_view_then_it_will_present_the_review_details() throws {
8484
// Given
85-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
85+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
8686

8787
var willPresentReviewDetailsFromPushNotificationCallCount: Int = 0
8888
let coordinator = makeReviewsCoordinator(willPresentReviewDetailsFromPushNotification: {
@@ -114,7 +114,7 @@ final class ReviewsCoordinatorTests: XCTestCase {
114114

115115
func test_when_receiving_a_review_notification_while_inactive_then_it_will_present_the_review_details() throws {
116116
// Given
117-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
117+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
118118

119119
var willPresentReviewDetailsFromPushNotificationCallCount: Int = 0
120120
let coordinator = makeReviewsCoordinator(willPresentReviewDetailsFromPushNotification: {
@@ -147,7 +147,7 @@ final class ReviewsCoordinatorTests: XCTestCase {
147147
func test_when_failing_to_retrieve_ProductReview_details_then_it_will_present_a_notice() throws {
148148
// Given
149149
let coordinator = makeReviewsCoordinator()
150-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
150+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
151151

152152
coordinator.start()
153153
coordinator.activate(siteID: siteID)
@@ -185,7 +185,7 @@ final class ReviewsCoordinatorTests: XCTestCase {
185185
sessionManager.setStoreId(1_000)
186186

187187
let coordinator = makeReviewsCoordinator()
188-
let pushNotification = PushNotification(noteID: 1_234, kind: .comment, title: "", subtitle: "", message: "")
188+
let pushNotification = PushNotification(noteID: 1_234, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
189189
let differentSiteID: Int64 = 2_000_111
190190

191191
coordinator.start()

WooCommerce/WooCommerceTests/ViewRelated/Orders/OrderListViewModelTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ final class OrderListViewModelTests: XCTestCase {
204204
viewModel.activate()
205205

206206
// Act
207-
let notification = PushNotification(noteID: 1, kind: .storeOrder, title: "", subtitle: "", message: "")
207+
let notification = PushNotification(noteID: 1, siteID: 1, kind: .storeOrder, title: "", subtitle: "", message: "")
208208
pushNotificationsManager.sendForegroundNotification(notification)
209209

210210
// Assert
@@ -224,7 +224,7 @@ final class OrderListViewModelTests: XCTestCase {
224224
viewModel.activate()
225225

226226
// Act
227-
let notification = PushNotification(noteID: 1, kind: .comment, title: "", subtitle: "", message: "")
227+
let notification = PushNotification(noteID: 1, siteID: 1, kind: .comment, title: "", subtitle: "", message: "")
228228
pushNotificationsManager.sendForegroundNotification(notification)
229229

230230
// Assert

0 commit comments

Comments
 (0)