actions-test/lib/main.dart
Jakob Meier 1af8d6f068
Migrated from localstore to shared preferences
(only for user, server and theme)

This was done, because localstore is somewhat inconsistent
in terms of events on different platforms.
Also storing user, server and theme using shared-preferences
should fit into flutters ecosystem a little better
2023-03-29 18:27:05 +02:00

253 lines
8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:outbag_app/backend/themes.dart';
import 'package:outbag_app/backend/user.dart';
import 'package:outbag_app/screens/room/edit.dart';
import 'package:outbag_app/screens/room/join.dart';
import 'package:outbag_app/screens/room/members.dart';
import 'package:outbag_app/screens/room/permissions.dart';
import 'package:outbag_app/screens/room/new.dart';
import 'package:outbag_app/screens/settings/main.dart';
import 'package:outbag_app/tools/fetch_wrapper.dart';
import 'package:provider/provider.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import './screens/home.dart';
import './screens/welcome.dart';
import './screens/room/main.dart';
import './screens/auth.dart';
import './backend/request.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const OutbagApp());
}
final GlobalKey<NavigatorState> _rootNavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'root');
final GlobalKey<NavigatorState> _userShellNavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'user');
class OutbagApp extends StatefulWidget {
const OutbagApp({super.key});
@override
State<StatefulWidget> createState() => _OutbagAppState();
}
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;
void loadTheme() async {
// load theme
try {
final theme = await AppTheme.fromDisk();
setState(() {
this.theme = theme;
});
} catch (_) {}
}
void loadUser() async {
// load user
try {
final user = await User.fromDisk();
setState(() {
this.user = user;
});
fetchInfo(user);
} catch (_) {
// user unavailable
// invalid credentials
// log out
setState(() {
user = null;
});
}
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
loadTheme();
loadUser();
});
}
void fetchInfo(User user) async {
// 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
setState(() {
info = null;
this.user = null;
});
return true;
},
onNetworkErr: () {
// user is currently offline
// approve login,
// until user goes back offline
// NOTE TODO: check user data once online
return true;
});
}
@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
],
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(),
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),
),
]),
// 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()),
]),
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'))
]),
));
}
}