5b398eb2ae
- Fixed bug where switching room pages (list,products,categories,about), would result an unknown error, due to setState being called on a widget that isn't mounted. This was solved by surrounding the setState function, with a condition to check if the widget is mounted - Fixed bug where room members weren't recognized as admins This was caused by a typedifference between the server and the app (The server now returns booleans, where as before a ==1 comparison was needed) - Fixed bug where successfully closing the admin/kick member dialog, would crash the application. This was caused by popping the same context twice. We are now using two navigator (the outer and the inner one)
254 lines
9.9 KiB
Dart
254 lines
9.9 KiB
Dart
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<StatefulWidget> createState() => _RoomCategoriesPageState();
|
|
}
|
|
|
|
class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
|
|
List<RoomCategory> list = [];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
fetchCategories();
|
|
});
|
|
}
|
|
|
|
void fetchCategories() async {
|
|
final user = context.read<User>();
|
|
|
|
// 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<RoomCategory>((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<User>();
|
|
|
|
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<User>();
|
|
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,
|
|
);
|
|
}
|
|
}
|