diff --git a/lib/src/core/auth/data/repositories/onboarding_repository_impl.dart b/lib/src/core/auth/data/repositories/onboarding_repository_impl.dart index ef4787d0..07b91cda 100644 --- a/lib/src/core/auth/data/repositories/onboarding_repository_impl.dart +++ b/lib/src/core/auth/data/repositories/onboarding_repository_impl.dart @@ -30,7 +30,11 @@ class OnboardingRepositoryImpl implements OnboardingRepository { @override Future hasSparkProfile() async { - if (_did == null) return false; + await _authRepository.initializationComplete; + + if (_did == null || _did!.isEmpty) { + return false; + } final uri = AtUri.parse('at://$_did/so.sprk.actor.profile/self'); try { @@ -38,11 +42,11 @@ class OnboardingRepositoryImpl implements OnboardingRepository { _logger.i('Spark profile found: ${response.record.toJson()}'); return response.record.toJson().isNotEmpty; } catch (e) { - // Treat 404 and 'Could not locate record' 400 errors as no profile + // Treat explicit "record not found" failures as no profile. final msg = e.toString().toLowerCase(); if (msg.contains('404') || msg.contains('could not locate record') || - msg.contains('400')) { + msg.contains('record not found')) { return false; } _logger.e('Error checking Spark profile', error: e); diff --git a/lib/src/core/routing/app_router.dart b/lib/src/core/routing/app_router.dart index 5f2c448d..50890439 100644 --- a/lib/src/core/routing/app_router.dart +++ b/lib/src/core/routing/app_router.dart @@ -9,11 +9,17 @@ import 'package:spark/src/core/auth/data/repositories/auth_repository.dart'; import 'package:spark/src/core/auth/data/repositories/onboarding_repository.dart'; import 'package:spark/src/core/network/atproto/atproto.dart'; import 'package:spark/src/core/routing/pages.dart'; +import 'package:spark/src/core/utils/logging/log_service.dart'; +import 'package:spark/src/core/utils/logging/logger.dart'; import 'package:spark/src/features/profile/ui/pages/user_list_page.dart'; part 'app_router.gr.dart'; class AuthGuard extends AutoRouteGuard { + final SparkLogger _logger = GetIt.instance().getLogger( + 'AuthGuard', + ); + @override Future onNavigation( NavigationResolver resolver, @@ -23,22 +29,39 @@ class AuthGuard extends AutoRouteGuard { final onboardingRepository = GetIt.instance(); try { - final hasSpark = await onboardingRepository.hasSparkProfile(); + await authRepository.initializationComplete; + final isSessionValid = await authRepository.validateSession(); - if (!hasSpark) { + if (!isSessionValid) { + _logger.i('Redirecting to register because the user is not signed in'); resolver.redirectUntil(const RegisterRoute()); return; } - final isSessionValid = await authRepository.validateSession(); + final hasSpark = await onboardingRepository.hasSparkProfile(); - if (!isSessionValid) { - resolver.redirectUntil(const LoginRoute()); + if (!hasSpark) { + _logger.i( + 'Redirecting authenticated user to onboarding because ' + 'their Spark profile is missing', + ); + resolver.redirectUntil(const OnboardingRoute()); return; } resolver.next(); - } catch (e) { + } catch (e, stackTrace) { + _logger.e( + 'Auth guard failed while resolving navigation', + error: e, + stackTrace: stackTrace, + ); + + if (authRepository.isAuthenticated) { + resolver.next(); + return; + } + resolver.redirectUntil(const RegisterRoute()); } } diff --git a/lib/src/features/auth/ui/pages/login_page.dart b/lib/src/features/auth/ui/pages/login_page.dart index 55d641db..075b0578 100644 --- a/lib/src/features/auth/ui/pages/login_page.dart +++ b/lib/src/features/auth/ui/pages/login_page.dart @@ -103,7 +103,7 @@ class _LoginPageState extends ConsumerState { if (!mounted) return; - context.router.replaceAll([const FeedsRoute()]); + context.router.replaceAll([const MainRoute()]); } else { context.router.replaceAll([const OnboardingRoute()]); } diff --git a/lib/src/features/auth/ui/pages/register_page.dart b/lib/src/features/auth/ui/pages/register_page.dart index 4472f4a4..8a1c85fd 100644 --- a/lib/src/features/auth/ui/pages/register_page.dart +++ b/lib/src/features/auth/ui/pages/register_page.dart @@ -83,7 +83,7 @@ class _RegisterPageState extends ConsumerState { if (!mounted) return; - context.router.replaceAll([const FeedsRoute()]); + context.router.replaceAll([const MainRoute()]); } else { context.router.replaceAll([const OnboardingRoute()]); } @@ -171,9 +171,8 @@ class _RegisterPageState extends ConsumerState { ), const SizedBox(height: 12), Text( - 'Spark is currently in public beta.\n' - 'We value your feedback and are still \n' - 'rapidly improving.', + 'Share videos, connect with friends,\n' + 'and take back your timeline.', style: AppTypography.textMediumMedium.copyWith( color: colorScheme.onSurfaceVariant, height: 1.5, diff --git a/lib/src/features/settings/providers/settings_provider.dart b/lib/src/features/settings/providers/settings_provider.dart index bdfeb620..a590db74 100644 --- a/lib/src/features/settings/providers/settings_provider.dart +++ b/lib/src/features/settings/providers/settings_provider.dart @@ -429,13 +429,6 @@ class Settings extends _$Settings { await _saveActiveFeedToStorage(feed); } - /// Debug method to reload settings and verify persistence - Future reloadSettingsForTesting() async { - logger.d('Manually reloading settings for testing...'); - _hasLoadedSettings = false; - await loadSettings(); - } - // Helper methods for working with Preferences List _getSavedFeedsFromPreferences(Preferences preferences) { diff --git a/lib/src/features/settings/ui/pages/settings_page.dart b/lib/src/features/settings/ui/pages/settings_page.dart index c87fd16f..83eeb248 100644 --- a/lib/src/features/settings/ui/pages/settings_page.dart +++ b/lib/src/features/settings/ui/pages/settings_page.dart @@ -1,4 +1,3 @@ -import 'package:atproto/core.dart'; import 'package:auto_route/auto_route.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; @@ -109,147 +108,6 @@ class _SettingsPageState extends ConsumerState { } } - Future _handleUpdateSparkPosts() async { - final logger = GetIt.instance().getLogger('Settings'); - - try { - // Show loading indicator - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => const Center(child: CircularProgressIndicator()), - ); - - final authRepository = GetIt.instance(); - - final did = authRepository.did; - if (did == null || did.isEmpty) { - throw Exception('Not authenticated'); - } - - final atproto = authRepository.atproto; - if (atproto == null) { - throw Exception('AtProto not initialized'); - } - - logger.i('Fetching Spark posts for DID: $did'); - - // Fetch Spark posts directly from atproto to get raw records with URIs - const collection = 'so.sprk.feed.post'; - logger.d('Fetching records from collection: $collection'); - - final result = await atproto.repo.listRecords( - repo: did, - collection: collection, - limit: 100, // Limit can't be more than 100 - ); - - final allRecords = result.data.records; - logger.i('Found ${allRecords.length} Spark posts (before filtering)'); - - // Filter out posts with a reply field (only keep top-level posts) - final topLevelRecords = allRecords.where((record) { - final value = record.value; - return !value.containsKey('reply'); - }).toList(); - - logger.i('Filtered to ${topLevelRecords.length} top-level posts'); - - // Filter to only old posts and convert them to new format - final oldPosts = - <({AtUri uri, String? cid, Map convertedValue})>[]; - - for (final record in topLevelRecords) { - final value = record.value; - - final isOldPost = - (value.containsKey('text') || value.containsKey('embed')) && - !value.containsKey('caption') && - !value.containsKey('media'); - - if (isOldPost) { - // Convert old post to new format - final converted = Map.from(value); - - // Move "text" to "caption": { "text": "..." } - if (converted.containsKey('text')) { - final text = converted.remove('text'); - converted['caption'] = {'text': text}; - } - - // Convert "embed" to "media" and update namespace - if (converted.containsKey('embed')) { - final embed = converted.remove('embed') as Map; - final embedType = embed[r'$type'] as String?; - - if (embedType != null) { - // Convert namespace from so.sprk.embed.* to so.sprk.media.* - final newType = embedType.replaceFirst( - 'so.sprk.embed.', - 'so.sprk.media.', - ); - embed[r'$type'] = newType; - } - - converted['media'] = embed; - } - - oldPosts.add(( - uri: record.uri, - cid: record.cid, - convertedValue: converted, - )); - } - } - - logger.i('Found ${oldPosts.length} old posts to convert'); - - var successCount = 0; - var errorCount = 0; - - if (oldPosts.isEmpty) { - logger.i('No old posts found'); - } else { - // Update records in the PDS - for (var i = 0; i < oldPosts.length; i++) { - final post = oldPosts[i]; - try { - // Update the record in the PDS with the converted value - final result = await atproto.repo.putRecord( - repo: did, - collection: collection, - rkey: post.uri.rkey, - record: post.convertedValue, - ); - - successCount++; - logger.i( - 'Updated Post ${i + 1}: ${post.uri}, New CID: ${result.data.cid}', - ); - } catch (e) { - errorCount++; - logger.e('Error updating Post ${i + 1}: ${post.uri}', error: e); - } - } - - logger.i( - 'Update complete: $successCount successful, $errorCount failed', - ); - } - - // Close loading dialog - if (mounted) { - context.router.maybePop(); - } - } catch (e) { - // Close loading dialog if it's open - if (mounted) { - context.router.maybePop(); - } - logger.e('Error fetching Spark records', error: e); - } - } - @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); @@ -318,29 +176,6 @@ class _SettingsPageState extends ConsumerState { ), ), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Container( - decoration: BoxDecoration( - color: Theme.of( - context, - ).colorScheme.surfaceContainerHighest.withValues(alpha: 0.5), - borderRadius: BorderRadius.circular(12), - ), - child: ListTile( - title: const Text( - 'Import Legacy Posts', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - trailing: const Icon(FluentIcons.database_24_regular), - onTap: _handleUpdateSparkPosts, - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 4, - ), - ), - ), - ), Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Container(