8fffafde47
Translations are provided in *.arb* format. Some keys have descriptions (indicated by leading @-symbol). Descriptions should not be copied into the translation itself. Currently only English is supported (app_en.arb), but German is planned. Apparently weblate merged .arb support at some time, so it would be nice to enable community translations at some point.
277 lines
8.7 KiB
Dart
277 lines
8.7 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;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
// wait for user to be authorized
|
|
User.listen((_) async {
|
|
try {
|
|
final user = await User.fromDisk();
|
|
setState(() {
|
|
this.user = user;
|
|
});
|
|
fetchInfo(user);
|
|
} catch (_) {
|
|
// no userdata found
|
|
setState(() {
|
|
user = null;
|
|
});
|
|
}
|
|
});
|
|
|
|
AppTheme.listen((_) async {
|
|
final theme = await AppTheme.fromDisk();
|
|
setState(() {
|
|
this.theme = theme;
|
|
});
|
|
});
|
|
|
|
// load theme
|
|
try {
|
|
final theme = await AppTheme.fromDisk();
|
|
setState(() {
|
|
this.theme = theme;
|
|
});
|
|
} catch (_) {}
|
|
|
|
// load user
|
|
try {
|
|
final user = await User.fromDisk();
|
|
setState(() {
|
|
this.user = user;
|
|
});
|
|
fetchInfo(user);
|
|
} catch (_) {
|
|
// user unavailable
|
|
// invalid credentials
|
|
// log out
|
|
setState(() {
|
|
user = null;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
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) =>
|
|
const AuthPage(mode: Mode.signin),
|
|
),
|
|
GoRoute(
|
|
name: 'signup',
|
|
path: 'signup',
|
|
builder: (context, state) =>
|
|
const AuthPage(mode: Mode.signup),
|
|
),
|
|
GoRoute(
|
|
name: 'signup-ota',
|
|
path: 'signup-ota',
|
|
builder: (context, state) =>
|
|
const AuthPage(mode: Mode.signupOTA),
|
|
),
|
|
]),
|
|
|
|
// 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) =>
|
|
const SettingsPage()),
|
|
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'))
|
|
]),
|
|
));
|
|
}
|
|
}
|