Fixed bug where homescreen would load twice
Moved AccountMeta provider into <User> context and migrated to using a FutureProvider to perform the network request NOTE: Bug was caused by AccountMeta? being loaded late, causing the root provider to be reloaded.
This commit is contained in:
parent
1af8d6f068
commit
ecccec7950
3 changed files with 172 additions and 161 deletions
325
lib/main.dart
325
lib/main.dart
|
@ -24,9 +24,9 @@ void main() {
|
|||
}
|
||||
|
||||
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
||||
GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||
GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
||||
GlobalKey<NavigatorState>(debugLabel: 'user');
|
||||
GlobalKey<NavigatorState>(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<AccountMeta?> 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<AccountMeta?>.value(
|
||||
value: info,
|
||||
),
|
||||
Provider<AppTheme>.value(value: theme),
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
title: "Outbag",
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
AppLocalizations.delegate
|
||||
providers: [
|
||||
Provider<AppTheme>.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: <RouteBase>[
|
||||
// unauthorized routes
|
||||
GoRoute(
|
||||
name: 'welcome',
|
||||
path: '/welcome',
|
||||
builder: (context, state) => const WelcomePage(),
|
||||
return null;
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
// 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: <RouteBase>[
|
||||
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: <RouteBase>[
|
||||
GoRoute(
|
||||
path: 'new',
|
||||
name: 'new-room',
|
||||
builder: (context, state) =>
|
||||
const NewRoomPage()),
|
||||
name: 'welcome',
|
||||
path: '/welcome',
|
||||
builder: (context, state) => const WelcomePage(),
|
||||
routes: <RouteBase>[
|
||||
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: <RouteBase>[
|
||||
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<User>()),
|
||||
)),
|
||||
routes: <RouteBase>[
|
||||
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: <RouteBase>[
|
||||
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: <RouteBase>[
|
||||
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'))
|
||||
]),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<StatefulWidget> createState() => _SettingsPageState();
|
||||
|
@ -204,6 +206,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
|
||||
// go back home
|
||||
router.pushReplacementNamed('home');
|
||||
widget.refreshUser();
|
||||
},
|
||||
after: () {
|
||||
// close popup
|
||||
|
@ -254,6 +257,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||
|
||||
// go back home
|
||||
router.pushReplacementNamed('home');
|
||||
widget.refreshUser();
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.yes),
|
||||
)
|
||||
|
|
|
@ -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<void> doNetworkRequest(ScaffoldMessengerState? sm,
|
||||
{required Future<Response> Function() req,
|
||||
Function(Map<String, dynamic> body)? onOK,
|
||||
bool Function()? onNetworkErr,
|
||||
|
|
Loading…
Reference in a new issue