@@ -6,6 +6,7 @@ import 'package:data_repository/data_repository.dart';
66import 'package:flutter_news_app_mobile_client_full_source_code/notifications/services/push_notification_service.dart' ;
77import 'package:logging/logging.dart' ;
88import 'package:onesignal_flutter/onesignal_flutter.dart' ;
9+ import 'package:uuid/uuid.dart' ;
910
1011/// A concrete implementation of [PushNotificationService] for OneSignal.
1112class OneSignalPushNotificationService extends PushNotificationService {
@@ -115,31 +116,47 @@ class OneSignalPushNotificationService extends PushNotificationService {
115116 }
116117
117118 _logger.fine ('OneSignal Player ID received: $token ' );
118- // The device ID is now a composite key of userId and provider name to
119- // ensure idempotency and align with the backend's delete-then-create
120- // pattern.
121- final deviceId = '${userId }_${PushNotificationProvider .oneSignal .name }' ;
122-
123- // First, attempt to delete any existing device registration for this user
124- // and provider. This ensures a clean state and handles token updates
125- // by effectively performing a "delete-then-create".
119+
120+ // To ensure a user only receives notifications on their most recently
121+ // used device, we proactively clear all of their previous device
122+ // registrations before creating a new one. This prevents "ghost"
123+ // notifications from being sent to old, unused installations (e.g.,
124+ // after a user gets a new phone or reinstalls the app).
126125 try {
127- await _pushNotificationDeviceRepository.delete (id: deviceId);
128- _logger.info ('Existing device registration deleted for $deviceId .' );
129- } on NotFoundException {
130- _logger.info (
131- 'No existing device registration found for $deviceId . Proceeding with creation.' ,
126+ final existingDevices = await _pushNotificationDeviceRepository.readAll (
127+ userId: userId,
132128 );
129+
130+ if (existingDevices.items.isNotEmpty) {
131+ _logger.info (
132+ 'Found ${existingDevices .items .length } existing device(s) for user $userId . Deleting...' ,
133+ );
134+ await Future .wait (
135+ existingDevices.items.map (
136+ (device) => _pushNotificationDeviceRepository.delete (
137+ id: device.id,
138+ userId: userId,
139+ ),
140+ ),
141+ );
142+ _logger.info ('All existing devices for user $userId deleted.' );
143+ }
133144 } catch (e, s) {
145+ // If the proactive cleanup fails (e.g., due to a temporary network
146+ // issue), we log the error but do not halt the registration process.
147+ // The backend's passive, self-healing mechanism (which prunes invalid
148+ // tokens upon send failure) will eventually clean up any orphaned
149+ // device records. This ensures that a failure in cleanup does not
150+ // prevent the user from receiving notifications on their new device.
134151 _logger.warning (
135- 'Failed to delete existing device registration for $ deviceId . Proceeding with creation anyway . Error: $e ' ,
152+ 'Could not clean up existing devices for user $ userId , proceeding with registration . Error: $e ' ,
136153 e,
137154 s,
138155 );
139156 }
140157
141158 final newDevice = PushNotificationDevice (
142- id: deviceId ,
159+ id: const Uuid (). v4 () ,
143160 userId: userId,
144161 platform: Platform .isIOS ? DevicePlatform .ios : DevicePlatform .android,
145162 providerTokens: {PushNotificationProvider .oneSignal: token},
0 commit comments