2023-03-17 09:41:08 +01:00
|
|
|
import 'package:flutter/material.dart';
|
2023-03-28 14:54:39 +02:00
|
|
|
import 'package:go_router/go_router.dart';
|
2023-04-01 09:49:30 +02:00
|
|
|
|
2023-03-25 14:29:28 +01:00
|
|
|
import 'package:outbag_app/backend/themes.dart';
|
2023-03-18 20:24:48 +01:00
|
|
|
import 'package:outbag_app/backend/user.dart';
|
2023-04-01 09:49:30 +02:00
|
|
|
import 'package:outbag_app/backend/request.dart';
|
2023-04-01 20:13:03 +02:00
|
|
|
import 'package:outbag_app/screens/room/categories/edit.dart';
|
2023-04-05 19:04:18 +02:00
|
|
|
import 'package:outbag_app/screens/room/items/edit.dart';
|
2024-02-22 20:36:59 +01:00
|
|
|
import 'package:outbag_app/screens/room/items/new.dart';
|
2023-04-04 20:28:26 +02:00
|
|
|
import 'package:outbag_app/screens/room/products/edit.dart';
|
|
|
|
import 'package:outbag_app/screens/room/products/view.dart';
|
2023-04-01 09:49:30 +02:00
|
|
|
|
|
|
|
import 'package:outbag_app/tools/fetch_wrapper.dart';
|
|
|
|
|
|
|
|
import 'package:outbag_app/screens/home.dart';
|
|
|
|
import 'package:outbag_app/screens/welcome.dart';
|
|
|
|
import 'package:outbag_app/screens/auth.dart';
|
|
|
|
|
|
|
|
import 'package:outbag_app/screens/room/new.dart';
|
2023-03-20 21:19:25 +01:00
|
|
|
import 'package:outbag_app/screens/room/join.dart';
|
2023-04-01 09:49:30 +02:00
|
|
|
import 'package:outbag_app/screens/room/main.dart';
|
2023-04-01 09:36:59 +02:00
|
|
|
import 'package:outbag_app/screens/room/about/members.dart';
|
|
|
|
import 'package:outbag_app/screens/room/about/permissions.dart';
|
2023-03-25 14:29:28 +01:00
|
|
|
import 'package:outbag_app/screens/settings/main.dart';
|
2023-04-01 09:49:30 +02:00
|
|
|
|
2023-03-23 10:51:34 +01:00
|
|
|
import 'package:provider/provider.dart';
|
2023-03-29 15:14:27 +02:00
|
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
2023-12-22 20:14:36 +01:00
|
|
|
|
2023-03-17 09:41:08 +01:00
|
|
|
void main() {
|
2023-03-17 21:08:45 +01:00
|
|
|
WidgetsFlutterBinding.ensureInitialized();
|
2023-03-17 09:41:08 +01:00
|
|
|
runApp(const OutbagApp());
|
|
|
|
}
|
|
|
|
|
2023-03-28 14:54:39 +02:00
|
|
|
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
2023-12-22 20:14:36 +01:00
|
|
|
GlobalKey<NavigatorState>(debugLabel: 'root');
|
2023-03-28 14:54:39 +02:00
|
|
|
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
2023-12-22 20:14:36 +01:00
|
|
|
GlobalKey<NavigatorState>(debugLabel: 'user');
|
2023-03-28 14:54:39 +02:00
|
|
|
|
2023-03-17 09:41:08 +01:00
|
|
|
class OutbagApp extends StatefulWidget {
|
|
|
|
const OutbagApp({super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<StatefulWidget> createState() => _OutbagAppState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _OutbagAppState extends State {
|
2023-03-18 20:24:48 +01:00
|
|
|
// assume user is logged in
|
|
|
|
// unless not userdata is found
|
|
|
|
// or the userdata turns out to be wrong
|
2023-03-25 17:18:46 +01:00
|
|
|
User? user;
|
2023-03-17 09:41:08 +01:00
|
|
|
|
2023-03-25 14:29:28 +01:00
|
|
|
AppTheme theme = AppTheme.auto;
|
|
|
|
|
2023-03-29 18:02:00 +02:00
|
|
|
void loadTheme() async {
|
|
|
|
// load theme
|
|
|
|
try {
|
|
|
|
final theme = await AppTheme.fromDisk();
|
|
|
|
setState(() {
|
2023-12-22 20:14:36 +01:00
|
|
|
this.theme = theme;
|
2023-03-29 18:02:00 +02:00
|
|
|
});
|
|
|
|
} catch (_) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
void loadUser() async {
|
|
|
|
// load user
|
|
|
|
try {
|
|
|
|
final user = await User.fromDisk();
|
|
|
|
setState(() {
|
2023-12-22 20:14:36 +01:00
|
|
|
this.user = user;
|
2023-03-29 18:02:00 +02:00
|
|
|
});
|
|
|
|
} catch (_) {
|
|
|
|
// user unavailable
|
|
|
|
// invalid credentials
|
|
|
|
// log out
|
|
|
|
setState(() {
|
2023-12-22 20:14:36 +01:00
|
|
|
user = null;
|
2023-03-29 18:02:00 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-17 09:41:08 +01:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
|
2023-03-28 14:54:39 +02:00
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
2023-12-22 20:14:36 +01:00
|
|
|
loadTheme();
|
|
|
|
loadUser();
|
2023-03-25 17:18:46 +01:00
|
|
|
});
|
2023-03-24 16:33:37 +01:00
|
|
|
}
|
|
|
|
|
2023-03-29 18:23:46 +02:00
|
|
|
Future<AccountMeta?> fetchInfo(User user) async {
|
|
|
|
AccountMeta? info;
|
2023-03-23 10:19:14 +01:00
|
|
|
// try to obtain user account information
|
2023-03-17 09:41:08 +01:00
|
|
|
// with existing details
|
2023-03-23 10:19:14 +01:00
|
|
|
// NOTE: also functions as a way to verify ther data
|
2023-03-29 18:23:46 +02:00
|
|
|
await doNetworkRequest(null,
|
2023-12-22 20:14:36 +01:00
|
|
|
req: () => postWithCreadentials(
|
|
|
|
target: user.server,
|
|
|
|
path: 'getMyAccount',
|
|
|
|
credentials: user,
|
|
|
|
body: {}),
|
|
|
|
onOK: (body) async {
|
|
|
|
final i = AccountMeta.fromJSON(body['data']);
|
|
|
|
info = i;
|
|
|
|
},
|
|
|
|
onServerErr: (_) {
|
|
|
|
info = null;
|
2023-03-23 14:48:53 +01:00
|
|
|
|
2023-12-22 20:14:36 +01:00
|
|
|
setState(() {
|
2023-03-28 14:54:39 +02:00
|
|
|
this.user = null;
|
2023-12-22 20:14:36 +01:00
|
|
|
});
|
|
|
|
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;
|
2023-03-23 14:48:53 +01:00
|
|
|
});
|
2023-03-29 18:23:46 +02:00
|
|
|
|
|
|
|
return info;
|
2023-03-17 09:41:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2023-03-23 10:51:34 +01:00
|
|
|
return MultiProvider(
|
2023-12-22 20:14:36 +01:00
|
|
|
providers: [
|
|
|
|
Provider<AppTheme>.value(value: theme),
|
2023-03-29 15:14:27 +02:00
|
|
|
],
|
2023-12-22 20:14:36 +01:00
|
|
|
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 '/';
|
|
|
|
}
|
|
|
|
}
|
2023-03-29 18:23:46 +02:00
|
|
|
|
2023-12-22 20:14:36 +01:00
|
|
|
return null;
|
|
|
|
},
|
2023-04-01 20:13:03 +02:00
|
|
|
routes: <RouteBase>[
|
2023-12-22 20:14:36 +01:00
|
|
|
// unauthorized routes
|
2023-04-01 20:13:03 +02:00
|
|
|
GoRoute(
|
2023-12-22 20:14:36 +01:00
|
|
|
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),
|
|
|
|
),
|
2023-03-28 14:54:39 +02:00
|
|
|
]),
|
2023-04-05 19:04:18 +02:00
|
|
|
|
2023-12-22 20:14:36 +01:00
|
|
|
// 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(),
|
2023-04-04 20:28:26 +02:00
|
|
|
routes: [
|
|
|
|
GoRoute(
|
2023-12-22 20:14:36 +01:00
|
|
|
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) =>
|
|
|
|
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) => NewRoomPage(
|
|
|
|
server: state.params['server'] ?? '',
|
|
|
|
tag: 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'] ?? '')),
|
|
|
|
GoRoute(
|
|
|
|
name: 'new-category',
|
|
|
|
path: 'new-category',
|
|
|
|
builder: (context, state) =>
|
|
|
|
EditCategoryPage(
|
|
|
|
state.params['server'] ?? '',
|
|
|
|
state.params['id'] ?? '')),
|
|
|
|
GoRoute(
|
|
|
|
name: 'edit-category',
|
|
|
|
path: 'edit-category/:category',
|
|
|
|
builder: (context, state) =>
|
|
|
|
EditCategoryPage(
|
|
|
|
state.params['server'] ?? '',
|
|
|
|
state.params['id'] ?? '',
|
|
|
|
id: int.tryParse(
|
|
|
|
state.params['category'] ??
|
|
|
|
''))),
|
|
|
|
GoRoute(
|
|
|
|
name: 'new-product',
|
|
|
|
path: 'new-product',
|
|
|
|
builder: (context, state) =>
|
|
|
|
EditProductPage(
|
|
|
|
server:
|
|
|
|
state.params['server'] ?? '',
|
|
|
|
room: state.params['id'] ?? '',
|
|
|
|
)),
|
|
|
|
GoRoute(
|
|
|
|
name: 'view-product',
|
|
|
|
path: 'p/:product',
|
|
|
|
builder: (context, state) =>
|
|
|
|
ViewProductPage(
|
|
|
|
server:
|
|
|
|
state.params['server'] ?? '',
|
|
|
|
room: state.params['id'] ?? '',
|
|
|
|
product: int.tryParse(
|
|
|
|
state.params['product'] ??
|
|
|
|
'') ??
|
|
|
|
0),
|
|
|
|
routes: [
|
|
|
|
GoRoute(
|
|
|
|
name: 'edit-product',
|
|
|
|
path: 'edit',
|
|
|
|
builder: (context, state) =>
|
|
|
|
EditProductPage(
|
|
|
|
server: state
|
|
|
|
.params['server'] ??
|
|
|
|
'',
|
|
|
|
room: state.params['id'] ??
|
|
|
|
'',
|
|
|
|
product: int.tryParse(
|
|
|
|
state.params[
|
|
|
|
'product'] ??
|
|
|
|
''))),
|
|
|
|
]),
|
|
|
|
GoRoute(
|
|
|
|
name: 'new-item',
|
|
|
|
path: 'new-item',
|
2024-02-22 20:36:59 +01:00
|
|
|
builder: (context, state) => NewItemPage(
|
2023-12-22 20:14:36 +01:00
|
|
|
server:
|
|
|
|
state.params['server'] ?? '',
|
|
|
|
room: state.params['id'] ?? '',
|
|
|
|
)),
|
|
|
|
GoRoute(
|
|
|
|
name: 'edit-item',
|
|
|
|
path: 'i/:item',
|
|
|
|
builder: (context, state) => EditItemPage(
|
|
|
|
server: state.params['server'] ?? '',
|
|
|
|
room: state.params['id'] ?? '',
|
|
|
|
item: int.tryParse(
|
|
|
|
state.params['item'] ?? '') ??
|
|
|
|
0),
|
|
|
|
)
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
]),
|
2023-03-29 18:23:46 +02:00
|
|
|
|
2023-12-22 20:14:36 +01:00
|
|
|
// 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'))
|
|
|
|
]),
|
|
|
|
));
|
2023-03-17 09:41:08 +01:00
|
|
|
}
|
|
|
|
}
|