diff options
Diffstat (limited to 'player-android/lib/screens/settings_screen.dart')
| -rw-r--r-- | player-android/lib/screens/settings_screen.dart | 37 |
1 files changed, 37 insertions, 0 deletions
diff --git a/player-android/lib/screens/settings_screen.dart b/player-android/lib/screens/settings_screen.dart index 53e171a..1260273 100644 --- a/player-android/lib/screens/settings_screen.dart +++ b/player-android/lib/screens/settings_screen.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import '../app_routes.dart'; import '../providers/api_client_provider.dart'; import '../providers/auth_state_provider.dart'; +import '../providers/current_user_provider.dart'; import '../providers/settings_provider.dart'; import '../providers/theme_provider.dart'; @@ -20,6 +21,10 @@ import '../providers/theme_provider.dart'; /// triggers go_router's redirect callback (via [refreshListenable]) and /// navigates to /login automatically. An explicit [context.go] acts as a /// safety net in case the redirect has not fired yet. +/// - The Admin section (Manage Users entry) is shown only when +/// [currentUserProvider] resolves to a user with [User.isAdmin] == true. +/// Non-admin users never see the tile; the server also enforces this via +/// 403 on the API endpoints, so the gating is defence-in-depth in the UI. /// - All async continuations guard on [mounted] to prevent setState/context /// calls after widget disposal. class SettingsScreen extends ConsumerStatefulWidget { @@ -100,6 +105,10 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> { // Watch settings to seed the URL field on first load. final settingsAsync = ref.watch(settingsProvider); + // Watch the current user to conditionally show the Admin section. + // currentUserProvider is autoDispose and resolves to null for non-admins. + final isAdmin = ref.watch(currentUserProvider).valueOrNull?.isAdmin ?? false; + // Seed the URL text field exactly once, after settings have loaded. // Doing this in build (rather than initState) ensures we have the loaded // value; [_urlInitialised] prevents clobbering an in-progress edit. @@ -247,6 +256,34 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> { trailing: const Icon(Icons.chevron_right), onTap: () => context.go(AppRoutes.shares), ), + + // ---------------------------------------------------------------- + // Admin section: only visible to admin users. + // Non-admin users are gated out here; the server enforces this + // independently via 403 responses, making this defence-in-depth. + // ---------------------------------------------------------------- + if (isAdmin) ...[ + const SizedBox(height: 32), + const Divider(), + const SizedBox(height: 24), + + Text( + 'Administration', + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 12), + + // Manage Users tile — navigates to /admin/users. + ListTile( + key: const Key('settings_manage_users'), + contentPadding: EdgeInsets.zero, + leading: const Icon(Icons.manage_accounts_outlined), + title: const Text('Manage Users'), + subtitle: const Text('Create and delete user accounts'), + trailing: const Icon(Icons.chevron_right), + onTap: () => context.go(AppRoutes.adminUsers), + ), + ], ], ), ), |
