Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 54 additions & 52 deletions lib/src/services/push_notification_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,62 +201,64 @@ class DefaultPushNotificationService implements IPushNotificationService {

// 7. Iterate through each subscribed user to create and send a
// personalized notification.
final sendFutures = <Future<void>>[];
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 = <InAppNotification>[];
final userDeviceTokensMapForSending = <String, List<String>>{};

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(
Expand Down
Loading