From d9ffc780bf55078a1708acde0cf8f9bfda3aa936 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Sat, 21 Mar 2026 22:26:58 -0400 Subject: [PATCH] feat: account management screen --- .../data/repositories/auth_repository.dart | 3 + .../repositories/auth_repository_impl.dart | 3 + .../settings/ui/pages/settings_page.dart | 85 +++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/lib/src/core/auth/data/repositories/auth_repository.dart b/lib/src/core/auth/data/repositories/auth_repository.dart index 76881910..046d72cf 100644 --- a/lib/src/core/auth/data/repositories/auth_repository.dart +++ b/lib/src/core/auth/data/repositories/auth_repository.dart @@ -15,6 +15,9 @@ abstract class AuthRepository { /// Gets the current user's handle String? get handle; + /// Gets the current user's PDS endpoint + String? get pdsEndpoint; + /// Gets the AT Protocol client ATProto? get atproto; diff --git a/lib/src/core/auth/data/repositories/auth_repository_impl.dart b/lib/src/core/auth/data/repositories/auth_repository_impl.dart index ef5b2aba..82a50bf9 100644 --- a/lib/src/core/auth/data/repositories/auth_repository_impl.dart +++ b/lib/src/core/auth/data/repositories/auth_repository_impl.dart @@ -66,6 +66,9 @@ class AuthRepositoryImpl implements AuthRepository { @override String? get handle => _handle; + @override + String? get pdsEndpoint => _pdsEndpoint; + @override ATProto? get atproto => _atProto; diff --git a/lib/src/features/settings/ui/pages/settings_page.dart b/lib/src/features/settings/ui/pages/settings_page.dart index e2fee106..c87fd16f 100644 --- a/lib/src/features/settings/ui/pages/settings_page.dart +++ b/lib/src/features/settings/ui/pages/settings_page.dart @@ -10,6 +10,7 @@ import 'package:spark/src/core/routing/app_router.dart'; import 'package:spark/src/core/utils/logging/log_service.dart'; import 'package:spark/src/features/auth/auth.dart'; import 'package:spark/src/features/auth/providers/auth_providers.dart'; +import 'package:url_launcher/url_launcher.dart'; @RoutePage() class SettingsPage extends ConsumerStatefulWidget { @@ -20,6 +21,10 @@ class SettingsPage extends ConsumerStatefulWidget { } class _SettingsPageState extends ConsumerState { + static final Uri _manageAccountUri = Uri.parse( + 'https://bsky.app/settings/account', + ); + Future _handleLogout() async { try { // Show loading indicator @@ -47,6 +52,63 @@ class _SettingsPageState extends ConsumerState { } } + Future _handleManageAccount() async { + final authRepository = GetIt.instance(); + final pdsUrl = authRepository.pdsEndpoint ?? 'your PDS URL'; + final shouldOpen = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Open Bluesky account management?'), + content: Text( + 'This opens the Bluesky account management screen. ' + 'You may have to log in again.\n\n' + 'If prompted for an account provider, use:\n$pdsUrl', + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Cancel'), + ), + FilledButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Open'), + ), + ], + ), + ); + + if (shouldOpen != true || !mounted) { + return; + } + + final logger = GetIt.instance().getLogger('Settings'); + + try { + final didLaunch = await launchUrl( + _manageAccountUri, + mode: LaunchMode.externalApplication, + ); + + if (!didLaunch && mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Unable to open link right now.')), + ); + } + } catch (error, stackTrace) { + logger.e( + 'Failed to launch manage account URL: $_manageAccountUri', + error: error, + stackTrace: stackTrace, + ); + + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Unable to open link right now.')), + ); + } + } + } + Future _handleUpdateSparkPosts() async { final logger = GetIt.instance().getLogger('Settings'); @@ -305,6 +367,29 @@ 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( + 'Manage Account', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + trailing: const Icon(FluentIcons.open_24_regular), + onTap: _handleManageAccount, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + ), + ), + ), Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Container( -- 2.51.2