diff --git a/lib/main.dart b/lib/main.dart index 2c1d718..5396968 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,9 +24,9 @@ void main() { } final GlobalKey _rootNavigatorKey = -GlobalKey(debugLabel: 'root'); + GlobalKey(debugLabel: 'root'); final GlobalKey _userShellNavigatorKey = -GlobalKey(debugLabel: 'user'); + GlobalKey(debugLabel: 'user'); class OutbagApp extends StatefulWidget { const OutbagApp({super.key}); @@ -39,7 +39,6 @@ class _OutbagAppState extends State { // assume user is logged in // unless not userdata is found // or the userdata turns out to be wrong - AccountMeta? info; User? user; AppTheme theme = AppTheme.auto; @@ -49,7 +48,7 @@ class _OutbagAppState extends State { try { final theme = await AppTheme.fromDisk(); setState(() { - this.theme = theme; + this.theme = theme; }); } catch (_) {} } @@ -59,15 +58,14 @@ class _OutbagAppState extends State { try { final user = await User.fromDisk(); setState(() { - this.user = user; + this.user = user; }); - fetchInfo(user); } catch (_) { // user unavailable // invalid credentials // log out setState(() { - user = null; + user = null; }); } } @@ -77,177 +75,186 @@ class _OutbagAppState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { - loadTheme(); - loadUser(); + loadTheme(); + loadUser(); }); } - void fetchInfo(User user) async { + Future fetchInfo(User user) async { + AccountMeta? info; // try to obtain user account information // with existing details // NOTE: also functions as a way to verify ther data - doNetworkRequest(null, - req: () => postWithCreadentials( - target: user.server, - path: 'getMyAccount', - credentials: user, - body: {}), - onOK: (body) { - final info = AccountMeta.fromJSON(body['data']); - setState(() { - this.info = info; - }); - }, - onServerErr: (_) { - // credentials are wrong - // log out + await doNetworkRequest(null, + req: () => postWithCreadentials( + target: user.server, + path: 'getMyAccount', + credentials: user, + body: {}), + onOK: (body) async { + final i = AccountMeta.fromJSON(body['data']); + info = i; + }, + onServerErr: (_) { + info = null; - setState(() { - info = null; + setState(() { this.user = null; + }); + return true; + }, + onNetworkErr: () { + info = null; + // user is currently offline + // approve login, + // until user goes back offline + // NOTE TODO: check user data once online + return true; }); - return true; - }, - onNetworkErr: () { - // user is currently offline - // approve login, - // until user goes back offline - // NOTE TODO: check user data once online - return true; - }); + + return info; } @override Widget build(BuildContext context) { return MultiProvider( - providers: [ - Provider.value( - value: info, - ), - Provider.value(value: theme), - ], - child: MaterialApp.router( - title: "Outbag", - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - AppLocalizations.delegate + providers: [ + Provider.value(value: theme), ], - supportedLocales: AppLocalizations.supportedLocales, - themeMode: theme.mode, - theme: ThemeData(useMaterial3: true, brightness: Brightness.light), - darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark), - routerConfig: GoRouter( - navigatorKey: _rootNavigatorKey, - initialLocation: '/', - redirect: (context, state) async { - if (user == null) { - // prelogin - if (!state.subloc.startsWith('/welcome')) { - // prevent unauthorized user from accessing home - return '/welcome'; - } - } else { - // post login - if (state.subloc.startsWith('/welcome')) { - // prevent authorized user from accessing /welcome - return '/'; - } - } + child: MaterialApp.router( + title: "Outbag", + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + AppLocalizations.delegate + ], + supportedLocales: AppLocalizations.supportedLocales, + themeMode: theme.mode, + theme: ThemeData(useMaterial3: true, brightness: Brightness.light), + darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark), + routerConfig: GoRouter( + navigatorKey: _rootNavigatorKey, + initialLocation: '/', + redirect: (context, state) async { + if (user == null) { + // prelogin + if (!state.subloc.startsWith('/welcome')) { + // prevent unauthorized user from accessing home + return '/welcome'; + } + } else { + // post login + if (state.subloc.startsWith('/welcome')) { + // prevent authorized user from accessing /welcome + return '/'; + } + } - return null; - }, - routes: [ - // unauthorized routes - GoRoute( - name: 'welcome', - path: '/welcome', - builder: (context, state) => const WelcomePage(), + return null; + }, routes: [ + // unauthorized routes GoRoute( - name: 'signin', - path: 'signin', - builder: (context, state) => AuthPage(mode: Mode.signin, refresh: loadUser), - ), - GoRoute( - name: 'signup', - path: 'signup', - builder: (context, state) => AuthPage(mode: Mode.signup, refresh: loadUser), - ), - GoRoute( - name: 'signup-ota', - path: 'signup-ota', - builder: (context, state) => AuthPage(mode: Mode.signupOTA, refresh: loadUser), - ), - ]), - - // authorized routes - ShellRoute( - navigatorKey: _userShellNavigatorKey, - builder: (context, state, child) => - Provider.value(value: user!, child: child), - routes: [ - GoRoute( - path: '/', - name: 'home', - builder: (context, state) => const HomePage(), - routes: [ - GoRoute( - name: 'settings', - path: 'settings', - builder: (context, state) => SettingsPage(refreshTheme: loadTheme)), - GoRoute( - path: 'join-room', - name: 'add-room', - builder: (context, state) => - const JoinRoomPage(), - routes: [ - GoRoute( - path: 'new', - name: 'new-room', - builder: (context, state) => - const NewRoomPage()), + name: 'welcome', + path: '/welcome', + builder: (context, state) => const WelcomePage(), + routes: [ + GoRoute( + name: 'signin', + path: 'signin', + builder: (context, state) => + AuthPage(mode: Mode.signin, refresh: loadUser), + ), + GoRoute( + name: 'signup', + path: 'signup', + builder: (context, state) => + AuthPage(mode: Mode.signup, refresh: loadUser), + ), + GoRoute( + name: 'signup-ota', + path: 'signup-ota', + builder: (context, state) => + AuthPage(mode: Mode.signupOTA, refresh: loadUser), + ), ]), - GoRoute( - name: 'room', - path: 'r/:server/:id', - builder: (context, state) => RoomPage( - state.params['server'] ?? '', - state.params['id'] ?? ''), - routes: [ - GoRoute( - name: 'edit-room', - path: 'edit', - builder: (context, state) => EditRoomPage( - state.params['server'] ?? '', - state.params['id'] ?? '')), - GoRoute( - name: 'room-members', - path: 'members', - builder: (context, state) => - ManageRoomMembersPage( - state.params['server'] ?? '', - state.params['id'] ?? '')), - GoRoute( - name: 'room-permissions', - path: 'roles', - builder: (context, state) => - EditRoomPermissionSetPage( - state.params['server'] ?? '', - state.params['id'] ?? '')), - ]) - ]), - ]), - // routes that can be accessed - // with and without an account - // i.e the about screen - GoRoute( - path: '/about', - name: 'about', - builder: (context, state) => const Text('About')) - ]), - )); + // authorized routes + ShellRoute( + navigatorKey: _userShellNavigatorKey, + builder: (context, state, child) => Provider.value( + value: user!, + child: FutureProvider( + initialData: null, + child: child, + create: (context)=>fetchInfo(context.read()), + )), + routes: [ + GoRoute( + path: '/', + name: 'home', + builder: (context, state) => const HomePage(), + routes: [ + GoRoute( + name: 'settings', + path: 'settings', + builder: (context, state) => + SettingsPage( + refreshTheme: loadTheme, + refreshUser: loadUser + )), + GoRoute( + path: 'join-room', + name: 'add-room', + builder: (context, state) => + const JoinRoomPage(), + routes: [ + GoRoute( + path: 'new', + name: 'new-room', + builder: (context, state) => + const NewRoomPage()), + ]), + GoRoute( + name: 'room', + path: 'r/:server/:id', + builder: (context, state) => RoomPage( + state.params['server'] ?? '', + state.params['id'] ?? ''), + routes: [ + GoRoute( + name: 'edit-room', + path: 'edit', + builder: (context, state) => EditRoomPage( + state.params['server'] ?? '', + state.params['id'] ?? '')), + GoRoute( + name: 'room-members', + path: 'members', + builder: (context, state) => + ManageRoomMembersPage( + state.params['server'] ?? '', + state.params['id'] ?? '')), + GoRoute( + name: 'room-permissions', + path: 'roles', + builder: (context, state) => + EditRoomPermissionSetPage( + state.params['server'] ?? '', + state.params['id'] ?? '')), + ]) + ]), + ]), + + // routes that can be accessed + // with and without an account + // i.e the about screen + GoRoute( + path: '/about', + name: 'about', + builder: (context, state) => const Text('About')) + ]), + )); } } diff --git a/lib/screens/settings/main.dart b/lib/screens/settings/main.dart index cd01d72..3348731 100644 --- a/lib/screens/settings/main.dart +++ b/lib/screens/settings/main.dart @@ -10,7 +10,9 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class SettingsPage extends StatefulWidget { Function refreshTheme; - SettingsPage({super.key, required this.refreshTheme}); + Function refreshUser; + SettingsPage( + {super.key, required this.refreshTheme, required this.refreshUser}); @override State createState() => _SettingsPageState(); @@ -204,6 +206,7 @@ class _SettingsPageState extends State { // go back home router.pushReplacementNamed('home'); + widget.refreshUser(); }, after: () { // close popup @@ -254,6 +257,7 @@ class _SettingsPageState extends State { // go back home router.pushReplacementNamed('home'); + widget.refreshUser(); }, child: Text(AppLocalizations.of(context)!.yes), ) diff --git a/lib/tools/fetch_wrapper.dart b/lib/tools/fetch_wrapper.dart index 4aed109..7e35bda 100644 --- a/lib/tools/fetch_wrapper.dart +++ b/lib/tools/fetch_wrapper.dart @@ -4,7 +4,7 @@ import 'package:outbag_app/backend/request.dart'; import 'package:outbag_app/tools/snackbar.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -void doNetworkRequest(ScaffoldMessengerState? sm, +Future doNetworkRequest(ScaffoldMessengerState? sm, {required Future Function() req, Function(Map body)? onOK, bool Function()? onNetworkErr,