Skip to content

Commit 156b767

Browse files
committed
refactor(notifications): improve device registration process
- Replace composite device ID with UUID for better uniqueness - Implement proactive cleanup of existing devices for a user - Enhance error handling and logging for device registration - Update comments to reflect new logic and edge cases
1 parent 2bd03f9 commit 156b767

File tree

1 file changed

+32
-15
lines changed

1 file changed

+32
-15
lines changed

lib/notifications/services/one_signal_push_notification_service.dart

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:data_repository/data_repository.dart';
66
import 'package:flutter_news_app_mobile_client_full_source_code/notifications/services/push_notification_service.dart';
77
import 'package:logging/logging.dart';
88
import 'package:onesignal_flutter/onesignal_flutter.dart';
9+
import 'package:uuid/uuid.dart';
910

1011
/// A concrete implementation of [PushNotificationService] for OneSignal.
1112
class 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

Comments
 (0)