import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:outbag_app/backend/permissions.dart'; import 'package:outbag_app/backend/request.dart'; import 'package:outbag_app/backend/room.dart'; import 'package:outbag_app/backend/user.dart'; import 'package:outbag_app/tools/fetch_wrapper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; class RoomCategoriesPage extends StatefulWidget { final RoomInfo? info; final Room? room; const RoomCategoriesPage(this.room, this.info, {super.key}); @override State createState() => _RoomCategoriesPageState(); } class _RoomCategoriesPageState extends State { List list = []; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { fetchCategories(); }); } void fetchCategories() async { final user = context.read(); // 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(); if (mounted) { setState(() { list = resp; }); } }); } @override Widget build(BuildContext context) { final textTheme = Theme.of(context) .textTheme .apply(displayColor: Theme.of(context).colorScheme.onSurface); return Scaffold( body: ReorderableListView.builder( buildDefaultDragHandles: false, itemBuilder: (context, index) { final item = list[index]; return ListTile( 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, title: Text(item.name), onTap: () { // TODO show edit category popup // NOTE: maybe use ModalBottomSheet // and show delete button in there if (!((widget.info?.isAdmin ?? false) || (widget.info?.isOwner ?? false) || ((widget.info?.permissions)! & RoomPermission.editRoomContent != 0))) { // user is not allowed to edit or delete categories return; } showModalBottomSheet( context: context, builder: (context) => BottomSheet( builder: (context) => Column(children: [ 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), 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(); // 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( 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 }, after: () { // close popup navInner.pop(); // close modal bottom sheet nav.pop(); }); }, child: Text(AppLocalizations.of(context)! .deleteCategory), ) ], )); }, ), ]), onClosing: () {}, ), ); }, ); }, itemCount: list.length, onReorder: (int oldIndex, int newIndex) { if (!((widget.info?.isAdmin ?? false) || (widget.info?.isOwner ?? false) || ((widget.info?.permissions)! & RoomPermission.editRoomContent != 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); // 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() })); }); }, ), floatingActionButton: ((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, ); } }