diff --git a/lib/backend/permissions.dart b/lib/backend/permissions.dart index d5d6f5a..5e749c5 100644 --- a/lib/backend/permissions.dart +++ b/lib/backend/permissions.dart @@ -107,19 +107,19 @@ class RoomPermission { switch (permission) { case 1: - return trans!.roomPermissionAddItems; + return trans!.roomPermissionAddItems; case 2: - return trans!.roomPermissionRemoveItems; + return trans!.roomPermissionRemoveItems; case 4: - return trans!.roomPermissionEditContent; + return trans!.roomPermissionEditContent; case 8: - return trans!.roomPermissionChangeMeta; + return trans!.roomPermissionChangeMeta; case 16: - return trans!.roomPermissionManageOTA; + return trans!.roomPermissionManageOTA; case 32: - return trans!.roomPermissionManageAdmins; + return trans!.roomPermissionManageAdmins; case 64: - return trans!.roomPermissionManageMembers; + return trans!.roomPermissionManageMembers; } return trans!.roomPermissionUnknown; @@ -130,19 +130,19 @@ class RoomPermission { switch (permission) { case 1: - return trans!.roomPermissionAddItemsSubtitle; + return trans!.roomPermissionAddItemsSubtitle; case 2: - return trans!.roomPermissionRemoveItemsSubtitle; + return trans!.roomPermissionRemoveItemsSubtitle; case 4: - return trans!.roomPermissionEditContentSubtitle; + return trans!.roomPermissionEditContentSubtitle; case 8: - return trans!.roomPermissionChangeMetaSubtitle; + return trans!.roomPermissionChangeMetaSubtitle; case 16: - return trans!.roomPermissionManageOTASubtitle; + return trans!.roomPermissionManageOTASubtitle; case 32: - return trans!.roomPermissionManageAdminsSubtitle; + return trans!.roomPermissionManageAdminsSubtitle; case 64: - return trans!.roomPermissionManageMembersSubtitle; + return trans!.roomPermissionManageMembersSubtitle; } return trans!.roomPermissionUnknownSubtitle; diff --git a/lib/backend/request.dart b/lib/backend/request.dart index cb966ec..c462287 100644 --- a/lib/backend/request.dart +++ b/lib/backend/request.dart @@ -13,19 +13,19 @@ class Response { } Future usePostApi( - {required OutbagServer target, + {required OutbagServer target, String path = '', required Map headers, required Map body}) async { final resp = await http.post(Uri.parse('${target.base}api/$path'), - headers: headers, body: jsonEncode({'data': body})); + headers: headers, body: jsonEncode({'data': body})); final json = jsonDecode(resp.body); return Response( - body: json, res: resp.statusCode == 200 ? Result.ok : Result.err); + body: json, res: resp.statusCode == 200 ? Result.ok : Result.err); } Future postWithCreadentials( - {required OutbagServer target, + {required OutbagServer target, String path = '', required Map body, required User credentials}) async { @@ -33,14 +33,14 @@ Future postWithCreadentials( "Content-Type": "application/json", 'Connection': 'keep-alive', 'Authorization': - 'Digest name=${credentials.username} server=${target.tag} accountKey=${credentials.password}' + 'Digest name=${credentials.username} server=${target.tag} accountKey=${credentials.password}' }; return await usePostApi( - target: target, path: path, headers: headers, body: body); + target: target, path: path, headers: headers, body: body); } Future postWithToken( - {required OutbagServer target, + {required OutbagServer target, String path = '', required Map body, required String token}) async { @@ -49,16 +49,16 @@ Future postWithToken( 'Authorization': 'Bearer $token' }; return await usePostApi( - target: target, path: path, headers: headers, body: body); + target: target, path: path, headers: headers, body: body); } Future postUnauthorized( - {required OutbagServer target, + {required OutbagServer target, String path = '', required Map body}) async { Map headers = { "Content-Type": "application/json", }; return await usePostApi( - target: target, path: path, headers: headers, body: body); + target: target, path: path, headers: headers, body: body); } diff --git a/lib/backend/resolve_url.dart b/lib/backend/resolve_url.dart index 9c0d4a3..c2a2126 100644 --- a/lib/backend/resolve_url.dart +++ b/lib/backend/resolve_url.dart @@ -61,11 +61,10 @@ class OutbagServer { final SharedPreferences prefs = await SharedPreferences.getInstance(); return OutbagServer( - path: prefs.getString('server-path')!, - port: prefs.getInt('server-port')!, - tag: prefs.getString('server-tag')!, - host: prefs.getString('server-host')! - ); + path: prefs.getString('server-path')!, + port: prefs.getInt('server-port')!, + tag: prefs.getString('server-tag')!, + host: prefs.getString('server-host')!); } static Future removeDisk() async { diff --git a/lib/backend/room.dart b/lib/backend/room.dart index 112f92e..679bd67 100644 --- a/lib/backend/room.dart +++ b/lib/backend/room.dart @@ -322,11 +322,17 @@ class RoomMember { final bool isInvitePending; const RoomMember( - {required this.id, required this.serverTag, required this.isAdmin, this.isInvitePending=false}); + {required this.id, + required this.serverTag, + required this.isAdmin, + this.isInvitePending = false}); factory RoomMember.fromJSON(dynamic json) { return RoomMember( - id: json['name'], serverTag: json['server'], isAdmin: json['admin'], isInvitePending: json['confirmed']); + id: json['name'], + serverTag: json['server'], + isAdmin: json['admin'], + isInvitePending: json['confirmed']); } String get humanReadableName { diff --git a/lib/components/category_chip.dart b/lib/components/category_chip.dart index e878923..e966273 100644 --- a/lib/components/category_chip.dart +++ b/lib/components/category_chip.dart @@ -9,7 +9,8 @@ class CategoryChip extends StatelessWidget { @override Widget build(BuildContext context) { return ActionChip( - avatar: Icon(Icons.square_rounded, color: category?.color ?? RoomCategory.other(context).color), + avatar: Icon(Icons.square_rounded, + color: category?.color ?? RoomCategory.other(context).color), label: Text(category?.name ?? RoomCategory.other(context).name), ); } diff --git a/lib/components/category_picker.dart b/lib/components/category_picker.dart index b0a97bb..e7c008b 100644 --- a/lib/components/category_picker.dart +++ b/lib/components/category_picker.dart @@ -12,7 +12,7 @@ class CategoryPicker extends StatelessWidget { final String? label; const CategoryPicker( - {super.key, + {super.key, required this.categories, this.selected, this.onSelect, @@ -23,36 +23,34 @@ class CategoryPicker extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all(8), - child: DropdownButtonFormField( - hint: hint==null?null:Text(hint!), - decoration: InputDecoration( - label: label==null?null:Text(label!), - border: const OutlineInputBorder(), - prefixIcon: const Icon(Icons.category) - ), - value: selected, - items: [ - ...categories, - RoomCategory.other(context) - ].map((category)=>DropdownMenuItem( - value: category.id, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text(category.name), - Icon(Icons.square_rounded, - color:category.color, - size: 32) - ] - ), - )).toList(), - onChanged: enabled?(cid) { - if (onSelect != null) { - onSelect!(cid); - } - }:null, - )); + padding: const EdgeInsets.all(8), + child: DropdownButtonFormField( + hint: hint == null ? null : Text(hint!), + decoration: InputDecoration( + label: label == null ? null : Text(label!), + border: const OutlineInputBorder(), + prefixIcon: const Icon(Icons.category)), + value: selected, + items: [...categories, RoomCategory.other(context)] + .map((category) => DropdownMenuItem( + value: category.id, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(category.name), + Icon(Icons.square_rounded, + color: category.color, size: 32) + ]), + )) + .toList(), + onChanged: enabled + ? (cid) { + if (onSelect != null) { + onSelect!(cid); + } + } + : null, + )); } } diff --git a/lib/components/labeled_divider.dart b/lib/components/labeled_divider.dart index 9855e03..61611cd 100644 --- a/lib/components/labeled_divider.dart +++ b/lib/components/labeled_divider.dart @@ -6,14 +6,10 @@ class LabeledDivider extends StatelessWidget { @override Widget build(BuildContext context) { - return Row( - children: [ - const Expanded(child: Divider()), - Padding( - padding: const EdgeInsets.all(8), - child: Text(label)), - const Expanded(child: Divider()), - ] - ); + return Row(children: [ + const Expanded(child: Divider()), + Padding(padding: const EdgeInsets.all(8), child: Text(label)), + const Expanded(child: Divider()), + ]); } } diff --git a/lib/components/product_picker.dart b/lib/components/product_picker.dart index 3cd723a..854e994 100644 --- a/lib/components/product_picker.dart +++ b/lib/components/product_picker.dart @@ -14,7 +14,7 @@ class ProductPicker extends StatelessWidget { final String? help; const ProductPicker( - {super.key, + {super.key, required this.products, this.selected, this.onSelect, @@ -26,32 +26,32 @@ class ProductPicker extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all(8), - child: DropdownButtonFormField( - hint: hint == null ? null : Text(hint!), - decoration: InputDecoration( - label: label == null ? null : Text(label!), - border: const OutlineInputBorder(), - prefixIcon: const Icon(Icons.inventory_2), - helperText: help), - value: selected, - items: [ - // "no product" entry - DropdownMenuItem( - value: null, - child: Text(AppLocalizations.of(context)!.productNameNone), - ), - // other products - ...products.map((product) => DropdownMenuItem( - value: product.id, child: Text(product.name))) - ], - onChanged: enabled - ? (pid) { - if (onSelect != null) { - onSelect!(pid); - } - } - : null, - )); + padding: const EdgeInsets.all(8), + child: DropdownButtonFormField( + hint: hint == null ? null : Text(hint!), + decoration: InputDecoration( + label: label == null ? null : Text(label!), + border: const OutlineInputBorder(), + prefixIcon: const Icon(Icons.inventory_2), + helperText: help), + value: selected, + items: [ + // "no product" entry + DropdownMenuItem( + value: null, + child: Text(AppLocalizations.of(context)!.productNameNone), + ), + // other products + ...products.map((product) => DropdownMenuItem( + value: product.id, child: Text(product.name))) + ], + onChanged: enabled + ? (pid) { + if (onSelect != null) { + onSelect!(pid); + } + } + : null, + )); } } diff --git a/lib/components/room_icon_picker.dart b/lib/components/room_icon_picker.dart index e523189..85dca0e 100644 --- a/lib/components/room_icon_picker.dart +++ b/lib/components/room_icon_picker.dart @@ -39,12 +39,7 @@ class RoomIconPicker extends StatelessWidget { if (onSelect != null) { onSelect!(icon); } - } - ) - ); - }).toList() - ) - ) - ); + })); + }).toList()))); } } diff --git a/lib/main.dart b/lib/main.dart index a14791b..a6f161c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,15 +25,16 @@ 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 _rootNavigatorKey = -GlobalKey(debugLabel: 'root'); + GlobalKey(debugLabel: 'root'); final GlobalKey _userShellNavigatorKey = -GlobalKey(debugLabel: 'user'); + GlobalKey(debugLabel: 'user'); class OutbagApp extends StatefulWidget { const OutbagApp({super.key}); @@ -55,7 +56,7 @@ class _OutbagAppState extends State { try { final theme = await AppTheme.fromDisk(); setState(() { - this.theme = theme; + this.theme = theme; }); } catch (_) {} } @@ -65,14 +66,14 @@ class _OutbagAppState extends State { try { final user = await User.fromDisk(); setState(() { - this.user = user; + this.user = user; }); } catch (_) { // user unavailable // invalid credentials // log out setState(() { - user = null; + user = null; }); } } @@ -82,8 +83,8 @@ class _OutbagAppState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { - loadTheme(); - loadUser(); + loadTheme(); + loadUser(); }); } @@ -93,31 +94,31 @@ class _OutbagAppState extends State { // 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; + 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(() { + 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: () { - info = null; - // user is currently offline - // approve login, - // until user goes back offline - // NOTE TODO: check user data once online - return true; - }); return info; } @@ -125,195 +126,213 @@ class _OutbagAppState extends State { @override Widget build(BuildContext context) { return MultiProvider( - providers: [ - Provider.value(value: theme), - ], - child: MaterialApp.router( - title: "Outbag", - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - AppLocalizations.delegate + providers: [ + Provider.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: [ - // unauthorized routes - GoRoute( - name: 'welcome', - path: '/welcome', - builder: (context, state) => const WelcomePage(), + return null; + }, routes: [ + // 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: FutureProvider( - initialData: null, - child: child, - create: (context)=>fetchInfo(context.read()), - )), - routes: [ - 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: [ - GoRoute( - path: 'new', - name: 'new-room', - builder: (context, state) => NewRoomPage()), + name: 'welcome', + path: '/welcome', + builder: (context, state) => const WelcomePage(), + 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), + ), ]), - GoRoute( - name: 'room', - path: 'r/:server/:id', - builder: (context, state) => RoomPage( - state.params['server'] ?? '', - state.params['id'] ?? ''), - routes: [ - 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), + // authorized routes + ShellRoute( + navigatorKey: _userShellNavigatorKey, + builder: (context, state, child) => Provider.value( + value: user!, + child: FutureProvider( + initialData: null, + child: child, + create: (context) => fetchInfo(context.read()), + )), + routes: [ + GoRoute( + path: '/', + name: 'home', + builder: (context, state) => const HomePage(), 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'] ?? ''))), - ] - ), + 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: [ + 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: [ + 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) => EditItemPage( + 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), + ) + ]) + ]), + ]), - GoRoute( - name: 'new-item', - path: 'new-item', - builder: (context, state)=>EditItemPage( - 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')) - ]), - )); + // 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')) + ]), + )); } } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 0d01f9b..5eae005 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -24,12 +24,12 @@ class _HomePageState extends State { // wait for background room changes Room.listen((_) async { - try { - final newRooms = await Room.listRooms(); - setState(() { - rooms = newRooms; - }); - } catch (_) {} + try { + final newRooms = await Room.listRooms(); + setState(() { + rooms = newRooms; + }); + } catch (_) {} }); WidgetsBinding.instance.addPostFrameCallback((_) => fetchList()); @@ -43,17 +43,17 @@ class _HomePageState extends State { try { final newRooms = await Room.listRooms(); setState(() { - rooms = newRooms; + rooms = newRooms; }); } catch (_) {} doNetworkRequest( sm, req: () => postWithCreadentials( - path: 'listRooms', credentials: user, target: user.server, body: {}), + path: 'listRooms', credentials: user, target: user.server, body: {}), onOK: (body) async { final List list = body['data'].map((json) { - return Room.fromJSON(json); + return Room.fromJSON(json); }).toList(); for (Room r in list) { await r.toDisk(); @@ -91,33 +91,34 @@ class _HomePageState extends State { }, menuChildren: [ MenuItemButton( - leadingIcon: const Icon(Icons.settings), - child: Text(AppLocalizations.of(context)!.settings), - onPressed: () { - // show settings screen - context.goNamed('settings'); - }), - ...(context.watch() != null && - (context.watch()?.permissions)! & - ServerPermission.allManagement != - 0) - ? [ - MenuItemButton( - leadingIcon: const Icon(Icons.dns), - child: Text(AppLocalizations.of(context)!.serverDashboard), + leadingIcon: const Icon(Icons.settings), + child: Text(AppLocalizations.of(context)!.settings), onPressed: () { // show settings screen - context.goNamed('dash'); - }), - ] - : [], + context.goNamed('settings'); + }), + ...(context.watch() != null && + (context.watch()?.permissions)! & + ServerPermission.allManagement != + 0) + ? [ + MenuItemButton( + leadingIcon: const Icon(Icons.dns), + child: Text( + AppLocalizations.of(context)!.serverDashboard), + onPressed: () { + // show settings screen + context.goNamed('dash'); + }), + ] + : [], MenuItemButton( - leadingIcon: const Icon(Icons.info_rounded), - child: Text(AppLocalizations.of(context)!.about), - onPressed: () { - // show about screen - context.goNamed('about'); - }), + leadingIcon: const Icon(Icons.info_rounded), + child: Text(AppLocalizations.of(context)!.about), + onPressed: () { + // show about screen + context.goNamed('about'); + }), ], ) ], @@ -127,31 +128,31 @@ class _HomePageState extends State { itemBuilder: (ctx, i) { final room = rooms[i]; return Card( - margin: const EdgeInsets.all(8.0), - clipBehavior: Clip.antiAliasWithSaveLayer, - semanticContainer: true, - child: InkWell( - onTap: () { - // open room - context.goNamed('room', - params: {'server': room.serverTag, 'id': room.id}); - }, - onLongPress: () { - // open bottom sheet - // NOTE: feature yet to be confirmed - }, - child: Container( - padding: const EdgeInsets.fromLTRB(10, 5, 5, 10), - child: ListTile( - title: Text(room.name), - visualDensity: const VisualDensity(vertical: 3), - subtitle: Text(room.description), - leading: AspectRatio( - aspectRatio: 1 / 1, - child: SvgPicture.asset("${room.icon?.img}"), - ), - hoverColor: Colors.transparent, - )))); + margin: const EdgeInsets.all(8.0), + clipBehavior: Clip.antiAliasWithSaveLayer, + semanticContainer: true, + child: InkWell( + onTap: () { + // open room + context.goNamed('room', + params: {'server': room.serverTag, 'id': room.id}); + }, + onLongPress: () { + // open bottom sheet + // NOTE: feature yet to be confirmed + }, + child: Container( + padding: const EdgeInsets.fromLTRB(10, 5, 5, 10), + child: ListTile( + title: Text(room.name), + visualDensity: const VisualDensity(vertical: 3), + subtitle: Text(room.description), + leading: AspectRatio( + aspectRatio: 1 / 1, + child: SvgPicture.asset("${room.icon?.img}"), + ), + hoverColor: Colors.transparent, + )))); }, ), floatingActionButton: FloatingActionButton.extended( diff --git a/lib/screens/room/about/invite.dart b/lib/screens/room/about/invite.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/screens/room/about/invite.dart @@ -0,0 +1 @@ + diff --git a/lib/screens/room/about/members.dart b/lib/screens/room/about/members.dart index 82745c6..91f8144 100644 --- a/lib/screens/room/about/members.dart +++ b/lib/screens/room/about/members.dart @@ -30,10 +30,10 @@ class _ManageRoomMembersPageState extends State { doNetworkRequest( sm, req: () => postWithCreadentials( - path: 'getRoomInfo', - credentials: user, - target: user.server, - body: {'room': widget.tag, 'server': widget.server}), + path: 'getRoomInfo', + credentials: user, + target: user.server, + body: {'room': widget.tag, 'server': widget.server}), onAnyErr: () { // user should not be here // close screen @@ -43,7 +43,7 @@ class _ManageRoomMembersPageState extends State { onOK: (body) async { final info = RoomInfo.fromJSON(body['data']); setState(() { - this.info = info; + this.info = info; }); return true; }, @@ -56,26 +56,26 @@ class _ManageRoomMembersPageState extends State { final user = context.read(); doNetworkRequest(sm, - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getRoomMembers', - body: {'room': widget.tag, 'server': widget.server}), - onAnyErr: () { - // user should not be here - // close screen - router.pushReplacementNamed('home'); - return false; - }, - onOK: (body) { - final List list = body['data'].map((json) { + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getRoomMembers', + body: {'room': widget.tag, 'server': widget.server}), + onAnyErr: () { + // user should not be here + // close screen + router.pushReplacementNamed('home'); + return false; + }, + onOK: (body) { + final List list = body['data'].map((json) { return RoomMember.fromJSON(json); - }).toList(); + }).toList(); - setState(() { + setState(() { this.list = list; + }); }); - }); } @override @@ -83,21 +83,21 @@ class _ManageRoomMembersPageState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - fetchUserInfo(); - fetchMembers(); + fetchUserInfo(); + fetchMembers(); }); } @override Widget build(BuildContext context) { final textTheme = Theme.of(context) - .textTheme - .apply(displayColor: Theme.of(context).colorScheme.onSurface); + .textTheme + .apply(displayColor: Theme.of(context).colorScheme.onSurface); return Scaffold( appBar: AppBar( title: - Text(AppLocalizations.of(context)!.roomMembersTitle(list.length)), + Text(AppLocalizations.of(context)!.roomMembersTitle(list.length)), //actions: [ // // NOTE: Maybe add a search icon // // and general search functionality here @@ -109,8 +109,8 @@ class _ManageRoomMembersPageState extends State { String role = AppLocalizations.of(context)!.roleMember; if (info != null && - (info?.owner)! == item.id && - widget.server == item.serverTag) { + (info?.owner)! == item.id && + widget.server == item.serverTag) { role = AppLocalizations.of(context)!.roleOwner; } else if (item.isAdmin) { role = AppLocalizations.of(context)!.roleAdmin; @@ -119,17 +119,17 @@ class _ManageRoomMembersPageState extends State { bool enable = true; // perform permission check if (info == null || - !((info?.isAdmin)! || - (info?.isOwner)! || - ((info?.permissions)! & oB("1100000") != 0))) { + !((info?.isAdmin)! || + (info?.isOwner)! || + ((info?.permissions)! & oB("1100000") != 0))) { // NOTE: do not show error message // user should assume, // that it wasn't even possible // to click on ListTile enable = false; } else if (info != null && - item.id == info?.owner && - widget.server == item.serverTag) { + item.id == info?.owner && + widget.server == item.serverTag) { // cannot kick admin enable = false; } @@ -139,236 +139,236 @@ class _ManageRoomMembersPageState extends State { subtitle: Text(role), leading: const Icon(Icons.person), onTap: !enable - ? null - : () { - showModalBottomSheet( - context: context, - builder: (context) => BottomSheet( - onClosing: () {}, - builder: (context) => Column( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: Text(item.humanReadableName, - style: textTheme.displaySmall)), - Padding( - padding: const EdgeInsets.all(8), - child: Column( - children: [ - ...((info?.isAdmin)! || - (info?.isOwner)! || - ((info?.permissions)! & - RoomPermission - .changeAdmin != - 0)) - ? [ - ListTile( - leading: const Icon( - Icons.supervisor_account), - title: Text(item.isAdmin - ? AppLocalizations.of( - context)! - .removeAdminTitle - : AppLocalizations.of( - context)! - .makeAdminTitle), - subtitle: Text(item.isAdmin - ? AppLocalizations.of( - context)! - .removeAdminSubtitle - : AppLocalizations.of( - context)! - .makeAdminSubtitle), - onTap: () { - // make user admin - showDialog( - context: context, - builder: - (ctx) => - AlertDialog( - icon: const Icon( - Icons - .supervisor_account), - title: Text(item.isAdmin - ? AppLocalizations.of( - context)! - .removeAdminTitle - : AppLocalizations.of( - context)! - .makeAdminTitle), - content: Text(item - .isAdmin - ? AppLocalizations.of( - context)! - .removeAdminConfirm(item - .humanReadableName) - : AppLocalizations.of( - context)! - .makeAdminConfirm( - item.humanReadableName)), - actions: [ - TextButton( - onPressed: - () { - // close popup - // NOTE: cancel only closes the dialog - // whilst OK closes both - Navigator.of(ctx) - .pop(); - }, - child: Text( - AppLocalizations.of(context)! - .cancel), - ), - FilledButton( - onPressed: - () async { - // send request - final scaffMgr = - ScaffoldMessenger.of(context); - final nav = - Navigator.of(ctx); - final nav2 = - Navigator.of(context); - final user = - context.read(); + ? null + : () { + showModalBottomSheet( + context: context, + builder: (context) => BottomSheet( + onClosing: () {}, + builder: (context) => Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Text(item.humanReadableName, + style: textTheme.displaySmall)), + Padding( + padding: const EdgeInsets.all(8), + child: Column( + children: [ + ...((info?.isAdmin)! || + (info?.isOwner)! || + ((info?.permissions)! & + RoomPermission + .changeAdmin != + 0)) + ? [ + ListTile( + leading: const Icon( + Icons.supervisor_account), + title: Text(item.isAdmin + ? AppLocalizations.of( + context)! + .removeAdminTitle + : AppLocalizations.of( + context)! + .makeAdminTitle), + subtitle: Text(item.isAdmin + ? AppLocalizations.of( + context)! + .removeAdminSubtitle + : AppLocalizations.of( + context)! + .makeAdminSubtitle), + onTap: () { + // make user admin + showDialog( + context: context, + builder: + (ctx) => + AlertDialog( + icon: const Icon( + Icons + .supervisor_account), + title: Text(item.isAdmin + ? AppLocalizations.of( + context)! + .removeAdminTitle + : AppLocalizations.of( + context)! + .makeAdminTitle), + content: Text(item + .isAdmin + ? AppLocalizations.of( + context)! + .removeAdminConfirm(item + .humanReadableName) + : AppLocalizations.of( + context)! + .makeAdminConfirm( + item.humanReadableName)), + actions: [ + TextButton( + onPressed: + () { + // close popup + // NOTE: cancel only closes the dialog + // whilst OK closes both + Navigator.of(ctx) + .pop(); + }, + child: Text( + AppLocalizations.of(context)! + .cancel), + ), + FilledButton( + onPressed: + () async { + // send request + final scaffMgr = + ScaffoldMessenger.of(context); + final nav = + Navigator.of(ctx); + final nav2 = + Navigator.of(context); + final user = + context.read(); - doNetworkRequest( - scaffMgr, - req: () => - postWithCreadentials(path: 'setAdminStatus', credentials: user, target: user.server, body: { - 'room': widget.tag, - 'roomServer': widget.server, - 'server': item.serverTag, - 'name': item.id, - 'admin': !item.isAdmin - }), - onOK: (_) { - fetchMembers(); - }, - after: () { - // close popup - nav.pop(); - // close bottom sheet - nav2.pop(); - }); - }, - child: Text( - AppLocalizations.of(context)! - .ok), - ) + doNetworkRequest( + scaffMgr, + req: () => + postWithCreadentials(path: 'setAdminStatus', credentials: user, target: user.server, body: { + 'room': widget.tag, + 'roomServer': widget.server, + 'server': item.serverTag, + 'name': item.id, + 'admin': !item.isAdmin + }), + onOK: (_) { + fetchMembers(); + }, + after: () { + // close popup + nav.pop(); + // close bottom sheet + nav2.pop(); + }); + }, + child: Text( + AppLocalizations.of(context)! + .ok), + ) + ], + )); + }, + ) + ] + : [], + ...((info?.isAdmin)! || + (info?.isOwner)! || + ((info?.permissions)! & + RoomPermission + .manageMembers != + 0)) + ? [ + ListTile( + leading: const Icon( + Icons.person_remove), + title: Text( + AppLocalizations.of( + context)! + .kickUserTitle), + subtitle: Text( + AppLocalizations.of( + context)! + .kickUserSubtitle), + onTap: () { + // remove user from room + showDialog( + context: context, + builder: + (ctx) => + AlertDialog( + icon: const Icon( + Icons + .person_remove), + title: Text(AppLocalizations.of( + context)! + .kickUserTitle), + content: Text(AppLocalizations.of( + context)! + .kichUserConfirm( + item.humanReadableName)), + actions: [ + TextButton( + onPressed: + () { + // close popup + // NOTE: cancel only closes the dialog + // whilst OK closes both + + Navigator.of(ctx) + .pop(); + }, + child: Text( + AppLocalizations.of(context)! + .cancel), + ), + FilledButton( + onPressed: + () async { + // send request + final scaffMgr = + ScaffoldMessenger.of(ctx); + final nav = + Navigator.of(ctx); + final nav2 = + Navigator.of(context); + final user = + context.read(); + + doNetworkRequest( + scaffMgr, + req: () => + postWithCreadentials(path: 'kickMember', credentials: user, target: user.server, body: { + 'room': widget.tag, + 'roomServer': widget.server, + 'name': item.id, + 'server': item.serverTag + }), + onOK: (_) { + fetchMembers(); + }, + after: () { + // close popup + nav.pop(); + // close bottom sheet + nav2.pop(); + }); + }, + child: Text( + AppLocalizations.of(context)! + .ok), + ) + ], + )); + }, + ) + ] + : [], ], - )); - }, - ) - ] - : [], - ...((info?.isAdmin)! || - (info?.isOwner)! || - ((info?.permissions)! & - RoomPermission - .manageMembers != - 0)) - ? [ - ListTile( - leading: const Icon( - Icons.person_remove), - title: Text( - AppLocalizations.of( - context)! - .kickUserTitle), - subtitle: Text( - AppLocalizations.of( - context)! - .kickUserSubtitle), - onTap: () { - // remove user from room - showDialog( - context: context, - builder: - (ctx) => - AlertDialog( - icon: const Icon( - Icons - .person_remove), - title: Text(AppLocalizations.of( - context)! - .kickUserTitle), - content: Text(AppLocalizations.of( - context)! - .kichUserConfirm( - item.humanReadableName)), - actions: [ - TextButton( - onPressed: - () { - // close popup - // NOTE: cancel only closes the dialog - // whilst OK closes both - - Navigator.of(ctx) - .pop(); - }, - child: Text( - AppLocalizations.of(context)! - .cancel), - ), - FilledButton( - onPressed: - () async { - // send request - final scaffMgr = - ScaffoldMessenger.of(ctx); - final nav = - Navigator.of(ctx); - final nav2 = - Navigator.of(context); - final user = - context.read(); - - doNetworkRequest( - scaffMgr, - req: () => - postWithCreadentials(path: 'kickMember', credentials: user, target: user.server, body: { - 'room': widget.tag, - 'roomServer': widget.server, - 'name': item.id, - 'server': item.serverTag - }), - onOK: (_) { - fetchMembers(); - }, - after: () { - // close popup - nav.pop(); - // close bottom sheet - nav2.pop(); - }); - }, - child: Text( - AppLocalizations.of(context)! - .ok), - ) - ], - )); - }, - ) - ] - : [], - ], - ), - ), - FilledButton( - child: Text( - AppLocalizations.of(context)!.close), - onPressed: () { - Navigator.of(context).pop(); - }, - ) - ], - ), - )); - }, + ), + ), + FilledButton( + child: Text( + AppLocalizations.of(context)!.close), + onPressed: () { + Navigator.of(context).pop(); + }, + ) + ], + ), + )); + }, ); }, itemCount: list.length, diff --git a/lib/screens/room/about/ota.dart b/lib/screens/room/about/ota.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/screens/room/about/ota.dart @@ -0,0 +1 @@ + diff --git a/lib/screens/room/about/permissions.dart b/lib/screens/room/about/permissions.dart index 94558a6..b7af026 100644 --- a/lib/screens/room/about/permissions.dart +++ b/lib/screens/room/about/permissions.dart @@ -31,10 +31,10 @@ class _EditRoomPermissionSetPageState extends State { doNetworkRequest( sm, req: () => postWithCreadentials( - path: 'getRoomInfo', - credentials: user, - target: user.server, - body: {'room': widget.tag, 'server': widget.server}), + path: 'getRoomInfo', + credentials: user, + target: user.server, + body: {'room': widget.tag, 'server': widget.server}), onAnyErr: () { // user should not be here // close screen @@ -44,7 +44,7 @@ class _EditRoomPermissionSetPageState extends State { onOK: (body) async { final info = RoomInfo.fromJSON(body['data']); setState(() { - permissions = info.permissions; + permissions = info.permissions; }); return true; }, @@ -72,18 +72,18 @@ class _EditRoomPermissionSetPageState extends State { final int col = pow(2, index) as int; return SwitchListTile( - title: Text(RoomPermission.name(item, context)), - subtitle: Text(RoomPermission.describe(item, context)), - onChanged: (state) { - setState(() { + title: Text(RoomPermission.name(item, context)), + subtitle: Text(RoomPermission.describe(item, context)), + onChanged: (state) { + setState(() { if (state) { permissions |= col; } else { permissions &= ~col; } - }); - }, - value: permissions & col != 0); + }); + }, + value: permissions & col != 0); }, ), floatingActionButton: FloatingActionButton.extended( @@ -97,18 +97,18 @@ class _EditRoomPermissionSetPageState extends State { // update permissions doNetworkRequest(sm, - req: () => postWithCreadentials( - path: 'setRoomRight', - credentials: user, - target: user.server, - body: { - 'room': widget.tag, - 'server': widget.server, - 'rights': permissions - }), - onOK: (_) { - router.pop(); - }); + req: () => postWithCreadentials( + path: 'setRoomRight', + credentials: user, + target: user.server, + body: { + 'room': widget.tag, + 'server': widget.server, + 'rights': permissions + }), + onOK: (_) { + router.pop(); + }); }, ), ); diff --git a/lib/screens/room/categories/edit.dart b/lib/screens/room/categories/edit.dart index ede33f3..8ee05c2 100644 --- a/lib/screens/room/categories/edit.dart +++ b/lib/screens/room/categories/edit.dart @@ -31,11 +31,11 @@ class _EditCategoryPageState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - if (widget.id == null) { - // trying to create a new category - return; - } - fetchCategory(); + if (widget.id == null) { + // trying to create a new category + return; + } + fetchCategory(); }); } @@ -45,210 +45,210 @@ class _EditCategoryPageState extends State { // TODO: load cached rooms doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getCategory', - body: { - 'room': widget.tag, - 'server': widget.server, - 'listCatID': widget.id - }), - onOK: (json) { - setState(() { + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getCategory', + body: { + 'room': widget.tag, + 'server': widget.server, + 'listCatID': widget.id + }), + onOK: (json) { + setState(() { _ctrName.text = json['data']['title']; _ctrColor = colorFromString(json['data']['color']); + }); }); - }); } @override Widget build(BuildContext context) { final textTheme = Theme.of(context) - .textTheme - .apply(displayColor: Theme.of(context).colorScheme.onSurface); + .textTheme + .apply(displayColor: Theme.of(context).colorScheme.onSurface); double width = MediaQuery.of(context).size.width; double height = MediaQuery.of(context).size.height; double smallest = min(min(width, height), 400); return showSpinner - ? Scaffold( - body: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CircularProgressIndicator(), - Text(AppLocalizations.of(context)!.loading, - style: textTheme.titleLarge), - ]))) - : Scaffold( - appBar: AppBar( - title: Text((widget.id == null) - ? AppLocalizations.of(context)!.newCategory - : AppLocalizations.of(context)!.editCategory), - ), - body: SingleChildScrollView( - child: Center( - child: Padding( - padding: const EdgeInsets.all(14), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 400), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - IconButton( - icon: Icon(Icons.square_rounded, - size: 48.0, color: _ctrColor), - tooltip: AppLocalizations.of(context)! - .changeCategoryColor, - onPressed: () { - showDialog( - context: context, - builder: (ctx) => AlertDialog( - title: Text( - AppLocalizations.of(context)! - .chooseCategoryColor), - actions: const [], - content: SizedBox( - width: smallest * 0.3 * 3, - height: smallest * 0.3 * 3, - child: GridView.count( - crossAxisCount: 3, - children: RoomCategory - .listColors() - .map((color) { - return GridTile( - child: IconButton( - icon: Icon( - Icons - .square_rounded, - color: - color, - size: 48.0), - // do not display tooltip for now - // as it is hard to translate - // and the tooltip prevented the click event, - // when clicked on the tooltip bar - // tooltip:icon.text, - onPressed: () { - setState(() { - _ctrColor = - color; - }); - Navigator.of( - ctx) - .pop(); - })); - }).toList())), - )); - }, - ), - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrName, - keyboardType: TextInputType.name, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.badge), - labelText: AppLocalizations.of(context)! - .inputCategoryNameLabel, - hintText: AppLocalizations.of(context)! - .inputCategoryNameHint, - helperText: AppLocalizations.of(context)! - .inputCategoryNameHelp, - border: const OutlineInputBorder(), - ), - ), - ), - ], - ))))), - floatingActionButton: FloatingActionButton.extended( - onPressed: () async { - final scaffMgr = ScaffoldMessenger.of(context); - final router = GoRouter.of(context); - final trans = AppLocalizations.of(context); + ? Scaffold( + body: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CircularProgressIndicator(), + Text(AppLocalizations.of(context)!.loading, + style: textTheme.titleLarge), + ]))) + : Scaffold( + appBar: AppBar( + title: Text((widget.id == null) + ? AppLocalizations.of(context)!.newCategory + : AppLocalizations.of(context)!.editCategory), + ), + body: SingleChildScrollView( + child: Center( + child: Padding( + padding: const EdgeInsets.all(14), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + icon: Icon(Icons.square_rounded, + size: 48.0, color: _ctrColor), + tooltip: AppLocalizations.of(context)! + .changeCategoryColor, + onPressed: () { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text( + AppLocalizations.of(context)! + .chooseCategoryColor), + actions: const [], + content: SizedBox( + width: smallest * 0.3 * 3, + height: smallest * 0.3 * 3, + child: GridView.count( + crossAxisCount: 3, + children: RoomCategory + .listColors() + .map((color) { + return GridTile( + child: IconButton( + icon: Icon( + Icons + .square_rounded, + color: + color, + size: 48.0), + // do not display tooltip for now + // as it is hard to translate + // and the tooltip prevented the click event, + // when clicked on the tooltip bar + // tooltip:icon.text, + onPressed: () { + setState(() { + _ctrColor = + color; + }); + Navigator.of( + ctx) + .pop(); + })); + }).toList())), + )); + }, + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrName, + keyboardType: TextInputType.name, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.badge), + labelText: AppLocalizations.of(context)! + .inputCategoryNameLabel, + hintText: AppLocalizations.of(context)! + .inputCategoryNameHint, + helperText: AppLocalizations.of(context)! + .inputCategoryNameHelp, + border: const OutlineInputBorder(), + ), + ), + ), + ], + ))))), + floatingActionButton: FloatingActionButton.extended( + onPressed: () async { + final scaffMgr = ScaffoldMessenger.of(context); + final router = GoRouter.of(context); + final trans = AppLocalizations.of(context); - // name may not be empty - if (_ctrName.text.isEmpty) { - showSimpleSnackbar(scaffMgr, - text: trans!.errorNoRoomName, action: trans.ok); + // name may not be empty + if (_ctrName.text.isEmpty) { + showSimpleSnackbar(scaffMgr, + text: trans!.errorNoRoomName, action: trans.ok); - return; - } + return; + } - setState(() { - showSpinner = true; - }); + setState(() { + showSpinner = true; + }); - final user = context.read(); - final color = colorIdFromColor(_ctrColor); + final user = context.read(); + final color = colorIdFromColor(_ctrColor); - if (widget.id == null) { - await doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - target: user.server, - credentials: user, - path: 'addCategory', - body: { - 'room': widget.tag, - 'server': widget.server, - 'title': _ctrName.text, - 'color': color - }), - onOK: (body) async { - final id = body['data']['catID']; + if (widget.id == null) { + await doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + target: user.server, + credentials: user, + path: 'addCategory', + body: { + 'room': widget.tag, + 'server': widget.server, + 'title': _ctrName.text, + 'color': color + }), + onOK: (body) async { + final id = body['data']['catID']; - final cat = RoomCategory( - id: id, name: _ctrName.text, color: _ctrColor); - // TODO: cache category + final cat = RoomCategory( + id: id, name: _ctrName.text, color: _ctrColor); + // TODO: cache category - // go back - router.pop(); - return; - }, - after: () { - setState(() { - showSpinner = false; - }); - }); - } else { - await doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - target: user.server, - credentials: user, - path: 'changeCategory', - body: { - 'room': widget.tag, - 'server': widget.server, - 'title': _ctrName.text, - 'listCatID': widget.id, - 'color': color - }), - onOK: (body) async { - final cat = RoomCategory( - id: widget.id!, - name: _ctrName.text, - color: _ctrColor); - // TODO: cache category + // go back + router.pop(); + return; + }, + after: () { + setState(() { + showSpinner = false; + }); + }); + } else { + await doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + target: user.server, + credentials: user, + path: 'changeCategory', + body: { + 'room': widget.tag, + 'server': widget.server, + 'title': _ctrName.text, + 'listCatID': widget.id, + 'color': color + }), + onOK: (body) async { + final cat = RoomCategory( + id: widget.id!, + name: _ctrName.text, + color: _ctrColor); + // TODO: cache category - // go back - router.pop(); - return; - }, - after: () { - setState(() { - showSpinner = false; - }); - }); - } - }, - label: Text((widget.id == null) - ? AppLocalizations.of(context)!.newCategoryShort - : AppLocalizations.of(context)!.editCategoryShort), - icon: Icon((widget.id == null) ? Icons.add : Icons.edit)), - ); + // go back + router.pop(); + return; + }, + after: () { + setState(() { + showSpinner = false; + }); + }); + } + }, + label: Text((widget.id == null) + ? AppLocalizations.of(context)!.newCategoryShort + : AppLocalizations.of(context)!.editCategoryShort), + icon: Icon((widget.id == null) ? Icons.add : Icons.edit)), + ); } } diff --git a/lib/screens/room/items/edit.dart b/lib/screens/room/items/edit.dart index 15fa1b3..d95c857 100644 --- a/lib/screens/room/items/edit.dart +++ b/lib/screens/room/items/edit.dart @@ -17,7 +17,7 @@ class EditItemPage extends StatefulWidget { final int? item; const EditItemPage( - {super.key, required this.room, required this.server, this.item}); + {super.key, required this.room, required this.server, this.item}); @override State createState() => _EditItemPageState(); @@ -43,20 +43,20 @@ class _EditItemPageState extends State { // TODO: load cached categories first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getCategories', - body: {'room': widget.room, 'server': widget.server}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomCategory.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getCategories', + body: {'room': widget.room, 'server': widget.server}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomCategory.fromJSON(raw)) + .toList(); - setState(() { + setState(() { categories = resp; + }); }); - }); } void fetchProducts() { @@ -65,20 +65,20 @@ class _EditItemPageState extends State { // TODO: load cached products first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getProducts', - body: {'room': widget.room, 'server': widget.server}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomProduct.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getProducts', + body: {'room': widget.room, 'server': widget.server}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomProduct.fromJSON(raw)) + .toList(); - setState(() { + setState(() { products = resp; + }); }); - }); } void fetchItem() { @@ -87,21 +87,21 @@ class _EditItemPageState extends State { // TODO: load cached item first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getItem', - body: { - 'room': widget.room, - 'server': widget.server, - 'listItemID': widget.item - }), - onOK: (body) async { - final resp = RoomItem.fromJSON(body['data']); - setState(() { + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getItem', + body: { + 'room': widget.room, + 'server': widget.server, + 'listItemID': widget.item + }), + onOK: (body) async { + final resp = RoomItem.fromJSON(body['data']); + setState(() { item = resp; + }); }); - }); } @override @@ -109,12 +109,12 @@ class _EditItemPageState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - fetchCategories(); - fetchProducts(); + fetchCategories(); + fetchProducts(); - if (widget.item != null) { - fetchItem(); - } + if (widget.item != null) { + fetchItem(); + } }); } @@ -123,156 +123,156 @@ class _EditItemPageState extends State { return Scaffold( appBar: AppBar( title: Text((widget.item == null) - ? AppLocalizations.of(context)!.createItem - : AppLocalizations.of(context)!.editItem), + ? AppLocalizations.of(context)!.createItem + : AppLocalizations.of(context)!.editItem), ), body: SingleChildScrollView( - child: Center( - child: Padding( - padding: const EdgeInsets.all(14), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 400), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrName, - keyboardType: TextInputType.name, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.badge), - labelText: AppLocalizations.of(context)! - .inputItemNameLabel, - hintText: AppLocalizations.of(context)! - .inputItemNameHint, - helperText: AppLocalizations.of(context)! - .inputItemNameHelp, - border: const OutlineInputBorder(), - ), - ), - ), - ProductPicker( - label: AppLocalizations.of(context)! - .selectLinkedProductLabel, - hint: AppLocalizations.of(context)! - .selectLinkedProductHint, - products: products, - selected: _ctrLink, - onSelect: (pid) { - setState(() { - _ctrLink = pid; - }); - }, - ), - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrDescription, - keyboardType: TextInputType.text, - decoration: InputDecoration( - labelText: AppLocalizations.of(context)! - .inputItemDescriptionLabel, - hintText: AppLocalizations.of(context)! - .inputItemDescriptionHint, - helperText: AppLocalizations.of(context)! - .inputItemDescriptionHelp, - prefixIcon: const Icon(Icons.dns), - border: const OutlineInputBorder(), - ), - ), - ), - DynamicValueUnitInput( - initialUnit: _ctrUnit, - initialValue: _ctrValue, - onUnitChange: (unit) { - setState(() { - _ctrUnit = unit; - }); - }, - onValueChange: (value) { - setState(() { - _ctrValue = value; - }); - }, - ), - CategoryPicker( - label: AppLocalizations.of(context)! - .selectCategoryLabel, - hint: AppLocalizations.of(context)! - .selectCategoryHint, - categories: categories, - selected: _ctrCategory, - onSelect: (cid) { - setState(() { - _ctrCategory = cid; - }); - }, - ), - ], - ))))), + child: Center( + child: Padding( + padding: const EdgeInsets.all(14), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrName, + keyboardType: TextInputType.name, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.badge), + labelText: AppLocalizations.of(context)! + .inputItemNameLabel, + hintText: AppLocalizations.of(context)! + .inputItemNameHint, + helperText: AppLocalizations.of(context)! + .inputItemNameHelp, + border: const OutlineInputBorder(), + ), + ), + ), + ProductPicker( + label: AppLocalizations.of(context)! + .selectLinkedProductLabel, + hint: AppLocalizations.of(context)! + .selectLinkedProductHint, + products: products, + selected: _ctrLink, + onSelect: (pid) { + setState(() { + _ctrLink = pid; + }); + }, + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrDescription, + keyboardType: TextInputType.text, + decoration: InputDecoration( + labelText: AppLocalizations.of(context)! + .inputItemDescriptionLabel, + hintText: AppLocalizations.of(context)! + .inputItemDescriptionHint, + helperText: AppLocalizations.of(context)! + .inputItemDescriptionHelp, + prefixIcon: const Icon(Icons.dns), + border: const OutlineInputBorder(), + ), + ), + ), + DynamicValueUnitInput( + initialUnit: _ctrUnit, + initialValue: _ctrValue, + onUnitChange: (unit) { + setState(() { + _ctrUnit = unit; + }); + }, + onValueChange: (value) { + setState(() { + _ctrValue = value; + }); + }, + ), + CategoryPicker( + label: AppLocalizations.of(context)! + .selectCategoryLabel, + hint: AppLocalizations.of(context)! + .selectCategoryHint, + categories: categories, + selected: _ctrCategory, + onSelect: (cid) { + setState(() { + _ctrCategory = cid; + }); + }, + ), + ], + ))))), floatingActionButton: FloatingActionButton.extended( - onPressed: () async { - final scaffMgr = ScaffoldMessenger.of(context); - final trans = AppLocalizations.of(context); - final nav = Navigator.of(context); + onPressed: () async { + final scaffMgr = ScaffoldMessenger.of(context); + final trans = AppLocalizations.of(context); + final nav = Navigator.of(context); - if (_ctrName.text.isEmpty) { - showSimpleSnackbar(scaffMgr, - text: trans!.errorProductNameShouldNotBeEmpty, - action: trans.ok); - return; - } + if (_ctrName.text.isEmpty) { + showSimpleSnackbar(scaffMgr, + text: trans!.errorProductNameShouldNotBeEmpty, + action: trans.ok); + return; + } - final user = context.read(); + final user = context.read(); - if (widget.item == null) { - doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'addItem', - body: { - 'room': widget.room, - 'server': widget.server, - 'state': 0, - 'title': _ctrName.text, - 'description': _ctrDescription.text, - 'listCatID': _ctrCategory, - 'unit': _ctrUnit, - 'value': _ctrValue, - 'listProdID': _ctrLink - }), - onOK: (_) async { - nav.pop(); - }); - } else { - doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'changeItem', - body: { - 'listItemID': widget.item, - 'room': widget.room, - 'server': widget.server, - 'title': _ctrName.text, - 'description': _ctrDescription.text, - 'listCatID': _ctrCategory, - 'defUnit': _ctrUnit, - 'defValue': _ctrValue, - 'listProdID': _ctrLink - }), - onOK: (_) async { - nav.pop(); - }); - } - }, - label: Text(widget.item != null - ? AppLocalizations.of(context)!.editItemShort - : AppLocalizations.of(context)!.createItemShort), - icon: Icon(widget.item != null ? Icons.edit : Icons.add)), + if (widget.item == null) { + doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'addItem', + body: { + 'room': widget.room, + 'server': widget.server, + 'state': 0, + 'title': _ctrName.text, + 'description': _ctrDescription.text, + 'listCatID': _ctrCategory, + 'unit': _ctrUnit, + 'value': _ctrValue, + 'listProdID': _ctrLink + }), + onOK: (_) async { + nav.pop(); + }); + } else { + doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'changeItem', + body: { + 'listItemID': widget.item, + 'room': widget.room, + 'server': widget.server, + 'title': _ctrName.text, + 'description': _ctrDescription.text, + 'listCatID': _ctrCategory, + 'defUnit': _ctrUnit, + 'defValue': _ctrValue, + 'listProdID': _ctrLink + }), + onOK: (_) async { + nav.pop(); + }); + } + }, + label: Text(widget.item != null + ? AppLocalizations.of(context)!.editItemShort + : AppLocalizations.of(context)!.createItemShort), + icon: Icon(widget.item != null ? Icons.edit : Icons.add)), ); } } diff --git a/lib/screens/room/join.dart b/lib/screens/room/join.dart index 73d994b..80b0970 100644 --- a/lib/screens/room/join.dart +++ b/lib/screens/room/join.dart @@ -24,60 +24,60 @@ class _JoinRoomPageState extends State { final user = context.read(); doNetworkRequest(null, - req: () => postWithCreadentials( - path: 'listPublicRooms', - credentials: user, - target: user.server, - body: {}), - onOK: (body) async { - // parse rooms - final list = body['data']; - - // try to fetch a list of rooms the user is a member of - // use an empty blacklist when request is not successful - final List blacklist = []; - await doNetworkRequest(sm, - req: () => postWithCreadentials( - path: 'listRooms', + req: () => postWithCreadentials( + path: 'listPublicRooms', credentials: user, target: user.server, body: {}), - onOK: (body) { - final List list = body['data'].map((json) { - return Room.fromJSON(json); - }).toList(); - for (Room r in list) { - blacklist.add(r); - } - }); + onOK: (body) async { + // parse rooms + final list = body['data']; - // process the list of public rooms - final List builder = []; - processor: - for (dynamic raw in list) { - try { - final room = Room.fromJSON(raw); + // try to fetch a list of rooms the user is a member of + // use an empty blacklist when request is not successful + final List blacklist = []; + await doNetworkRequest(sm, + req: () => postWithCreadentials( + path: 'listRooms', + credentials: user, + target: user.server, + body: {}), + onOK: (body) { + final List list = body['data'].map((json) { + return Room.fromJSON(json); + }).toList(); + for (Room r in list) { + blacklist.add(r); + } + }); - // figure out if room is on blacklist - // only add room to list, - // if not on blacklist - for (Room r in blacklist) { - if (r == room) { - // server on white list - // move to next iteration on outer for loop - continue processor; + // process the list of public rooms + final List builder = []; + processor: + for (dynamic raw in list) { + try { + final room = Room.fromJSON(raw); + + // figure out if room is on blacklist + // only add room to list, + // if not on blacklist + for (Room r in blacklist) { + if (r == room) { + // server on white list + // move to next iteration on outer for loop + continue processor; + } } - } - builder.add(room); - } catch (_) { - // ignore room + builder.add(room); + } catch (_) { + // ignore room + } } - } - setState(() { + setState(() { rooms = builder; + }); }); - }); } @override @@ -90,8 +90,8 @@ class _JoinRoomPageState extends State { @override Widget build(BuildContext context) { final textTheme = Theme.of(context) - .textTheme - .apply(displayColor: Theme.of(context).colorScheme.onSurface); + .textTheme + .apply(displayColor: Theme.of(context).colorScheme.onSurface); double width = MediaQuery.of(context).size.width; double height = MediaQuery.of(context).size.height; @@ -118,182 +118,189 @@ class _JoinRoomPageState extends State { }, ), MenuAnchor( - builder: (ctx, controller, child) { - return IconButton( - onPressed: () { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }, - icon: const Icon(Icons.more_vert), - ); - }, - menuChildren: [ - MenuItemButton( - leadingIcon: const Icon(Icons.drafts), - child: Text(AppLocalizations.of(context)!.joinRoomInvite), - onPressed: () { - // show settings screen - context.goNamed('join-room-ota'); - }), - ]) + builder: (ctx, controller, child) { + return IconButton( + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + icon: const Icon(Icons.more_vert), + ); + }, + menuChildren: [ + MenuItemButton( + leadingIcon: const Icon(Icons.drafts), + child: Text(AppLocalizations.of(context)!.joinRoomInvite), + onPressed: () { + // show settings screen + context.goNamed('join-room-ota'); + }), + ]) ], ), body: rooms.isEmpty - ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text(AppLocalizations.of(context)!.noNewRoomsFound, style: textTheme.titleLarge), - ], - )) - : ListView.builder( - itemCount: rooms.length, - itemBuilder: (ctx, i) { - final room = rooms[i]; - return Card( - margin: const EdgeInsets.all(8.0), - clipBehavior: Clip.antiAliasWithSaveLayer, - semanticContainer: true, - child: InkWell( - onTap: () { - // show modalBottomSheet - // with room information - // and join button - showModalBottomSheet( - context: ctx, - builder: (ctx) { - return BottomSheet( - onClosing: () {}, - builder: (ctx) { - return Column( - crossAxisAlignment: - CrossAxisAlignment.center, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(14), - child: Column(children: [ - // room icon - SvgPicture.asset( - (room.icon?.img)!, - width: smallest * 0.2, - height: smallest * 0.2, - ), - // room name - Text( - room.name, - style: textTheme.displayMedium, - ), - Text( - '${room.id}@${room.serverTag}', - style: textTheme.labelSmall, - ), - // description - Text(room.description, - style: textTheme.bodyLarge), - // visibility - Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Icon(room.visibility?.icon), - Text((room - .visibility?.text(context))!), - ]), - ])), - // action buttons - Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - // cancel button - Padding( - padding: - const EdgeInsets.all(14), - child: ElevatedButton.icon( - icon: - const Icon(Icons.close), - label: Text(AppLocalizations.of(context)!.cancel), - onPressed: () { - // close sheet - Navigator.pop(context); - }, - )), - // join room button - Padding( - padding: - const EdgeInsets.all(14), - child: FilledButton.icon( - icon: - const Icon(Icons.check), - label: Text(AppLocalizations.of(context)!.joinRoom), - onPressed: () async { - final scaffMgr = - ScaffoldMessenger.of( - context); + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(AppLocalizations.of(context)!.noNewRoomsFound, + style: textTheme.titleLarge), + ], + )) + : ListView.builder( + itemCount: rooms.length, + itemBuilder: (ctx, i) { + final room = rooms[i]; + return Card( + margin: const EdgeInsets.all(8.0), + clipBehavior: Clip.antiAliasWithSaveLayer, + semanticContainer: true, + child: InkWell( + onTap: () { + // show modalBottomSheet + // with room information + // and join button + showModalBottomSheet( + context: ctx, + builder: (ctx) { + return BottomSheet( + onClosing: () {}, + builder: (ctx) { + return Column( + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(14), + child: Column(children: [ + // room icon + SvgPicture.asset( + (room.icon?.img)!, + width: smallest * 0.2, + height: smallest * 0.2, + ), + // room name + Text( + room.name, + style: textTheme.displayMedium, + ), + Text( + '${room.id}@${room.serverTag}', + style: textTheme.labelSmall, + ), + // description + Text(room.description, + style: textTheme.bodyLarge), + // visibility + Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Icon(room.visibility?.icon), + Text((room.visibility + ?.text(context))!), + ]), + ])), + // action buttons + Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + // cancel button + Padding( + padding: + const EdgeInsets.all(14), + child: ElevatedButton.icon( + icon: + const Icon(Icons.close), + label: Text( + AppLocalizations.of( + context)! + .cancel), + onPressed: () { + // close sheet + Navigator.pop(context); + }, + )), + // join room button + Padding( + padding: + const EdgeInsets.all(14), + child: FilledButton.icon( + icon: + const Icon(Icons.check), + label: Text( + AppLocalizations.of( + context)! + .joinRoom), + onPressed: () async { + final scaffMgr = + ScaffoldMessenger.of( + context); - final nav = - Navigator.of(context); - final user = - context.read(); - final router = - GoRouter.of(context); + final nav = + Navigator.of(context); + final user = + context.read(); + final router = + GoRouter.of(context); - doNetworkRequest(scaffMgr, - req: () => - postWithCreadentials( - credentials: - user, - target: user - .server, - path: - 'joinPublicRoom', - body: { - 'room': - room.id, - 'server': room - .serverTag - }), - onOK: (body) async { - await room.toDisk(); - nav.pop(); - router - .pushReplacementNamed( - 'room', - params: { - 'server': room - .serverTag, - 'id': room.id - }); - }); - }, - )) - ]) - ], - ); - }, - ); - }); + doNetworkRequest(scaffMgr, + req: () => + postWithCreadentials( + credentials: + user, + target: user + .server, + path: + 'joinPublicRoom', + body: { + 'room': + room.id, + 'server': room + .serverTag + }), + onOK: (body) async { + await room.toDisk(); + nav.pop(); + router + .pushReplacementNamed( + 'room', + params: { + 'server': room + .serverTag, + 'id': room.id + }); + }); + }, + )) + ]) + ], + ); + }, + ); + }); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(10, 5, 5, 10), + child: ListTile( + title: Text(room.name), + visualDensity: const VisualDensity(vertical: 3), + subtitle: Text(room.description), + leading: AspectRatio( + aspectRatio: 1 / 1, + child: SvgPicture.asset("${room.icon?.img}"), + ), + hoverColor: Colors.transparent, + )))); }, - child: Container( - padding: const EdgeInsets.fromLTRB(10, 5, 5, 10), - child: ListTile( - title: Text(room.name), - visualDensity: const VisualDensity(vertical: 3), - subtitle: Text(room.description), - leading: AspectRatio( - aspectRatio: 1 / 1, - child: SvgPicture.asset("${room.icon?.img}"), - ), - hoverColor: Colors.transparent, - )))); - }, - ), + ), floatingActionButton: FloatingActionButton.extended( label: Text(AppLocalizations.of(context)!.newRoom), icon: const Icon(Icons.add), diff --git a/lib/screens/room/main.dart b/lib/screens/room/main.dart index 2bba06f..cc7c8fb 100644 --- a/lib/screens/room/main.dart +++ b/lib/screens/room/main.dart @@ -34,72 +34,69 @@ class _RoomPageState extends State { try { final diskRoom = - await Room.fromDisk(serverTag: widget.server, id: widget.tag); + await Room.fromDisk(serverTag: widget.server, id: widget.tag); setState(() { - room = diskRoom; + room = diskRoom; }); } catch (_) {} doNetworkRequest(sm, - req: () => postWithCreadentials( - path: 'getRoomInfo', - credentials: user, - target: user.server, - body: {'room': widget.tag, 'server': widget.server}), - onOK: (body) async { - final info = RoomInfo.fromJSON(body['data']); - final room = Room.fromJSON(body['data']); + req: () => postWithCreadentials( + path: 'getRoomInfo', + credentials: user, + target: user.server, + body: {'room': widget.tag, 'server': widget.server}), + onOK: (body) async { + final info = RoomInfo.fromJSON(body['data']); + final room = Room.fromJSON(body['data']); - room.toDisk(); + room.toDisk(); - setState(() { + setState(() { this.info = info; + }); + return true; + }, + onNetworkErr: () { + // user offline + if (room == null) { + // no room data available + // NOTE: close room? + } + return true; + }, + onServerErr: (json) { + // user no longer in room + // TODO: close room + return true; }); - return true; - }, - onNetworkErr: () { - // user offline - if (room == null) { - // no room data available - // NOTE: close room? - } - return true; - }, - onServerErr: (json) { - // user no longer in room - // TODO: close room - return true; - }); } @override void initState() { super.initState(); - room = Room( - id: widget.tag, - serverTag: widget.server - ); + room = Room(id: widget.tag, serverTag: widget.server); _ctr.addListener(() { - setState(() { - page = _ctr.page?.toInt() ?? _ctr.initialPage; - }); + setState(() { + page = _ctr.page?.toInt() ?? _ctr.initialPage; + }); }); Room.listen((_) async { - // rooms changed on disk - // probably this one, - // because it is currently open - // NOTE: might be a different room - // (if a background listener is implemented at some point, - // checking if this room changed might improve performance) - try { - final r = await Room.fromDisk(serverTag: widget.server, id: widget.tag); - setState(() { - room = r; - }); - } catch (_) {} + // rooms changed on disk + // probably this one, + // because it is currently open + // NOTE: might be a different room + // (if a background listener is implemented at some point, + // checking if this room changed might improve performance) + try { + final r = await Room.fromDisk(serverTag: widget.server, id: widget.tag); + setState(() { + room = r; + }); + } catch (_) {} }); WidgetsBinding.instance.addPostFrameCallback((_) => fetchInfo()); @@ -123,27 +120,27 @@ class _RoomPageState extends State { bottomNavigationBar: NavigationBar( onDestinationSelected: (int index) { _ctr.animateToPage(index, - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 300)); + curve: Curves.easeInOut, + duration: const Duration(milliseconds: 300)); }, selectedIndex: page, destinations: [ NavigationDestination( - icon: const Icon(Icons.list), - label: AppLocalizations.of(context)!.roomListTitle, - tooltip: AppLocalizations.of(context)!.roomListSubtitle), + icon: const Icon(Icons.list), + label: AppLocalizations.of(context)!.roomListTitle, + tooltip: AppLocalizations.of(context)!.roomListSubtitle), NavigationDestination( - icon: const Icon(Icons.inventory_2), - label: AppLocalizations.of(context)!.roomProductsTitle, - tooltip: AppLocalizations.of(context)!.roomProductsSubtitle), + icon: const Icon(Icons.inventory_2), + label: AppLocalizations.of(context)!.roomProductsTitle, + tooltip: AppLocalizations.of(context)!.roomProductsSubtitle), NavigationDestination( - icon: const Icon(Icons.category), - label: AppLocalizations.of(context)!.roomCategoriesTitle, - tooltip: AppLocalizations.of(context)!.roomCategoriesSubtitle), + icon: const Icon(Icons.category), + label: AppLocalizations.of(context)!.roomCategoriesTitle, + tooltip: AppLocalizations.of(context)!.roomCategoriesSubtitle), NavigationDestination( - icon: const Icon(Icons.info_rounded), - label: AppLocalizations.of(context)!.roomAboutTitle, - tooltip: AppLocalizations.of(context)!.roomAboutSubtitle), + icon: const Icon(Icons.info_rounded), + label: AppLocalizations.of(context)!.roomAboutTitle, + tooltip: AppLocalizations.of(context)!.roomAboutSubtitle), ], ), ); diff --git a/lib/screens/room/new.dart b/lib/screens/room/new.dart index aa22288..4ad920a 100644 --- a/lib/screens/room/new.dart +++ b/lib/screens/room/new.dart @@ -38,7 +38,7 @@ class _NewRoomPageState extends State { _ctrIcon = room.icon!; setState(() { - this.room = room; + this.room = room; }); } @@ -50,17 +50,17 @@ class _NewRoomPageState extends State { try { final diskRoom = - await Room.fromDisk(serverTag: widget.server!, id: widget.tag!); + await Room.fromDisk(serverTag: widget.server!, id: widget.tag!); initFromRoom(diskRoom); } catch (_) {} doNetworkRequest( sm, req: () => postWithCreadentials( - path: 'getRoomInfo', - credentials: user, - target: user.server, - body: {'room': widget.tag, 'server': widget.server}), + path: 'getRoomInfo', + credentials: user, + target: user.server, + body: {'room': widget.tag, 'server': widget.server}), onOK: (body) async { final room = Room.fromJSON(body['data']); room.toDisk(); @@ -70,9 +70,9 @@ class _NewRoomPageState extends State { // no room data available // use data from disk (() async { - // no room data available - // close screen - router.pushReplacementNamed('home'); + // no room data available + // close screen + router.pushReplacementNamed('home'); })(); return true; }, @@ -89,9 +89,9 @@ class _NewRoomPageState extends State { void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - if (isEditPage()) { - fetchInfo(); - } + if (isEditPage()) { + fetchInfo(); + } }); } @@ -102,235 +102,233 @@ class _NewRoomPageState extends State { @override Widget build(BuildContext context) { final textTheme = Theme.of(context) - .textTheme - .apply(displayColor: Theme.of(context).colorScheme.onSurface); + .textTheme + .apply(displayColor: Theme.of(context).colorScheme.onSurface); double width = MediaQuery.of(context).size.width; double height = MediaQuery.of(context).size.height; double smallest = min(min(width, height), 400); return showSpinner - ? Scaffold( - body: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const CircularProgressIndicator(), - Text(AppLocalizations.of(context)!.loading, - style: textTheme.titleLarge), - ]))) - : Scaffold( - appBar: AppBar( - title: Text(isEditPage() - ? AppLocalizations.of(context)!.editRoomMetadata - : AppLocalizations.of(context)!.newRoom), - ), - body: SingleChildScrollView( - child: Center( - child: Padding( - padding: const EdgeInsets.all(14), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 400), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - IconButton( - icon: SvgPicture.asset( - _ctrIcon.img, - width: smallest * 0.3, - height: smallest * 0.3, - ), - tooltip: AppLocalizations.of(context)! - .changeRoomIcon, - onPressed: () { - showDialog( - context: context, - builder: (ctx) => - RoomIconPicker(onSelect: (icon) { - setState(() { - _ctrIcon = icon; - }); - context.pop(); - })); - }), - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - enabled: !isEditPage(), - controller: _ctrID, - keyboardType: TextInputType.emailAddress, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.fact_check), - labelText: AppLocalizations.of(context)! - .inputRoomIdLabel, - hintText: AppLocalizations.of(context)! - .inputRoomIdHint, - helperText: AppLocalizations.of(context)! - .inputRoomIdHelp, - border: const OutlineInputBorder(), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrName, - keyboardType: TextInputType.name, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.badge), - labelText: AppLocalizations.of(context)! - .inputRoomNameLabel, - hintText: AppLocalizations.of(context)! - .inputRoomNameHint, - helperText: AppLocalizations.of(context)! - .inputRoomNameHelp, - border: const OutlineInputBorder(), - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrDescription, - keyboardType: TextInputType.text, - decoration: InputDecoration( - labelText: AppLocalizations.of(context)! - .inputRoomDescriptionLabel, - hintText: AppLocalizations.of(context)! - .inputRoomDescriptionHint, - helperText: AppLocalizations.of(context)! - .inputRoomDescriptionHelp, - prefixIcon: const Icon(Icons.dns), - border: const OutlineInputBorder(), - ), - ), - ), - ...(!isEditPage()) - ? [ - Text( - AppLocalizations.of(context)! - .roomVisibilityTitle, - style: textTheme.labelLarge), - Text( - AppLocalizations.of(context)! - .roomVisibilitySubtitle, - style: textTheme.bodySmall), - SegmentedButton( - showSelectedIcon: true, - multiSelectionEnabled: false, - emptySelectionAllowed: false, - segments: - RoomVisibility.list().map((vis) { - return ButtonSegment< - RoomVisibility>( - value: vis, - label: Text(vis.text(context)), - icon: Icon(vis.icon)); - }).toList(), - onSelectionChanged: ((vset) { - setState(() { - _ctrVis = vset.single; - }); - }), - selected: {_ctrVis}, - selectedIcon: Icon(_ctrVis.icon), - ), - ] - : [] - ], - ))))), - floatingActionButton: FloatingActionButton.extended( - onPressed: () async { - final scaffMgr = ScaffoldMessenger.of(context); - final router = GoRouter.of(context); - final trans = AppLocalizations.of(context); + ? Scaffold( + body: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CircularProgressIndicator(), + Text(AppLocalizations.of(context)!.loading, + style: textTheme.titleLarge), + ]))) + : Scaffold( + appBar: AppBar( + title: Text(isEditPage() + ? AppLocalizations.of(context)!.editRoomMetadata + : AppLocalizations.of(context)!.newRoom), + ), + body: SingleChildScrollView( + child: Center( + child: Padding( + padding: const EdgeInsets.all(14), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + icon: SvgPicture.asset( + _ctrIcon.img, + width: smallest * 0.3, + height: smallest * 0.3, + ), + tooltip: AppLocalizations.of(context)! + .changeRoomIcon, + onPressed: () { + showDialog( + context: context, + builder: (ctx) => + RoomIconPicker(onSelect: (icon) { + setState(() { + _ctrIcon = icon; + }); + context.pop(); + })); + }), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + enabled: !isEditPage(), + controller: _ctrID, + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.fact_check), + labelText: AppLocalizations.of(context)! + .inputRoomIdLabel, + hintText: AppLocalizations.of(context)! + .inputRoomIdHint, + helperText: AppLocalizations.of(context)! + .inputRoomIdHelp, + border: const OutlineInputBorder(), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrName, + keyboardType: TextInputType.name, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.badge), + labelText: AppLocalizations.of(context)! + .inputRoomNameLabel, + hintText: AppLocalizations.of(context)! + .inputRoomNameHint, + helperText: AppLocalizations.of(context)! + .inputRoomNameHelp, + border: const OutlineInputBorder(), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrDescription, + keyboardType: TextInputType.text, + decoration: InputDecoration( + labelText: AppLocalizations.of(context)! + .inputRoomDescriptionLabel, + hintText: AppLocalizations.of(context)! + .inputRoomDescriptionHint, + helperText: AppLocalizations.of(context)! + .inputRoomDescriptionHelp, + prefixIcon: const Icon(Icons.dns), + border: const OutlineInputBorder(), + ), + ), + ), + ...(!isEditPage()) + ? [ + Text( + AppLocalizations.of(context)! + .roomVisibilityTitle, + style: textTheme.labelLarge), + Text( + AppLocalizations.of(context)! + .roomVisibilitySubtitle, + style: textTheme.bodySmall), + SegmentedButton( + showSelectedIcon: true, + multiSelectionEnabled: false, + emptySelectionAllowed: false, + segments: + RoomVisibility.list().map((vis) { + return ButtonSegment< + RoomVisibility>( + value: vis, + label: Text(vis.text(context)), + icon: Icon(vis.icon)); + }).toList(), + onSelectionChanged: ((vset) { + setState(() { + _ctrVis = vset.single; + }); + }), + selected: {_ctrVis}, + selectedIcon: Icon(_ctrVis.icon), + ), + ] + : [] + ], + ))))), + floatingActionButton: FloatingActionButton.extended( + onPressed: () async { + final scaffMgr = ScaffoldMessenger.of(context); + final router = GoRouter.of(context); + final trans = AppLocalizations.of(context); - // name may not be empty - if (_ctrName.text.isEmpty) { - showSimpleSnackbar(scaffMgr, - text: trans!.errorNoRoomName, action: trans.ok); + // name may not be empty + if (_ctrName.text.isEmpty) { + showSimpleSnackbar(scaffMgr, + text: trans!.errorNoRoomName, action: trans.ok); - return; - } + return; + } - final user = context.read(); + final user = context.read(); - if (isEditPage()) { - final nav = Navigator.of(context); - Room clone = room!; - clone.name = _ctrName.text; - clone.description = _ctrDescription.text; - clone.icon = _ctrIcon; + if (isEditPage()) { + final nav = Navigator.of(context); + Room clone = room!; + clone.name = _ctrName.text; + clone.description = _ctrDescription.text; + clone.icon = _ctrIcon; - doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - target: user.server, - credentials: user, - path: 'changeRoomMeta', - body: { - 'room': clone.id, - 'server': clone.serverTag, - 'title': clone.name, - 'description': clone.description, - 'icon': clone.icon?.type, - }), - onOK: (_) async { - // room was created - // save room - await clone.toDisk(); - nav.pop(); - }); + doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + target: user.server, + credentials: user, + path: 'changeRoomMeta', + body: { + 'room': clone.id, + 'server': clone.serverTag, + 'title': clone.name, + 'description': clone.description, + 'icon': clone.icon?.type, + }), + onOK: (_) async { + // room was created + // save room + await clone.toDisk(); + nav.pop(); + }); + } else { + // new room specific tests & request - } else { - // new room specific tests & request + // ID should be at least three characters long + if (_ctrID.text.length < 3) { + showSimpleSnackbar(scaffMgr, + text: _ctrID.text.isEmpty + ? trans!.errorNoRoomId + : trans!.errorRoomIdLength, + action: trans.ok); - // ID should be at least three characters long - if (_ctrID.text.length < 3) { - showSimpleSnackbar(scaffMgr, - text: _ctrID.text.isEmpty - ? trans!.errorNoRoomId - : trans!.errorRoomIdLength, - action: trans.ok); + return; + } - return; - } + final room = Room( + id: _ctrID.text, + serverTag: user.server.tag, + name: _ctrName.text, + description: _ctrDescription.text, + icon: _ctrIcon, + visibility: _ctrVis); - final room = Room( - id: _ctrID.text, - serverTag: user.server.tag, - name: _ctrName.text, - description: _ctrDescription.text, - icon: _ctrIcon, - visibility: _ctrVis); - - doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - target: user.server, - credentials: user, - path: 'createRoom', - body: { - 'room': room.id, - 'title': room.name, - 'description': room.description, - 'icon': room.icon?.type, - 'visibility': room.visibility?.type - }), - onOK: (_) async { - // room was created - // save room - await room.toDisk(); - // move to home page - router.pushReplacementNamed('home'); - }); - } - - }, - label: Text(isEditPage() - ? AppLocalizations.of(context)!.editRoomMetadataShort - : AppLocalizations.of(context)!.createRoomShort), - icon: Icon(isEditPage() ? Icons.edit : Icons.add)), - ); + doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + target: user.server, + credentials: user, + path: 'createRoom', + body: { + 'room': room.id, + 'title': room.name, + 'description': room.description, + 'icon': room.icon?.type, + 'visibility': room.visibility?.type + }), + onOK: (_) async { + // room was created + // save room + await room.toDisk(); + // move to home page + router.pushReplacementNamed('home'); + }); + } + }, + label: Text(isEditPage() + ? AppLocalizations.of(context)!.editRoomMetadataShort + : AppLocalizations.of(context)!.createRoomShort), + icon: Icon(isEditPage() ? Icons.edit : Icons.add)), + ); } } diff --git a/lib/screens/room/pages/about.dart b/lib/screens/room/pages/about.dart index e84e803..fdea953 100644 --- a/lib/screens/room/pages/about.dart +++ b/lib/screens/room/pages/about.dart @@ -24,19 +24,19 @@ class _AboutRoomPageState extends State { @override Widget build(BuildContext context) { final textTheme = Theme.of(context) - .textTheme - .apply(displayColor: Theme.of(context).colorScheme.onSurface); + .textTheme + .apply(displayColor: Theme.of(context).colorScheme.onSurface); double width = MediaQuery.of(context).size.width; double height = MediaQuery.of(context).size.height; double smallest = min(width, height); return SingleChildScrollView( - child: Center( - child: Column(children: [ - // room meta display - ...(widget.room != null) - ? [ + child: Center( + child: Column(children: [ + // room meta display + ...(widget.room != null) + ? [ Padding( padding: const EdgeInsets.all(14), child: Column( @@ -60,27 +60,27 @@ class _AboutRoomPageState extends State { textAlign: TextAlign.center, ), Padding( - padding: const EdgeInsets.all(8), - child: SegmentedButton( - showSelectedIcon: true, - multiSelectionEnabled: false, - emptySelectionAllowed: false, - segments: RoomVisibility.list().map((vis) { + padding: const EdgeInsets.all(8), + child: SegmentedButton( + showSelectedIcon: true, + multiSelectionEnabled: false, + emptySelectionAllowed: false, + segments: RoomVisibility.list().map((vis) { return ButtonSegment( - value: vis.type, - label: Text(vis.text(context)), - icon: Icon(vis.icon)); - }).toList(), - onSelectionChanged: ((vset) { + value: vis.type, + label: Text(vis.text(context)), + icon: Icon(vis.icon)); + }).toList(), + onSelectionChanged: ((vset) { // check permission // only show confirm dialog when user // is admin, owner or has CHANGE_ADMIN permission if (widget.info == null || - (!(widget.info?.isAdmin ?? false) && - !(widget.info?.isOwner ?? false) && - ((widget.info?.permissions)! & - RoomPermission.ota == - 0))) { + (!(widget.info?.isAdmin ?? false) && + !(widget.info?.isOwner ?? false) && + ((widget.info?.permissions)! & + RoomPermission.ota == + 0))) { // action not permitted // NOTE: no error dialog should be shown // because the action is supposed to be hidden @@ -89,226 +89,247 @@ class _AboutRoomPageState extends State { final vis = RoomVisibility(vset.first); showDialog( - context: context, - builder: (ctx) => AlertDialog( - title: Text(AppLocalizations.of(context)!.changeRoomVisibilityTitle), - content: Text(AppLocalizations.of(context)!.changeRoomVisibilitySubtitle(vis.text(context))), - actions: [ - TextButton( - onPressed: () { - context.pop(); - }, - child: Text(AppLocalizations.of(context)!.cancel), - ), - FilledButton( - onPressed: () async { - final scaffMgr = - ScaffoldMessenger.of(context); - final nav = Navigator.of(context); - final user = context.read(); - - doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - path: 'setVisibility', - target: user.server, - body: { - 'room': widget.room?.id, - 'server': (widget - .room?.serverTag)!, - 'visibility': vset.first + context: context, + builder: (ctx) => AlertDialog( + title: Text(AppLocalizations.of(context)! + .changeRoomVisibilityTitle), + content: Text( + AppLocalizations.of(context)! + .changeRoomVisibilitySubtitle( + vis.text(context))), + actions: [ + TextButton( + onPressed: () { + context.pop(); }, - credentials: user), - onOK: (_) { - Room r = widget.room!; - r.visibility = vis; - r.toDisk(); - }, - after: () { - nav.pop(); - }); - }, - child: Text(AppLocalizations.of(context)!.ok), - ) - ], - )); - }), - selected: {(widget.room?.visibility?.type)!}, - selectedIcon: Icon((widget.room?.visibility?.icon)!), - )), + child: Text( + AppLocalizations.of(context)! + .cancel), + ), + FilledButton( + onPressed: () async { + final scaffMgr = + ScaffoldMessenger.of(context); + final nav = Navigator.of(context); + final user = context.read(); + + doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + path: 'setVisibility', + target: user.server, + body: { + 'room': widget.room?.id, + 'server': (widget + .room?.serverTag)!, + 'visibility': vset.first + }, + credentials: user), + onOK: (_) { + Room r = widget.room!; + r.visibility = vis; + r.toDisk(); + }, + after: () { + nav.pop(); + }); + }, + child: Text( + AppLocalizations.of(context)!.ok), + ) + ], + )); + }), + selected: {(widget.room?.visibility?.type)!}, + selectedIcon: Icon((widget.room?.visibility?.icon)!), + )), ], ), ) ] - : [], + : [], - Padding( - padding: const EdgeInsets.all(14), - child: Column( - children: [ - // edit room meta button - ...(widget.info != null && - ((widget.info?.isAdmin ?? false) || - (widget.info?.isOwner ?? false) || - ((widget.info?.permissions)! & - RoomPermission.changeMeta != - 0))) + Padding( + padding: const EdgeInsets.all(14), + child: Column( + children: [ + // edit room meta button + ...(widget.info != null && + ((widget.info?.isAdmin ?? false) || + (widget.info?.isOwner ?? false) || + ((widget.info?.permissions)! & + RoomPermission.changeMeta != + 0))) ? [ - ListTile( - trailing: const Icon(Icons.chevron_right), - title: Text(AppLocalizations.of(context)!.editRoomMetadata), - subtitle: Text(AppLocalizations.of(context)!.editRoomMetadataSubtitle), - onTap: () { - // show edit room screen - context.goNamed('edit-room', params: { + ListTile( + trailing: const Icon(Icons.chevron_right), + title: Text( + AppLocalizations.of(context)!.editRoomMetadata), + subtitle: Text(AppLocalizations.of(context)! + .editRoomMetadataSubtitle), + onTap: () { + // show edit room screen + context.goNamed('edit-room', params: { 'server': (widget.room?.serverTag)!, 'id': (widget.room?.id)! - }); - }, - ), - ] + }); + }, + ), + ] : [], - // open members view - ListTile( - trailing: const Icon(Icons.chevron_right), - title: Text(AppLocalizations.of(context)!.showRoomMembers), - subtitle: Text(AppLocalizations.of(context)!.showRoomMembersSubtitle), - onTap: () { - // open member view screen - context.goNamed('room-members', params: { - 'server': (widget.room?.serverTag)!, - 'id': (widget.room?.id)! - }); - }, - ), - // edit default member permission - ...(widget.info != null && - ((widget.info?.isAdmin ?? false) || - (widget.info?.isOwner ?? false) || - ((widget.info?.permissions)! & - RoomPermission.changeAdmin != - 0))) + // open members view + ListTile( + trailing: const Icon(Icons.chevron_right), + title: Text(AppLocalizations.of(context)!.showRoomMembers), + subtitle: + Text(AppLocalizations.of(context)!.showRoomMembersSubtitle), + onTap: () { + // open member view screen + context.goNamed('room-members', params: { + 'server': (widget.room?.serverTag)!, + 'id': (widget.room?.id)! + }); + }, + ), + // edit default member permission + ...(widget.info != null && + ((widget.info?.isAdmin ?? false) || + (widget.info?.isOwner ?? false) || + ((widget.info?.permissions)! & + RoomPermission.changeAdmin != + 0))) ? [ - ListTile( - trailing: const Icon(Icons.chevron_right), - title: Text(AppLocalizations.of(context)!.editRoomPermissions), - subtitle: Text(AppLocalizations.of(context)!.editRoomPermissionsSubtitle), - onTap: () { - // show checkbox screen - context.goNamed('room-permissions', params: { + ListTile( + trailing: const Icon(Icons.chevron_right), + title: Text( + AppLocalizations.of(context)!.editRoomPermissions), + subtitle: Text(AppLocalizations.of(context)! + .editRoomPermissionsSubtitle), + onTap: () { + // show checkbox screen + context.goNamed('room-permissions', params: { 'server': (widget.room?.serverTag)!, 'id': (widget.room?.id)! - }); - }, - ), - ] + }); + }, + ), + ] : [], - ...(widget.info != null && - ((widget.info?.isAdmin ?? false) || - (widget.info?.isOwner ?? false) || - ((widget.info?.permissions)! & RoomPermission.ota != - 0))) + ...(widget.info != null && + ((widget.info?.isAdmin ?? false) || + (widget.info?.isOwner ?? false) || + ((widget.info?.permissions)! & RoomPermission.ota != + 0))) ? [ - ListTile( - trailing: const Icon(Icons.chevron_right), - title: Text(AppLocalizations.of(context)!.manageRoomOTA), - subtitle: Text(AppLocalizations.of(context)!.manageRoomOTASubtitle), - onTap: () { - // show manage ota screen - context.goNamed('room-ota', params: { + ListTile( + trailing: const Icon(Icons.chevron_right), + title: + Text(AppLocalizations.of(context)!.manageRoomOTA), + subtitle: Text(AppLocalizations.of(context)! + .manageRoomOTASubtitle), + onTap: () { + // show manage ota screen + context.goNamed('room-ota', params: { 'server': (widget.room?.serverTag)!, 'id': (widget.room?.id)! - }); - }, - ), - ListTile( - trailing: const Icon(Icons.chevron_right), - title: Text(AppLocalizations.of(context)!.manageRoomInvites), - subtitle: Text(AppLocalizations.of(context)!.manageRoomInvitesSubtitle), - onTap: () { - // show manage ota screen - context.goNamed('room-invite', params: { + }); + }, + ), + ListTile( + trailing: const Icon(Icons.chevron_right), + title: Text( + AppLocalizations.of(context)!.manageRoomInvites), + subtitle: Text(AppLocalizations.of(context)! + .manageRoomInvitesSubtitle), + onTap: () { + // show manage ota screen + context.goNamed('room-invite', params: { 'server': (widget.room?.serverTag)!, 'id': (widget.room?.id)! - }); - }, - ), - ] + }); + }, + ), + ] : [], - ], - )), + ], + )), - ...(widget.info != null) - ? [ + ...(widget.info != null) + ? [ Padding( - padding: const EdgeInsets.all(8), - child: FilledButton.tonal( - child: Text(((widget.info?.isOwner)!) - ? AppLocalizations.of(context)!.deleteRoom - : AppLocalizations.of(context)!.leaveRoom), - onPressed: () { - // show confirm dialog - showDialog( - context: context, - builder: (ctx) => AlertDialog( - title: Text(((widget.info?.isOwner)!) - ? AppLocalizations.of(context)!.deleteRoom - : AppLocalizations.of(context)!.leaveRoom), - content: Text(((widget.info?.isOwner)!) - ? AppLocalizations.of(context)!.deleteRoomConfirm - : AppLocalizations.of(context)!.leaveRoomConfirm), - actions: [ - TextButton( - onPressed: () { - // close popup - Navigator.of(ctx).pop(); - }, - child: Text(AppLocalizations.of(context)!.cancel), - ), - FilledButton( - onPressed: () async { - // send request - final scaffMgr = - ScaffoldMessenger.of(ctx); - final nav = Navigator.of(ctx); - final router = GoRouter.of(context); - final user = context.read(); + padding: const EdgeInsets.all(8), + child: FilledButton.tonal( + child: Text(((widget.info?.isOwner)!) + ? AppLocalizations.of(context)!.deleteRoom + : AppLocalizations.of(context)!.leaveRoom), + onPressed: () { + // show confirm dialog + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text(((widget.info?.isOwner)!) + ? AppLocalizations.of(context)!.deleteRoom + : AppLocalizations.of(context)!.leaveRoom), + content: Text(((widget.info?.isOwner)!) + ? AppLocalizations.of(context)! + .deleteRoomConfirm + : AppLocalizations.of(context)! + .leaveRoomConfirm), + actions: [ + TextButton( + onPressed: () { + // close popup + Navigator.of(ctx).pop(); + }, + child: Text( + AppLocalizations.of(context)!.cancel), + ), + FilledButton( + onPressed: () async { + // send request + final scaffMgr = + ScaffoldMessenger.of(ctx); + final nav = Navigator.of(ctx); + final router = GoRouter.of(context); + final user = context.read(); - doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - path: ((widget.info?.isOwner)!) - ? 'deleteRoom' - : 'leaveRoom', - target: user.server, - body: { - 'room': widget.room?.id, - 'server': - (widget.room?.serverTag)!, - }, - credentials: user), - onOK: (_) async { - // try delete room from disk - try { - await widget.room?.removeDisk(); - } catch (_) {} + doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + path: ((widget.info?.isOwner)!) + ? 'deleteRoom' + : 'leaveRoom', + target: user.server, + body: { + 'room': widget.room?.id, + 'server': + (widget.room?.serverTag)!, + }, + credentials: user), + onOK: (_) async { + // try delete room from disk + try { + await widget.room?.removeDisk(); + } catch (_) {} - // go back home - router.pushReplacementNamed('home'); - }, - after: () { - // close popup - nav.pop(); - }); - }, - child: Text(((widget.info?.isOwner)!) - ? AppLocalizations.of(context)!.deleteRoomShort - : AppLocalizations.of(context)!.leaveRoomShort), - ) - ], - )); - }, - )) + // go back home + router.pushReplacementNamed('home'); + }, + after: () { + // close popup + nav.pop(); + }); + }, + child: Text(((widget.info?.isOwner)!) + ? AppLocalizations.of(context)! + .deleteRoomShort + : AppLocalizations.of(context)! + .leaveRoomShort), + ) + ], + )); + }, + )) ] - : [], + : [], ]))); } } diff --git a/lib/screens/room/pages/categories.dart b/lib/screens/room/pages/categories.dart index b14fc35..89a8c43 100644 --- a/lib/screens/room/pages/categories.dart +++ b/lib/screens/room/pages/categories.dart @@ -26,7 +26,7 @@ class _RoomCategoriesPageState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - fetchCategories(); + fetchCategories(); }); } @@ -36,29 +36,29 @@ class _RoomCategoriesPageState extends State { // TODO: load cached rooms doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getCategories', - body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), - onOK: (json) { - final resp = json['data'] - .map((raw) => RoomCategory.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getCategories', + body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), + onOK: (json) { + final resp = json['data'] + .map((raw) => RoomCategory.fromJSON(raw)) + .toList(); - if (mounted) { - setState(() { + if (mounted) { + setState(() { list = resp; - }); - } - }); + }); + } + }); } @override Widget build(BuildContext context) { final textTheme = Theme.of(context) - .textTheme - .apply(displayColor: Theme.of(context).colorScheme.onSurface); + .textTheme + .apply(displayColor: Theme.of(context).colorScheme.onSurface); return Scaffold( body: ReorderableListView.builder( @@ -70,13 +70,13 @@ class _RoomCategoriesPageState extends State { key: Key('cat-${item.id}'), leading: Icon(Icons.square_rounded, color: item.color), trailing: ((widget.info?.isAdmin ?? false) || - (widget.info?.isOwner ?? false) || - ((widget.info?.permissions)! & - RoomPermission.editRoomContent != - 0)) - ? ReorderableDragStartListener( - index: index, child: const Icon(Icons.drag_handle)) - : null, + (widget.info?.isOwner ?? false) || + ((widget.info?.permissions)! & + RoomPermission.editRoomContent != + 0)) + ? ReorderableDragStartListener( + index: index, child: const Icon(Icons.drag_handle)) + : null, title: Text(item.name), onTap: () { // TODO show edit category popup @@ -86,8 +86,8 @@ class _RoomCategoriesPageState extends State { if (!((widget.info?.isAdmin ?? false) || (widget.info?.isOwner ?? false) || ((widget.info?.permissions)! & - RoomPermission.editRoomContent != - 0))) { + RoomPermission.editRoomContent != + 0))) { // user is not allowed to edit or delete categories return; } @@ -96,104 +96,104 @@ class _RoomCategoriesPageState extends State { context: context, builder: (context) => BottomSheet( builder: (context) => Column(children: [ - Padding( + Padding( padding: const EdgeInsets.all(8), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon(Icons.square_rounded, - size: 48.0, color: item.color), + size: 48.0, color: item.color), Text(item.name, style: textTheme.titleLarge) ], - )), - // edit category - ListTile( - leading: const Icon(Icons.edit), - title: Text(AppLocalizations.of(context)!.editCategory), - subtitle: - Text(AppLocalizations.of(context)!.editCategoryLong), - trailing: const Icon(Icons.chevron_right), - onTap: () { - // close the modal bottom sheet - // so the user returns to the list, - // when leaving the category editor - Navigator.of(context).pop(); + )), + // edit category + ListTile( + leading: const Icon(Icons.edit), + title: Text(AppLocalizations.of(context)!.editCategory), + subtitle: + Text(AppLocalizations.of(context)!.editCategoryLong), + trailing: const Icon(Icons.chevron_right), + onTap: () { + // close the modal bottom sheet + // so the user returns to the list, + // when leaving the category editor + Navigator.of(context).pop(); - // launch category editor - context.pushNamed('edit-category', params: { - 'server': widget.room!.serverTag, - 'id': widget.room!.id, - 'category': item.id.toString() - }); - }, - ), - // delete category - ListTile( - leading: const Icon(Icons.delete), - title: Text(AppLocalizations.of(context)!.deleteCategory), - subtitle: Text( + // launch category editor + context.pushNamed('edit-category', params: { + 'server': widget.room!.serverTag, + 'id': widget.room!.id, + 'category': item.id.toString() + }); + }, + ), + // delete category + ListTile( + leading: const Icon(Icons.delete), + title: Text(AppLocalizations.of(context)!.deleteCategory), + subtitle: Text( AppLocalizations.of(context)!.deleteCategoryLong), - trailing: const Icon(Icons.chevron_right), - onTap: () { - // show popup - showDialog( + trailing: const Icon(Icons.chevron_right), + onTap: () { + // show popup + showDialog( context: context, builder: (ctx) => AlertDialog( - icon: const Icon(Icons.delete), - title: Text(AppLocalizations.of(context)! - .deleteCategory), - content: Text(AppLocalizations.of(context)! - .deleteCategoryConfirm(item.name)), - actions: [ - TextButton( - onPressed: () { - // close popup - Navigator.of(ctx).pop(); - }, - child: Text( - AppLocalizations.of(context)!.cancel), - ), - FilledButton( - onPressed: () async { - // send request - final scaffMgr = - ScaffoldMessenger.of(ctx); - // popup context - final navInner = Navigator.of(ctx); - // bottomsheet context - final nav = Navigator.of(context); - final user = context.read(); - - doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - path: 'deleteCategory', - target: user.server, - body: { - 'room': widget.room?.id, - 'server': - widget.room?.serverTag, - 'listCatID': item.id - }, - credentials: user), - onOK: (_) async { - // TODO: remove cached category - fetchCategories(); - }, - after: () { + icon: const Icon(Icons.delete), + title: Text(AppLocalizations.of(context)! + .deleteCategory), + content: Text(AppLocalizations.of(context)! + .deleteCategoryConfirm(item.name)), + actions: [ + TextButton( + onPressed: () { // close popup - navInner.pop(); - // close modal bottom sheet - nav.pop(); - }); - }, - child: Text(AppLocalizations.of(context)! - .deleteCategory), - ) - ], - )); - }, - ), + Navigator.of(ctx).pop(); + }, + child: Text( + AppLocalizations.of(context)!.cancel), + ), + FilledButton( + onPressed: () async { + // send request + final scaffMgr = + ScaffoldMessenger.of(ctx); + // popup context + final navInner = Navigator.of(ctx); + // bottomsheet context + final nav = Navigator.of(context); + final user = context.read(); + + doNetworkRequest(scaffMgr, + req: () => postWithCreadentials( + path: 'deleteCategory', + target: user.server, + body: { + 'room': widget.room?.id, + 'server': + widget.room?.serverTag, + 'listCatID': item.id + }, + credentials: user), + onOK: (_) async { + // TODO: remove cached category + fetchCategories(); + }, + after: () { + // close popup + navInner.pop(); + // close modal bottom sheet + nav.pop(); + }); + }, + child: Text(AppLocalizations.of(context)! + .deleteCategory), + ) + ], + )); + }, + ), ]), onClosing: () {}, ), @@ -206,50 +206,52 @@ class _RoomCategoriesPageState extends State { if (!((widget.info?.isAdmin ?? false) || (widget.info?.isOwner ?? false) || ((widget.info?.permissions)! & RoomPermission.editRoomContent != - 0))) { + 0))) { // user is not allowed to edit or delete categories return; } setState(() { - if (oldIndex < newIndex) { - newIndex -= 1; - } - final item = list.removeAt(oldIndex); - list.insert(newIndex, item); + if (oldIndex < newIndex) { + newIndex -= 1; + } + final item = list.removeAt(oldIndex); + list.insert(newIndex, item); - // network request - final user = context.read(); - doNetworkRequest(ScaffoldMessenger.of(context), + // network request + final user = context.read(); + doNetworkRequest(ScaffoldMessenger.of(context), req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'changeCategoriesOrder', - body: { - 'room': widget.room?.id, - 'server': widget.room?.serverTag, - 'listCatIDs': list.map((item) => item.id).toList() - })); + credentials: user, + target: user.server, + path: 'changeCategoriesOrder', + body: { + 'room': widget.room?.id, + 'server': widget.room?.serverTag, + 'listCatIDs': list.map((item) => item.id).toList() + })); }); }, ), - floatingActionButton: (widget.info != null && ((widget.info?.isAdmin ?? false) || - (widget.info?.isOwner ?? false) || - ((widget.info?.permissions)! & RoomPermission.editRoomContent != - 0))) - ? FloatingActionButton.extended( - icon: const Icon(Icons.add), - label: Text(AppLocalizations.of(context)!.newCategoryShort), - tooltip: AppLocalizations.of(context)!.newCategoryLong, - onPressed: () { - // show new category popup - context.pushNamed('new-category', params: { - 'server': widget.room!.serverTag, - 'id': widget.room!.id, - }); - }, - ) - : null, + floatingActionButton: (widget.info != null && + ((widget.info?.isAdmin ?? false) || + (widget.info?.isOwner ?? false) || + ((widget.info?.permissions)! & + RoomPermission.editRoomContent != + 0))) + ? FloatingActionButton.extended( + icon: const Icon(Icons.add), + label: Text(AppLocalizations.of(context)!.newCategoryShort), + tooltip: AppLocalizations.of(context)!.newCategoryLong, + onPressed: () { + // show new category popup + context.pushNamed('new-category', params: { + 'server': widget.room!.serverTag, + 'id': widget.room!.id, + }); + }, + ) + : null, ); } } diff --git a/lib/screens/room/pages/list.dart b/lib/screens/room/pages/list.dart index 0cd62b0..439e1ca 100644 --- a/lib/screens/room/pages/list.dart +++ b/lib/screens/room/pages/list.dart @@ -36,74 +36,74 @@ class _ShoppingListPageState extends State { // TODO: load cached items first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getItems', - body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomItem.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getItems', + body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomItem.fromJSON(raw)) + .toList(); - final List l = []; - final List c = []; + final List l = []; + final List c = []; - for (RoomItem item in resp) { - if (item.state == 0) { - l.add(item); - } else { - c.add(item); + for (RoomItem item in resp) { + if (item.state == 0) { + l.add(item); + } else { + c.add(item); + } } - } - // TODO: cache items + // TODO: cache items - if (mounted) { - setState(() { + if (mounted) { + setState(() { list = l; cart = c; sortAll(); - }); - } - }); + }); + } + }); } void sortAll() { for (List input in [list, cart]) { setState(() { - input.sort((a, b) { - if (a.category == b.category) { - return 0; - } - if (a.category == null) { - // b should be below - return -1; - } - if (b.category == null) { - // a should be below - return 1; - } + input.sort((a, b) { + if (a.category == b.category) { + return 0; + } + if (a.category == null) { + // b should be below + return -1; + } + if (b.category == null) { + // a should be below + return 1; + } - final weightA = weights[a.category]; - final weightB = weights[b.category]; - // both could be null now, - // so we have to check agein - if (weightA == weightB) { - return 0; - } - if (weightA == null) { - // b should be below - return -1; - } - if (weightB == null) { - // a should be below - return 1; - } + final weightA = weights[a.category]; + final weightB = weights[b.category]; + // both could be null now, + // so we have to check agein + if (weightA == weightB) { + return 0; + } + if (weightA == null) { + // b should be below + return -1; + } + if (weightB == null) { + // a should be below + return 1; + } - return weightA.compareTo(weightB); - }); + return weightA.compareTo(weightB); + }); }); } } @@ -114,31 +114,31 @@ class _ShoppingListPageState extends State { // TODO: load cached categories first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getCategories', - body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomCategory.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getCategories', + body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomCategory.fromJSON(raw)) + .toList(); - Map map = {}; - Map cat = {}; - for (int i = 0; i < resp.length; i++) { - map[resp[i].id] = i; - cat[resp[i].id] = resp[i]; - } + Map map = {}; + Map cat = {}; + for (int i = 0; i < resp.length; i++) { + map[resp[i].id] = i; + cat[resp[i].id] = resp[i]; + } - if (mounted) { - setState(() { + if (mounted) { + setState(() { weights = map; categories = cat; sortAll(); - }); - } - }); + }); + } + }); } void fetchProducts() { @@ -147,22 +147,22 @@ class _ShoppingListPageState extends State { // TODO: load cached products first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getProducts', - body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomProduct.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getProducts', + body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomProduct.fromJSON(raw)) + .toList(); - if (mounted) { - setState(() { + if (mounted) { + setState(() { products = resp; - }); - } - }); + }); + } + }); } @override @@ -170,41 +170,41 @@ class _ShoppingListPageState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - fetchItems(); - fetchCategories(); - fetchProducts(); + fetchItems(); + fetchCategories(); + fetchProducts(); }); } void changeItemState(RoomItem item) { final user = context.read(); doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'changeItemState', - body: { - 'room': widget.room?.id, - 'server': widget.room?.serverTag, - 'listItemID': item.id, - 'state': item.state - })); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'changeItemState', + body: { + 'room': widget.room?.id, + 'server': widget.room?.serverTag, + 'listItemID': item.id, + 'state': item.state + })); } @override Widget build(BuildContext context) { return Scaffold( body: ListView(children: [ - LabeledDivider(AppLocalizations.of(context)!.shoppingList), - ListView.builder( - shrinkWrap: true, - physics: const ClampingScrollPhysics(), - itemCount: list.length, - itemBuilder: (context, index) { - final item = list[index]; - final cat = - categories[item.category] ?? RoomCategory.other(context); - return ShoppingListItem( + LabeledDivider(AppLocalizations.of(context)!.shoppingList), + ListView.builder( + shrinkWrap: true, + physics: const ClampingScrollPhysics(), + itemCount: list.length, + itemBuilder: (context, index) { + final item = list[index]; + final cat = + categories[item.category] ?? RoomCategory.other(context); + return ShoppingListItem( name: item.name, description: item.description, category: cat, @@ -215,9 +215,9 @@ class _ShoppingListPageState extends State { item.state = 1; setState(() { - list.removeAt(index); - cart.add(item); - sortAll(); + list.removeAt(index); + cart.add(item); + sortAll(); }); // network request @@ -235,28 +235,28 @@ class _ShoppingListPageState extends State { // - delete item (if allowed) // - move to/from shopping cart? showModalBottomSheet( - context: context, - builder: (context) => ShoppingListItemInfo( - products: products, - category: cat, - info: widget.info, - item: item, - room: widget.room!.id, - server: widget.room!.serverTag)); - }); - }, - ), - LabeledDivider(AppLocalizations.of(context)!.shoppingCart), - ListView.builder( - itemCount: cart.length, - shrinkWrap: true, - physics: const ClampingScrollPhysics(), - itemBuilder: (context, index) { - final item = cart[index]; - final cat = - categories[item.category] ?? RoomCategory.other(context); + context: context, + builder: (context) => ShoppingListItemInfo( + products: products, + category: cat, + info: widget.info, + item: item, + room: widget.room!.id, + server: widget.room!.serverTag)); + }); + }, + ), + LabeledDivider(AppLocalizations.of(context)!.shoppingCart), + ListView.builder( + itemCount: cart.length, + shrinkWrap: true, + physics: const ClampingScrollPhysics(), + itemBuilder: (context, index) { + final item = cart[index]; + final cat = + categories[item.category] ?? RoomCategory.other(context); - return ShoppingListItem( + return ShoppingListItem( name: item.name, description: item.description, category: cat, @@ -266,9 +266,9 @@ class _ShoppingListPageState extends State { // move back to list item.state = 0; setState(() { - cart.removeAt(index); - list.add(item); - sortAll(); + cart.removeAt(index); + list.add(item); + sortAll(); }); // network request @@ -286,37 +286,37 @@ class _ShoppingListPageState extends State { // - delete item (if allowed) // - move to/from shopping cart? showModalBottomSheet( - context: context, - builder: (context) => ShoppingListItemInfo( - products: products, - category: cat, - item: item, - info: widget.info, - room: widget.room!.id, - server: widget.room!.serverTag)); - }); - }, - ) + context: context, + builder: (context) => ShoppingListItemInfo( + products: products, + category: cat, + item: item, + info: widget.info, + room: widget.room!.id, + server: widget.room!.serverTag)); + }); + }, + ) ]), floatingActionButton: (widget.info != null && - ((widget.info?.isAdmin ?? false) || - (widget.info?.isOwner ?? false) || - ((widget.info?.permissions)! & - RoomPermission.addShoppingListItems != - 0))) - ? FloatingActionButton.extended( - icon: const Icon(Icons.add), - label: Text(AppLocalizations.of(context)!.newItemShort), - tooltip: AppLocalizations.of(context)!.newItemLong, - onPressed: () { - // show new category popup - context.pushNamed('new-item', params: { - 'server': widget.room!.serverTag, - 'id': widget.room!.id, - }); - }, - ) - : null, + ((widget.info?.isAdmin ?? false) || + (widget.info?.isOwner ?? false) || + ((widget.info?.permissions)! & + RoomPermission.addShoppingListItems != + 0))) + ? FloatingActionButton.extended( + icon: const Icon(Icons.add), + label: Text(AppLocalizations.of(context)!.newItemShort), + tooltip: AppLocalizations.of(context)!.newItemLong, + onPressed: () { + // show new category popup + context.pushNamed('new-item', params: { + 'server': widget.room!.serverTag, + 'id': widget.room!.id, + }); + }, + ) + : null, ); } } @@ -331,14 +331,14 @@ class ShoppingListItem extends StatelessWidget { final Function()? onTap; const ShoppingListItem( - {required this.name, + {required this.name, required this.category, required this.inCart, required this.description, required key, this.onDismiss, this.onTap}) - : _key = key; + : _key = key; @override Widget build(BuildContext context) { @@ -360,21 +360,21 @@ class ShoppingListItem extends StatelessWidget { return true; }, background: - Icon(!inCart ? Icons.shopping_cart : Icons.remove_shopping_cart), + Icon(!inCart ? Icons.shopping_cart : Icons.remove_shopping_cart), child: Opacity( - opacity: inCart ? 0.5 : 1.0, - child: ListTile( - title: Text(name), - subtitle: Text(description), - trailing: CategoryChip( - category: category, - ), - onTap: () { - if (onTap != null) { - onTap!(); - } - }, - )), + opacity: inCart ? 0.5 : 1.0, + child: ListTile( + title: Text(name), + subtitle: Text(description), + trailing: CategoryChip( + category: category, + ), + onTap: () { + if (onTap != null) { + onTap!(); + } + }, + )), ); } } @@ -388,7 +388,7 @@ class ShoppingListItemInfo extends StatelessWidget { final List products; const ShoppingListItemInfo( - {super.key, + {super.key, this.info, required this.item, required this.server, @@ -404,88 +404,88 @@ class ShoppingListItemInfo extends StatelessWidget { builder: (context) => Column( children: [ Padding( - padding: const EdgeInsets.all(14), - child: Center( - child: Column(children: [ - Text(item.name, style: textTheme.headlineLarge), - Text(item.description, style: textTheme.titleMedium), - CategoryChip( - category: category, - ), - Text(Unit.fromId(item.unit).display(context, item.value)) - ]))), + padding: const EdgeInsets.all(14), + child: Center( + child: Column(children: [ + Text(item.name, style: textTheme.headlineLarge), + Text(item.description, style: textTheme.titleMedium), + CategoryChip( + category: category, + ), + Text(Unit.fromId(item.unit).display(context, item.value)) + ]))), ...(item.link != null) - ? [ - ListTile( - title: Text(AppLocalizations.of(context)! - .itemShowLinkedProductTitle), - subtitle: Text(AppLocalizations.of(context)! - .itemShowLinkedProductSubtitle), - trailing: const Icon(Icons.chevron_right), - onTap: () { - // launch "view-product" page for specific product - context.pushNamed('view-product', params: { - 'server': server, - 'id': room, - 'product': item.link.toString() - }); - }, - ) - ] - : [], + ? [ + ListTile( + title: Text(AppLocalizations.of(context)! + .itemShowLinkedProductTitle), + subtitle: Text(AppLocalizations.of(context)! + .itemShowLinkedProductSubtitle), + trailing: const Icon(Icons.chevron_right), + onTap: () { + // launch "view-product" page for specific product + context.pushNamed('view-product', params: { + 'server': server, + 'id': room, + 'product': item.link.toString() + }); + }, + ) + ] + : [], ...(info != null && - ((info?.isAdmin ?? false) || - (info?.isOwner ?? false) || - ((info?.permissions)! & - RoomPermission.addShoppingListItems != - 0))) - ? [ - ListTile( - title: Text(AppLocalizations.of(context)!.editItem), - subtitle: Text(AppLocalizations.of(context)!.editItemLong), - trailing: const Icon(Icons.chevron_right), - onTap: () { - context.pushNamed('edit-product', params: { - 'server': server, - 'id': room, - 'item': item.id.toString() - }); - }, - ), - ListTile( - title: - Text(AppLocalizations.of(context)!.deleteItemTitle), - subtitle: Text( - AppLocalizations.of(context)!.deleteItemSubtitle), - trailing: const Icon(Icons.chevron_right), - onTap: () { - // TODO: show confirm dialog - }), - ] - : [], + ((info?.isAdmin ?? false) || + (info?.isOwner ?? false) || + ((info?.permissions)! & + RoomPermission.addShoppingListItems != + 0))) + ? [ + ListTile( + title: Text(AppLocalizations.of(context)!.editItem), + subtitle: Text(AppLocalizations.of(context)!.editItemLong), + trailing: const Icon(Icons.chevron_right), + onTap: () { + context.pushNamed('edit-product', params: { + 'server': server, + 'id': room, + 'item': item.id.toString() + }); + }, + ), + ListTile( + title: + Text(AppLocalizations.of(context)!.deleteItemTitle), + subtitle: Text( + AppLocalizations.of(context)!.deleteItemSubtitle), + trailing: const Icon(Icons.chevron_right), + onTap: () { + // TODO: show confirm dialog + }), + ] + : [], ListTile( - title: Text(item.state == 0 - ? AppLocalizations.of(context)!.moveItemToCartTitle - : AppLocalizations.of(context)!.moveItemToCartSubtitle), - subtitle: Text(item.state == 0 - ? AppLocalizations.of(context)!.removeItemFromCartTitle - : AppLocalizations.of(context)!.removeItemFromCartSubtitle), - onTap: () { - // flip state - item.state = (item.state - 1).abs(); - final user = context.read(); - doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'changeItemState', - body: { - 'room': room, - 'server': server, - 'listItemID': item.id, - 'state': item.state - })); - }) + title: Text(item.state == 0 + ? AppLocalizations.of(context)!.moveItemToCartTitle + : AppLocalizations.of(context)!.moveItemToCartSubtitle), + subtitle: Text(item.state == 0 + ? AppLocalizations.of(context)!.removeItemFromCartTitle + : AppLocalizations.of(context)!.removeItemFromCartSubtitle), + onTap: () { + // flip state + item.state = (item.state - 1).abs(); + final user = context.read(); + doNetworkRequest(ScaffoldMessenger.of(context), + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'changeItemState', + body: { + 'room': room, + 'server': server, + 'listItemID': item.id, + 'state': item.state + })); + }) ], ), ); diff --git a/lib/screens/room/pages/products.dart b/lib/screens/room/pages/products.dart index b0f80d4..6514940 100644 --- a/lib/screens/room/pages/products.dart +++ b/lib/screens/room/pages/products.dart @@ -25,24 +25,24 @@ class _RoomProductsPageState extends State { final user = context.read(); doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getProducts', - body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomProduct.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getProducts', + body: {'room': widget.room?.id, 'server': widget.room?.serverTag}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomProduct.fromJSON(raw)) + .toList(); - // TODO: cache products + // TODO: cache products - if (mounted) { - setState(() { + if (mounted) { + setState(() { products = resp; - }); - } - }); + }); + } + }); } @override @@ -73,31 +73,33 @@ class _RoomProductsPageState extends State { // where as reading the shopping item description, // might be a good idea context.pushNamed('view-product', params: { - 'server': widget.room!.serverTag, - 'id': widget.room!.id, - 'product': item.id.toString() + 'server': widget.room!.serverTag, + 'id': widget.room!.id, + 'product': item.id.toString() }); }, ); }, ), - floatingActionButton: (widget.info != null && ((widget.info?.isAdmin ?? false) || - (widget.info?.isOwner ?? false) || - ((widget.info?.permissions)! & RoomPermission.editRoomContent != - 0))) - ? FloatingActionButton.extended( - icon: const Icon(Icons.add), - label: Text(AppLocalizations.of(context)!.newProductShort), - tooltip: AppLocalizations.of(context)!.newProductLong, - onPressed: () { - // show new category popup - context.pushNamed('new-product', params: { - 'server': widget.room!.serverTag, - 'id': widget.room!.id, - }); - }, - ) - : null, + floatingActionButton: (widget.info != null && + ((widget.info?.isAdmin ?? false) || + (widget.info?.isOwner ?? false) || + ((widget.info?.permissions)! & + RoomPermission.editRoomContent != + 0))) + ? FloatingActionButton.extended( + icon: const Icon(Icons.add), + label: Text(AppLocalizations.of(context)!.newProductShort), + tooltip: AppLocalizations.of(context)!.newProductLong, + onPressed: () { + // show new category popup + context.pushNamed('new-product', params: { + 'server': widget.room!.serverTag, + 'id': widget.room!.id, + }); + }, + ) + : null, ); } } diff --git a/lib/screens/room/products/view.dart b/lib/screens/room/products/view.dart index d1d8166..f3476ce 100644 --- a/lib/screens/room/products/view.dart +++ b/lib/screens/room/products/view.dart @@ -15,7 +15,7 @@ class ViewProductPage extends StatefulWidget { final String server; final String room; const ViewProductPage( - {required this.server, + {required this.server, required this.room, required this.product, super.key}); @@ -39,14 +39,14 @@ class _ViewProductPageState extends State { doNetworkRequest( sm, req: () => postWithCreadentials( - path: 'getRoomInfo', - credentials: user, - target: user.server, - body: {'room': widget.room, 'server': widget.server}), + path: 'getRoomInfo', + credentials: user, + target: user.server, + body: {'room': widget.room, 'server': widget.server}), onOK: (body) async { final info = RoomInfo.fromJSON(body['data']); setState(() { - this.info = info; + this.info = info; }); return true; }, @@ -59,25 +59,25 @@ class _ViewProductPageState extends State { // TODO: load cached categories first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getCategories', - body: {'room': widget.room, 'server': widget.server}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomCategory.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getCategories', + body: {'room': widget.room, 'server': widget.server}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomCategory.fromJSON(raw)) + .toList(); - Map map = {}; + Map map = {}; - for (RoomCategory cat in resp) { - map[cat.id] = cat; - } - setState(() { + for (RoomCategory cat in resp) { + map[cat.id] = cat; + } + setState(() { categories = map; + }); }); - }); } void fetchProducts() { @@ -86,29 +86,29 @@ class _ViewProductPageState extends State { // TODO: load cached products first doNetworkRequest(ScaffoldMessenger.of(context), - req: () => postWithCreadentials( - credentials: user, - target: user.server, - path: 'getProducts', - body: {'room': widget.room, 'server': widget.server}), - onOK: (body) async { - final resp = body['data'] - .map((raw) => RoomProduct.fromJSON(raw)) - .toList(); + req: () => postWithCreadentials( + credentials: user, + target: user.server, + path: 'getProducts', + body: {'room': widget.room, 'server': widget.server}), + onOK: (body) async { + final resp = body['data'] + .map((raw) => RoomProduct.fromJSON(raw)) + .toList(); - for (RoomProduct prod in resp) { - // load product info - // for current product - if (prod.id == widget.product) { - setState(() { + for (RoomProduct prod in resp) { + // load product info + // for current product + if (prod.id == widget.product) { + setState(() { product = prod; - }); + }); + } } - } - setState(() { + setState(() { products = resp; + }); }); - }); } @override @@ -116,9 +116,9 @@ class _ViewProductPageState extends State { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - fetchCategories(); - fetchProducts(); - fetchInfo(); + fetchCategories(); + fetchProducts(); + fetchInfo(); }); } @@ -131,10 +131,10 @@ class _ViewProductPageState extends State { title: Text(product?.name ?? ''), ), body: SingleChildScrollView( - child: Column(children: [ - // display product into - Center( - child: Padding( + child: Column(children: [ + // display product into + Center( + child: Padding( padding: const EdgeInsets.all(14), child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -142,68 +142,71 @@ class _ViewProductPageState extends State { children: [ Text(product?.name ?? '', style: textTheme.headlineLarge), Text(product?.description ?? '', - style: textTheme.titleMedium), + style: textTheme.titleMedium), Text(product?.ean ?? ''), CategoryChip(category: categories[product?.category]), - Text(product!=null?Unit.fromId(product!.defaultUnit).display(context, product!.defaultValue):'') + Text(product != null + ? Unit.fromId(product!.defaultUnit) + .display(context, product!.defaultValue) + : '') ], - ))), + ))), - // show actions (if allowed / available - // edit product button - ...(info != null && - (info!.isAdmin || - info!.isOwner || - (info!.permissions & RoomPermission.editRoomContent != 0))) + // show actions (if allowed / available + // edit product button + ...(info != null && + (info!.isAdmin || + info!.isOwner || + (info!.permissions & RoomPermission.editRoomContent != 0))) ? [ - ListTile( - title: Text(AppLocalizations.of(context)!.editProductTitle), - subtitle: - Text(AppLocalizations.of(context)!.editProductSubtitle), - onTap: () { - context.pushNamed('edit-product', params: { + ListTile( + title: Text(AppLocalizations.of(context)!.editProductTitle), + subtitle: + Text(AppLocalizations.of(context)!.editProductSubtitle), + onTap: () { + context.pushNamed('edit-product', params: { 'server': widget.server, 'id': widget.room, 'product': widget.product.toString() - }); - }, - trailing: const Icon(Icons.chevron_right), - ), - ] + }); + }, + trailing: const Icon(Icons.chevron_right), + ), + ] : [], - // show parent? - ...(product?.parent != null) + // show parent? + ...(product?.parent != null) ? [ - ListTile( - title: Text( - AppLocalizations.of(context)!.viewParentProductTitle), - subtitle: Text( - AppLocalizations.of(context)!.viewParentProductSubtitle), - onTap: () { - context.pushNamed('view-product', params: { + ListTile( + title: Text( + AppLocalizations.of(context)!.viewParentProductTitle), + subtitle: Text( + AppLocalizations.of(context)!.viewParentProductSubtitle), + onTap: () { + context.pushNamed('view-product', params: { 'server': widget.server, 'id': widget.room, 'product': product!.parent.toString() - }); - }, - trailing: const Icon(Icons.chevron_right), - ), - ] + }); + }, + trailing: const Icon(Icons.chevron_right), + ), + ] : [], - // show/manage children - ListTile( - title: Text(AppLocalizations.of(context)!.viewProductChildrenTitle), - subtitle: + // show/manage children + ListTile( + title: Text(AppLocalizations.of(context)!.viewProductChildrenTitle), + subtitle: Text(AppLocalizations.of(context)!.viewProductChildrenSubtitle), - onTap: () { - context.pushNamed('view-product-children', params: { - 'server': widget.server, - 'id': widget.room, - 'product': widget.product.toString() - }); - }, - trailing: const Icon(Icons.chevron_right), - ), + onTap: () { + context.pushNamed('view-product-children', params: { + 'server': widget.server, + 'id': widget.room, + 'product': widget.product.toString() + }); + }, + trailing: const Icon(Icons.chevron_right), + ), ])), ); } diff --git a/lib/screens/server/dashboard.dart b/lib/screens/server/dashboard.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/screens/server/dashboard.dart @@ -0,0 +1 @@ + diff --git a/lib/screens/settings/dialogs/password.dart b/lib/screens/settings/dialogs/password.dart index 16fc982..0816c42 100644 --- a/lib/screens/settings/dialogs/password.dart +++ b/lib/screens/settings/dialogs/password.dart @@ -25,54 +25,57 @@ class _ChangePasswordDialogState extends State { title: Text(AppLocalizations.of(context)!.changeThemeTitle), icon: const Icon(Icons.password), content: SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrOldPassword, - keyboardType: TextInputType.visiblePassword, - obscureText: true, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.lock), - labelText: AppLocalizations.of(context)!.inputOldPasswordLabel, - hintText: AppLocalizations.of(context)!.inputOldPasswordHint, - helperText:AppLocalizations.of(context)!.inputOldPasswordHelp, - border: const OutlineInputBorder(), - ), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrOldPassword, + keyboardType: TextInputType.visiblePassword, + obscureText: true, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.lock), + labelText: AppLocalizations.of(context)!.inputOldPasswordLabel, + hintText: AppLocalizations.of(context)!.inputOldPasswordHint, + helperText: AppLocalizations.of(context)!.inputOldPasswordHelp, + border: const OutlineInputBorder(), ), ), - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrNewPassword, - keyboardType: TextInputType.visiblePassword, - obscureText: true, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.lock), - labelText: AppLocalizations.of(context)!.inputNewPasswordLabel, - hintText: AppLocalizations.of(context)!.inputNewPasswordHint, - helperText:AppLocalizations.of(context)!.inputNewPasswordHelp, - border: const OutlineInputBorder(), - ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrNewPassword, + keyboardType: TextInputType.visiblePassword, + obscureText: true, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.lock), + labelText: AppLocalizations.of(context)!.inputNewPasswordLabel, + hintText: AppLocalizations.of(context)!.inputNewPasswordHint, + helperText: AppLocalizations.of(context)!.inputNewPasswordHelp, + border: const OutlineInputBorder(), ), ), - Padding( - padding: const EdgeInsets.all(8), - child: TextField( - controller: _ctrNewPasswordRepeat, - keyboardType: TextInputType.visiblePassword, - obscureText: true, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.lock), - labelText: AppLocalizations.of(context)!.inputNewPasswordRepeatLabel, - hintText: AppLocalizations.of(context)!.inputNewPasswordRepeatHint, - helperText:AppLocalizations.of(context)!.inputNewPasswordRepeatHelp, - border: const OutlineInputBorder(), - ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _ctrNewPasswordRepeat, + keyboardType: TextInputType.visiblePassword, + obscureText: true, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.lock), + labelText: + AppLocalizations.of(context)!.inputNewPasswordRepeatLabel, + hintText: + AppLocalizations.of(context)!.inputNewPasswordRepeatHint, + helperText: + AppLocalizations.of(context)!.inputNewPasswordRepeatHelp, + border: const OutlineInputBorder(), ), ), - ], + ), + ], )), actions: [ TextButton( @@ -93,8 +96,7 @@ class _ChangePasswordDialogState extends State { if (_ctrNewPassword.text.length < 6) { // password has to be at least 6 characters long showSimpleSnackbar(scaffMgr, - text: trans!.errorPasswordLength, - action: trans.dismiss); + text: trans!.errorPasswordLength, action: trans.dismiss); _ctrNewPasswordRepeat.clear(); return; @@ -102,7 +104,7 @@ class _ChangePasswordDialogState extends State { if (_ctrNewPassword.text != _ctrNewPasswordRepeat.text) { // new passwords do not match showSimpleSnackbar(scaffMgr, - text: trans!.errorPasswordsDoNotMatch, action: trans.dismiss); + text: trans!.errorPasswordsDoNotMatch, action: trans.dismiss); _ctrNewPasswordRepeat.clear(); return; @@ -110,7 +112,7 @@ class _ChangePasswordDialogState extends State { if (hashPassword(_ctrOldPassword.text) != user.password) { // current password wrong showSimpleSnackbar(scaffMgr, - text: trans!.errorOldPasswordWrong, action: trans.dismiss); + text: trans!.errorOldPasswordWrong, action: trans.dismiss); _ctrOldPassword.clear(); return; @@ -120,23 +122,23 @@ class _ChangePasswordDialogState extends State { // send request doNetworkRequest(scaffMgr, - req: () => postWithCreadentials( - path: 'changePassword', - target: user.server, - body: {'accountKey': password}, - credentials: user), - onOK: (_) async { - // update local user struct - final updatedUser = User( - username: user.username, - password: password, - server: user.server); - await updatedUser.toDisk(); - }, - after: () { - // close popup - nav.pop(); - }); + req: () => postWithCreadentials( + path: 'changePassword', + target: user.server, + body: {'accountKey': password}, + credentials: user), + onOK: (_) async { + // update local user struct + final updatedUser = User( + username: user.username, + password: password, + server: user.server); + await updatedUser.toDisk(); + }, + after: () { + // close popup + nav.pop(); + }); }, child: Text(AppLocalizations.of(context)!.changeThemeTitle), ) diff --git a/lib/screens/welcome.dart b/lib/screens/welcome.dart index 9c0fc21..f6ffc50 100644 --- a/lib/screens/welcome.dart +++ b/lib/screens/welcome.dart @@ -22,17 +22,17 @@ class _WelcomePageState extends State { _currentPage = controller.initialPage; controller.addListener(() { - setState(() { - _currentPage = controller.page?.toInt() ?? controller.initialPage; - }); + setState(() { + _currentPage = controller.page?.toInt() ?? controller.initialPage; + }); }); } @override Widget build(BuildContext context) { final textTheme = Theme.of(context) - .textTheme - .apply(displayColor: Theme.of(context).colorScheme.onSurface); + .textTheme + .apply(displayColor: Theme.of(context).colorScheme.onSurface); String fabText = AppLocalizations.of(context)!.next; if (_currentPage == 0) { @@ -55,86 +55,80 @@ class _WelcomePageState extends State { body: Column( children: [ Expanded( - child: PageView( - controller: controller, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset(asset("undraw/undraw_shopping_app.svg"), + child: PageView( + controller: controller, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset(asset("undraw/undraw_shopping_app.svg"), fit: BoxFit.contain, width: smallest * 0.5, height: smallest * 0.5), - Text( - AppLocalizations.of(context)!.welcomeTitle, - style: textTheme.displaySmall, - ), - Text( - AppLocalizations.of(context)!.welcomeSubtitle, - style: textTheme.bodyMedium - ) - ], - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset(asset("undraw/undraw_mobile_login.svg"), - fit: BoxFit.contain, - width: smallest * 0.5, - height: smallest * 0.5), - Text( - AppLocalizations.of(context)!.page2Title, - style: textTheme.displaySmall, - ), - Text( - AppLocalizations.of(context)!.page2Subtitle, + Text( + AppLocalizations.of(context)!.welcomeTitle, + style: textTheme.displaySmall, + ), + Text(AppLocalizations.of(context)!.welcomeSubtitle, style: textTheme.bodyMedium) - ], - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset(asset("undraw/undraw_online_connection.svg"), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset(asset("undraw/undraw_mobile_login.svg"), fit: BoxFit.contain, width: smallest * 0.5, height: smallest * 0.5), - Text( - AppLocalizations.of(context)!.page3Title, - style: textTheme.displaySmall, - ), - Text( - AppLocalizations.of(context)!.page3Subtitle, + Text( + AppLocalizations.of(context)!.page2Title, + style: textTheme.displaySmall, + ), + Text(AppLocalizations.of(context)!.page2Subtitle, style: textTheme.bodyMedium) - ], - ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset(asset("undraw/undraw_online_groceries.svg"), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset(asset("undraw/undraw_online_connection.svg"), fit: BoxFit.contain, width: smallest * 0.5, height: smallest * 0.5), - Text( - AppLocalizations.of(context)!.page4Title, - style: textTheme.displaySmall, - ), - Text( - AppLocalizations.of(context)!.page4Subtitle, + Text( + AppLocalizations.of(context)!.page3Title, + style: textTheme.displaySmall, + ), + Text(AppLocalizations.of(context)!.page3Subtitle, style: textTheme.bodyMedium) - ], - ), - ], + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset(asset("undraw/undraw_online_groceries.svg"), + fit: BoxFit.contain, + width: smallest * 0.5, + height: smallest * 0.5), + Text( + AppLocalizations.of(context)!.page4Title, + style: textTheme.displaySmall, + ), + Text(AppLocalizations.of(context)!.page4Subtitle, + style: textTheme.bodyMedium) + ], + ), + ], )), TextButton( onPressed: () { context.goNamed('signin'); }, - child: Text(AppLocalizations.of(context)!.userHasAnAccount - ), + child: Text(AppLocalizations.of(context)!.userHasAnAccount), ) ], ), @@ -149,8 +143,8 @@ class _WelcomePageState extends State { } else { // move to next page controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 300)); + curve: Curves.easeInOut, + duration: const Duration(milliseconds: 300)); } }, ),