diff --git a/lib/services/github/github_sync_service.dart b/lib/services/github/github_sync_service.dart index 47c5ec9..03fdcb9 100644 --- a/lib/services/github/github_sync_service.dart +++ b/lib/services/github/github_sync_service.dart @@ -36,10 +36,10 @@ class GitHubUploadResult { /// Compatibility facade for the historical ride-detail sync service. /// -/// MotorSignal cloud backup is Firebase Authentication + Firestore + R2. -/// The old GitHub PAT exporter remains available only through the explicit -/// `uploadRideToGitHub` developer/export path and is never used by the normal -/// History -> Upload Now action. +/// Normal MotorSignal cloud backup is Firebase Authentication + Firestore + R2. +/// The historical GitHub PAT exporter remains available only through the +/// explicit `uploadRideToGitHub` developer/export path and is never used by +/// the normal History -> Upload Now action. class GitHubSyncService extends ChangeNotifier { final AppDatabase _db; final GitHubAuthService _githubAuth; @@ -51,12 +51,17 @@ class GitHubSyncService extends ChangeNotifier { String _statusMessage = ''; final Set _uploadQueue = {}; + /// Keeps the historical constructor compatible with tests/callers that pass + /// an HTTP client as the optional fourth argument. Production additionally + /// supplies the shared Firebase AuthService as the fifth argument. GitHubSyncService( this._db, this._githubAuth, - this._authService, - this._prefs, [http.Client? client], - ) : _client = client ?? http.Client(); + this._prefs, [ + http.Client? client, + AuthService? authService, + ]) : _authService = authService ?? FirebaseAuthService(), + _client = client ?? http.Client(); GitHubUploadState get state => _state; String get statusMessage => _statusMessage; @@ -69,21 +74,18 @@ class GitHubSyncService extends ChangeNotifier { } bool isRideUploaded(int rideId) { - final scoped = _userScopedKey('motorcloud_uploaded_ride', rideId); - if (scoped == null) return false; - return _prefs.getBool(scoped) ?? false; + final key = _userScopedKey('motorcloud_uploaded_ride', rideId); + return key != null && (_prefs.getBool(key) ?? false); } String? getRideCommitSha(int rideId) { - final scoped = _userScopedKey('motorcloud_ride_remote_id', rideId); - if (scoped == null) return null; - return _prefs.getString(scoped); + final key = _userScopedKey('motorcloud_ride_remote_id', rideId); + return key == null ? null : _prefs.getString(key); } String? getRideUploadTimestamp(int rideId) { - final scoped = _userScopedKey('motorcloud_uploaded_at_ride', rideId); - if (scoped == null) return null; - return _prefs.getString(scoped); + final key = _userScopedKey('motorcloud_uploaded_at_ride', rideId); + return key == null ? null : _prefs.getString(key); } void queueRideForUpload(int rideId) { @@ -105,23 +107,23 @@ class GitHubSyncService extends ChangeNotifier { /// Normal History -> Upload Now operation. /// - /// This deliberately ignores any configured GitHub PAT. A user's ride must - /// be written to that user's MotorCloud account, not to a public developer - /// repository. Authentication and ownership are enforced by Firebase/R2. + /// Deliberately ignores any configured GitHub PAT. A user's ride must be + /// written to that user's MotorCloud account, never to the developer's + /// public repository. Future uploadRide(int rideId, {bool isManual = false}) async { return _uploadToMotorCloud(rideId); } /// Explicit legacy/developer export. Not used by the normal ride UI. Future uploadRideToGitHub(int rideId) async { - final githubToken = _githubAuth.getToken(); - if (githubToken == null || githubToken.isEmpty) { + final token = _githubAuth.getToken(); + if (token == null || token.isEmpty) { return GitHubUploadResult( success: false, message: 'GitHub export requires an explicitly configured GitHub token.', ); } - return _uploadToGitHub(rideId, githubToken); + return _uploadToGitHub(rideId, token); } Future _uploadToMotorCloud(int rideId) async { @@ -137,7 +139,12 @@ class GitHubSyncService extends ChangeNotifier { _state = GitHubUploadState.success; _statusMessage = 'Ride #$rideId is already backed up to your MotorCloud account.'; notifyListeners(); - return GitHubUploadResult(success: true, message: _statusMessage, commitSha: getRideCommitSha(rideId), targetPath: 'MotorCloud'); + return GitHubUploadResult( + success: true, + message: _statusMessage, + commitSha: getRideCommitSha(rideId), + targetPath: 'MotorCloud', + ); } _state = GitHubUploadState.uploading; @@ -145,25 +152,25 @@ class GitHubSyncService extends ChangeNotifier { notifyListeners(); try { - final provider = FirebaseSyncProvider( - db: _db, - authService: _authService, - ); - final result = await provider.uploadRide( - SyncItem( - localRideId: rideId, - providerId: 'firebase', - createdAt: DateTime.now(), - ), - ); + final provider = FirebaseSyncProvider(db: _db, authService: _authService); + final result = await provider.uploadRide(SyncItem( + localRideId: rideId, + providerId: 'firebase', + createdAt: DateTime.now(), + )); if (result.success) { - final uploadedKey = _userScopedKey('motorcloud_uploaded_ride', rideId)!; - final timestampKey = _userScopedKey('motorcloud_uploaded_at_ride', rideId)!; - final remoteIdKey = _userScopedKey('motorcloud_ride_remote_id', rideId)!; - await _prefs.setBool(uploadedKey, true); - await _prefs.setString(timestampKey, DateTime.now().toIso8601String()); - if (result.remoteId != null) await _prefs.setString(remoteIdKey, result.remoteId!); + await _prefs.setBool(_userScopedKey('motorcloud_uploaded_ride', rideId)!, true); + await _prefs.setString( + _userScopedKey('motorcloud_uploaded_at_ride', rideId)!, + DateTime.now().toIso8601String(), + ); + if (result.remoteId != null) { + await _prefs.setString( + _userScopedKey('motorcloud_ride_remote_id', rideId)!, + result.remoteId!, + ); + } _uploadQueue.remove(rideId); _state = GitHubUploadState.success; _statusMessage = 'Ride #$rideId backed up to your MotorCloud account'; @@ -323,7 +330,7 @@ class GitHubSyncService extends ChangeNotifier { final githubSyncServiceProvider = ChangeNotifierProvider((ref) { final db = ref.watch(databaseProvider); final githubAuth = ref.watch(githubAuthServiceProvider); - final authService = ref.watch(authServiceProvider); final prefs = ref.watch(sharedPreferencesProvider); - return GitHubSyncService(db, githubAuth, authService, prefs); + final authService = ref.watch(authServiceProvider); + return GitHubSyncService(db, githubAuth, prefs, null, authService); });