From ca6a5105066fe475bba612eec95de772b1490ddb Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 22 Nov 2025 09:18:30 +0100 Subject: [PATCH 1/4] refactor(push notifications): improve efficiency and readability - Reorganized the notification sending process to create all InAppNotification documents in parallel before sending - Reduced nested code level by checking userDeviceTokens at the beginning of the loop - Renamed variables for clarity and created a new map for userDeviceTokens for sending - Improved code readability and structure without changing functionality --- .../services/push_notification_service.dart | 106 +++++++++--------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/lib/src/services/push_notification_service.dart b/lib/src/services/push_notification_service.dart index cb1fe65..847cb4a 100644 --- a/lib/src/services/push_notification_service.dart +++ b/lib/src/services/push_notification_service.dart @@ -201,62 +201,64 @@ class DefaultPushNotificationService implements IPushNotificationService { // 7. Iterate through each subscribed user to create and send a // personalized notification. - final sendFutures = >[]; - for (final userId in userIds) { - // Create the InAppNotification record first to get its unique ID. - final notificationId = ObjectId(); - final notification = InAppNotification( - id: notificationId.oid, - userId: userId, - payload: PushNotificationPayload( - title: headline.title, - body: headline.excerpt, - imageUrl: headline.imageUrl, - data: { - 'notificationType': - PushNotificationSubscriptionDeliveryType.breakingOnly.name, - 'contentType': 'headline', - 'headlineId': headline.id, - 'notificationId': notificationId.oid, - }, - ), - createdAt: DateTime.now(), - ); + final notificationsToCreate = []; + final userDeviceTokensMapForSending = >{}; - try { - await _inAppNotificationRepository.create(item: notification); - - // Efficiently retrieve the tokens for the current user from the map. - final userDeviceTokens = userDeviceTokensMap[userId] ?? []; - - if (userDeviceTokens.isNotEmpty) { - // Add the send operation to the list of futures. - // The result of this future will contain information about which - // tokens succeeded and which failed. - sendFutures.add( - client - .sendBulkNotifications( - deviceTokens: userDeviceTokens, - payload: notification.payload, - ) - .then((result) { - // After the send completes, trigger the cleanup process for - // any failed tokens. This is a fire-and-forget operation. - unawaited( - _cleanupInvalidDevices( - result.failedTokens, - primaryProvider, - ), - ); - }), - ); - } - } catch (e, s) { - _log.severe('Failed to process notification for user $userId.', e, s); + for (final userId in userIds) { + final userDeviceTokens = userDeviceTokensMap[userId]; + if (userDeviceTokens != null && userDeviceTokens.isNotEmpty) { + final notificationId = ObjectId(); + final notification = InAppNotification( + id: notificationId.oid, + userId: userId, + payload: PushNotificationPayload( + title: headline.title, + body: headline.excerpt, + imageUrl: headline.imageUrl, + data: { + 'notificationType': + PushNotificationSubscriptionDeliveryType.breakingOnly.name, + 'contentType': 'headline', + 'headlineId': headline.id, + 'notificationId': notificationId.oid, + }, + ), + createdAt: DateTime.now(), + ); + notificationsToCreate.add(notification); + userDeviceTokensMapForSending[userId] = userDeviceTokens; } } - // Await all the send operations to complete in parallel. + // 8. Create all InAppNotification documents in parallel. + final createFutures = notificationsToCreate.map( + (notification) => + _inAppNotificationRepository.create(item: notification), + ); + await Future.wait(createFutures); + _log.info( + 'Successfully created ${notificationsToCreate.length} in-app notifications.', + ); + + // 9. Dispatch all push notifications in parallel. + final sendFutures = notificationsToCreate.map((notification) { + final userDeviceTokens = + userDeviceTokensMapForSending[notification.userId] ?? []; + return client + .sendBulkNotifications( + deviceTokens: userDeviceTokens, + payload: notification.payload, + ) + .then((result) { + // After the send completes, trigger the cleanup process for + // any failed tokens. This is a fire-and-forget operation. + unawaited( + _cleanupInvalidDevices(result.failedTokens, primaryProvider), + ); + }); + }); + + // Await all the send operations to complete. await Future.wait(sendFutures); _log.info( From 4c8a09cc1be94574f806b529659ec4263adafbea Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 22 Nov 2025 09:20:21 +0100 Subject: [PATCH 2/4] fix(push-notification): Add non-null assertion for notification client - Ensure that the `client` is not null before calling `sendBulkNotifications` - This change improves the robustness of the push notification service --- lib/src/services/push_notification_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/services/push_notification_service.dart b/lib/src/services/push_notification_service.dart index 847cb4a..20490c2 100644 --- a/lib/src/services/push_notification_service.dart +++ b/lib/src/services/push_notification_service.dart @@ -244,7 +244,7 @@ class DefaultPushNotificationService implements IPushNotificationService { final sendFutures = notificationsToCreate.map((notification) { final userDeviceTokens = userDeviceTokensMapForSending[notification.userId] ?? []; - return client + return client! .sendBulkNotifications( deviceTokens: userDeviceTokens, payload: notification.payload, From 0634a768ed4d200c36bc68f2d84f760d7d1e4df4 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 22 Nov 2025 09:28:34 +0100 Subject: [PATCH 3/4] build(dependencies): add equatable dependency and update git sources - Add equatable as a direct dependency in pubspec.yaml - Update equatable version in pubspec.lock - Set equatable dependency type to "direct main" in pubspec.lock ``` --- pubspec.lock | 2 +- pubspec.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index 1023f8e..44a5078 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -249,7 +249,7 @@ packages: source: git version: "1.0.1" equatable: - dependency: transitive + dependency: "direct main" description: name: equatable sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" diff --git a/pubspec.yaml b/pubspec.yaml index cfd093c..d4eb70d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: git: url: https://github.com/flutter-news-app-full-source-code/email-sendgrid.git ref: v1.0.1 + equatable: ^2.0.7 http_client: git: url: https://github.com/flutter-news-app-full-source-code/http-client.git From 3587199bc8a72350d585a83df483bea1c32d0295 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 22 Nov 2025 09:29:32 +0100 Subject: [PATCH 4/4] refactor(notifications): simplify token mapping and remove redundant map --- lib/src/services/push_notification_service.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/src/services/push_notification_service.dart b/lib/src/services/push_notification_service.dart index 20490c2..dcdf039 100644 --- a/lib/src/services/push_notification_service.dart +++ b/lib/src/services/push_notification_service.dart @@ -202,7 +202,6 @@ class DefaultPushNotificationService implements IPushNotificationService { // 7. Iterate through each subscribed user to create and send a // personalized notification. final notificationsToCreate = []; - final userDeviceTokensMapForSending = >{}; for (final userId in userIds) { final userDeviceTokens = userDeviceTokensMap[userId]; @@ -226,7 +225,6 @@ class DefaultPushNotificationService implements IPushNotificationService { createdAt: DateTime.now(), ); notificationsToCreate.add(notification); - userDeviceTokensMapForSending[userId] = userDeviceTokens; } } @@ -242,8 +240,7 @@ class DefaultPushNotificationService implements IPushNotificationService { // 9. Dispatch all push notifications in parallel. final sendFutures = notificationsToCreate.map((notification) { - final userDeviceTokens = - userDeviceTokensMapForSending[notification.userId] ?? []; + final userDeviceTokens = userDeviceTokensMap[notification.userId] ?? []; return client! .sendBulkNotifications( deviceTokens: userDeviceTokens,