registerDevice method
Registers or updates the current device in Firestore.
Called automatically on login/auth refresh when connected to AuthService. Creates or updates the device document with current timezone, offset, platform, app version, and marks the device as active.
This is best-effort and should not block app startup. On success, updates internal cache for timezone/offset throttling.
When Called
- On initial login (via
addOnAuthenticatedCallback) - Can be called manually if needed
Returns
Right(unit)on successLeft(RepositoryFailure)on failure (logged, not surfaced to user)
Example
final result = await deviceService.registerDevice();
result.fold(
(failure) => log('Device registration failed: $failure'),
(_) => log('Device registered successfully'),
);
Implementation
@override
Future<Either<RepositoryFailure, Unit>> registerDevice() async {
logd('DeviceService: registerDevice called');
try {
final deviceId = await getDeviceId();
final timezone = await getCurrentTimezone();
final offsetMinutes = _getCurrentOffsetMinutes();
final platform = _getCurrentPlatform();
final platformString = DevicePlatformSerialization.serialize(platform);
final packageInfo = await PackageInfo.fromPlatform();
// Check authentication - if not authenticated, store pending and return
if (!_isUserAuthenticated()) {
logd('DeviceService: User not authenticated, storing pending registration');
await _updatePendingPayload(
deviceId: deviceId,
timezone: timezone,
timezoneOffsetMinutes: offsetMinutes,
platform: platformString,
appVersion: packageInfo.version,
touch: true,
hasChangedFields: true,
);
// Return success since we've stored it for later - this is best-effort
return const Right(unit);
}
logd('DeviceService: Registering device $deviceId with timezone $timezone');
// First, try to flush any existing pending payload
// This ensures we don't lose pending changes when registering
await _flushPendingPayload();
final result = await _deviceCallable.call({
'action': 'register',
'deviceId': deviceId,
'timezone': timezone,
'timezoneOffsetMinutes': offsetMinutes,
'platform': platformString,
'appVersion': packageInfo.version,
});
// Check for success
final data = Map<String, dynamic>.from(result.data as Map);
if (data['success'] != true) {
logw('DeviceService: Registration response indicated failure');
// Store in pending payload for retry
await _updatePendingPayload(
deviceId: deviceId,
timezone: timezone,
timezoneOffsetMinutes: offsetMinutes,
platform: platformString,
appVersion: packageInfo.version,
touch: true,
hasChangedFields: true,
);
return const Left(RepositoryFailure.unexpected);
}
// Update cached values on success
_cachedTimezone = timezone;
_cachedOffsetMinutes = offsetMinutes;
_lastServerSyncAt = DateTime.now();
_lastTouchAt = DateTime.now(); // registerDevice also updates lastActiveAt
// Clear any pending payload since we just synced
await _clearPendingPayload();
// Log if timezone changed
if (data['timezoneChanged'] == true) {
logd('DeviceService: Timezone changed from ${data['previousTimezone']} to $timezone');
}
logd('DeviceService: Device registered successfully');
return const Right(unit);
} on FirebaseFunctionsException catch (e) {
loge(e, 'DeviceService: Firebase Functions error during registration');
// Store in pending payload for retry on network/transient errors
if (_shouldStorePendingOnError(e)) {
await _storePendingRegistration();
}
return _mapFirebaseFunctionsException(e);
} catch (e) {
loge(e, 'DeviceService: Unexpected error during registration');
// Store in pending payload for retry
await _storePendingRegistration();
return const Left(RepositoryFailure.unexpected);
}
}