actions-test/lib/main.dart
Jakob Meier 384fbb0573
separate new item screen
The edit item screen might be overwhelming at first,
and if you only want to add simple items (by name) to the list,
it is way easier to simply type the name and click create to create a
simple item.
After creating the item the user will be redirected (history
replacement) to the edit screen, but clicking back will bring them back
to the list.
This screen also makes linking products easier and allows the user to
create new products if they notice they are using the same item multiple
times or can't be bothered to switch to the products tab
2024-02-22 20:36:59 +01:00

339 lines
14 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/backend/request.dart';
import 'package:outbag_app/screens/room/categories/edit.dart';
import 'package:outbag_app/screens/room/items/edit.dart';
import 'package:outbag_app/screens/room/items/new.dart';
import 'package:outbag_app/screens/room/products/edit.dart';
import 'package:outbag_app/screens/room/products/view.dart';
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';
import 'package:outbag_app/screens/room/join.dart';
import 'package:outbag_app/screens/room/main.dart';
import 'package:outbag_app/screens/room/about/members.dart';
import 'package:outbag_app/screens/room/about/permissions.dart';
import 'package:outbag_app/screens/settings/main.dart';
import 'package:provider/provider.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.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
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;
});
} catch (_) {
// user unavailable
// invalid credentials
// log out
setState(() {
user = null;
});
}
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
loadTheme();
loadUser();
});
}
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
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(() {
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 info;
}
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
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: 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) =>
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',
builder: (context, state) => NewItemPage(
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),
)
])
]),
]),
// 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'))
]),
));
}
}