remove item&product confirmation dialog

Because there currently is no way to auto-refresh the lists it will
appear as if nothing changed.
Switching to a different tab and back again will update the list though
This commit is contained in:
Jakob Meier 2024-02-23 10:16:57 +01:00
parent 384fbb0573
commit 13c071b8ca
No known key found for this signature in database
GPG key ID: 66BDC7E6A01A6152
8 changed files with 872 additions and 611 deletions

View file

@ -429,5 +429,12 @@
"newItemQueryEmpty": "Type to show quick access buttons", "newItemQueryEmpty": "Type to show quick access buttons",
"newItemQueryHint": "Type item or product name", "newItemQueryHint": "Type item or product name",
"newItemQuickAccessPrefix": "Create:", "newItemQuickAccessPrefix": "Create:",
"newItemQuickProduct": "product: {text}" "newItemQuickProduct": "product: {text}",
"deleteItem": "Delete Item",
"deleteItemConfirm": "Do you really want to remove the item named {item}",
"deleteProductTitle": "Delete Product",
"deleteProductSubtitle": "Remove the product from list of known products",
"deleteProduct": "Delete Product",
"deleteProductConfirm": "DO you really want to remove the product named {product}"
} }

View file

@ -123,7 +123,12 @@ class _HomePageState extends State<HomePage> {
) )
], ],
), ),
body: ListView.builder( body: Center(
child: Padding(
padding: const EdgeInsets.all(14),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ListView.builder(
itemCount: rooms.length, itemCount: rooms.length,
itemBuilder: (ctx, i) { itemBuilder: (ctx, i) {
final room = rooms[i]; final room = rooms[i];
@ -134,27 +139,32 @@ class _HomePageState extends State<HomePage> {
child: InkWell( child: InkWell(
onTap: () { onTap: () {
// open room // open room
context.goNamed('room', context.goNamed('room', params: {
params: {'server': room.serverTag, 'id': room.id}); 'server': room.serverTag,
'id': room.id
});
}, },
onLongPress: () { onLongPress: () {
// open bottom sheet // open bottom sheet
// NOTE: feature yet to be confirmed // NOTE: feature yet to be confirmed
}, },
child: Container( child: Container(
padding: const EdgeInsets.fromLTRB(10, 5, 5, 10), padding:
const EdgeInsets.fromLTRB(10, 5, 5, 10),
child: ListTile( child: ListTile(
title: Text(room.name), title: Text(room.name),
visualDensity: const VisualDensity(vertical: 3), visualDensity:
const VisualDensity(vertical: 3),
subtitle: Text(room.description), subtitle: Text(room.description),
leading: AspectRatio( leading: AspectRatio(
aspectRatio: 1 / 1, aspectRatio: 1 / 1,
child: SvgPicture.asset("${room.icon?.img}"), child:
SvgPicture.asset("${room.icon?.img}"),
), ),
hoverColor: Colors.transparent, hoverColor: Colors.transparent,
)))); ))));
}, },
), )))),
floatingActionButton: FloatingActionButton.extended( floatingActionButton: FloatingActionButton.extended(
label: Text(AppLocalizations.of(context)!.addRoom), label: Text(AppLocalizations.of(context)!.addRoom),
icon: const Icon(Icons.add), icon: const Icon(Icons.add),

View file

@ -138,7 +138,7 @@ class _EditItemPageState extends State<EditItemPage> {
child: Padding( child: Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400), constraints: const BoxConstraints(maxWidth: 600),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,

View file

@ -33,6 +33,10 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
return SingleChildScrollView( return SingleChildScrollView(
child: Center( child: Center(
child: Padding(
padding: const EdgeInsets.all(14),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: Column(children: [ child: Column(children: [
// room meta display // room meta display
...(widget.room != null) ...(widget.room != null)
@ -65,7 +69,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
showSelectedIcon: true, showSelectedIcon: true,
multiSelectionEnabled: false, multiSelectionEnabled: false,
emptySelectionAllowed: false, emptySelectionAllowed: false,
segments: RoomVisibility.list().map((vis) { segments:
RoomVisibility.list().map((vis) {
return ButtonSegment<int>( return ButtonSegment<int>(
value: vis.type, value: vis.type,
label: Text(vis.text(context)), label: Text(vis.text(context)),
@ -76,10 +81,14 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
// only show confirm dialog when user // only show confirm dialog when user
// is admin, owner or has CHANGE_ADMIN permission // is admin, owner or has CHANGE_ADMIN permission
if (widget.info == null || if (widget.info == null ||
(!(widget.info?.isAdmin ?? false) && (!(widget.info?.isAdmin ??
!(widget.info?.isOwner ?? false) && false) &&
((widget.info?.permissions)! & !(widget.info?.isOwner ??
RoomPermission.ota == false) &&
((widget.info
?.permissions)! &
RoomPermission
.ota ==
0))) { 0))) {
// action not permitted // action not permitted
// NOTE: no error dialog should be shown // NOTE: no error dialog should be shown
@ -87,46 +96,65 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
return; return;
} }
final vis = RoomVisibility(vset.first); final vis =
RoomVisibility(vset.first);
showDialog( showDialog(
context: context, context: context,
builder: (ctx) => AlertDialog( builder: (ctx) => AlertDialog(
title: Text(AppLocalizations.of(context)! title: Text(AppLocalizations
.of(context)!
.changeRoomVisibilityTitle), .changeRoomVisibilityTitle),
content: Text( content: Text(AppLocalizations
AppLocalizations.of(context)! .of(context)!
.changeRoomVisibilitySubtitle( .changeRoomVisibilitySubtitle(
vis.text(context))), vis.text(
context))),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
context.pop(); context.pop();
}, },
child: Text( child: Text(
AppLocalizations.of(context)! AppLocalizations.of(
context)!
.cancel), .cancel),
), ),
FilledButton( FilledButton(
onPressed: () async { onPressed: () async {
final scaffMgr = final scaffMgr =
ScaffoldMessenger.of(context); ScaffoldMessenger
final nav = Navigator.of(context); .of(context);
final user = context.read<User>(); final nav =
Navigator.of(
context);
final user = context
.read<User>();
doNetworkRequest(scaffMgr, doNetworkRequest(
req: () => postWithCreadentials( scaffMgr,
path: 'setVisibility', req: () =>
target: user.server, postWithCreadentials(
path:
'setVisibility',
target: user
.server,
body: { body: {
'room': widget.room?.id, 'room': widget
.room
?.id,
'server': (widget 'server': (widget
.room?.serverTag)!, .room
'visibility': vset.first ?.serverTag)!,
'visibility':
vset.first
}, },
credentials: user), credentials:
user),
onOK: (_) { onOK: (_) {
Room r = widget.room!; Room r = widget
r.visibility = vis; .room!;
r.visibility =
vis;
r.toDisk(); r.toDisk();
}, },
after: () { after: () {
@ -134,13 +162,18 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
}); });
}, },
child: Text( child: Text(
AppLocalizations.of(context)!.ok), AppLocalizations.of(
context)!
.ok),
) )
], ],
)); ));
}), }),
selected: {(widget.room?.visibility?.type)!}, selected: {
selectedIcon: Icon((widget.room?.visibility?.icon)!), (widget.room?.visibility?.type)!
},
selectedIcon: Icon(
(widget.room?.visibility?.icon)!),
)), )),
], ],
), ),
@ -161,10 +194,13 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
0))) 0)))
? [ ? [
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right), trailing:
const Icon(Icons.chevron_right),
title: Text( title: Text(
AppLocalizations.of(context)!.editRoomMetadata), AppLocalizations.of(context)!
subtitle: Text(AppLocalizations.of(context)! .editRoomMetadata),
subtitle: Text(
AppLocalizations.of(context)!
.editRoomMetadataSubtitle), .editRoomMetadataSubtitle),
onTap: () { onTap: () {
// show edit room screen // show edit room screen
@ -179,9 +215,10 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
// open members view // open members view
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
title: Text(AppLocalizations.of(context)!.showRoomMembers), title: Text(AppLocalizations.of(context)!
subtitle: .showRoomMembers),
Text(AppLocalizations.of(context)!.showRoomMembersSubtitle), subtitle: Text(AppLocalizations.of(context)!
.showRoomMembersSubtitle),
onTap: () { onTap: () {
// open member view screen // open member view screen
context.goNamed('room-members', params: { context.goNamed('room-members', params: {
@ -199,15 +236,20 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
0))) 0)))
? [ ? [
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right), trailing:
const Icon(Icons.chevron_right),
title: Text( title: Text(
AppLocalizations.of(context)!.editRoomPermissions), AppLocalizations.of(context)!
subtitle: Text(AppLocalizations.of(context)! .editRoomPermissions),
subtitle: Text(
AppLocalizations.of(context)!
.editRoomPermissionsSubtitle), .editRoomPermissionsSubtitle),
onTap: () { onTap: () {
// show checkbox screen // show checkbox screen
context.goNamed('room-permissions', params: { context.goNamed('room-permissions',
'server': (widget.room?.serverTag)!, params: {
'server':
(widget.room?.serverTag)!,
'id': (widget.room?.id)! 'id': (widget.room?.id)!
}); });
}, },
@ -217,14 +259,18 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
...(widget.info != null && ...(widget.info != null &&
((widget.info?.isAdmin ?? false) || ((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) || (widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & RoomPermission.ota != ((widget.info?.permissions)! &
RoomPermission.ota !=
0))) 0)))
? [ ? [
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right), trailing:
title: const Icon(Icons.chevron_right),
Text(AppLocalizations.of(context)!.manageRoomOTA), title: Text(
subtitle: Text(AppLocalizations.of(context)! AppLocalizations.of(context)!
.manageRoomOTA),
subtitle: Text(
AppLocalizations.of(context)!
.manageRoomOTASubtitle), .manageRoomOTASubtitle),
onTap: () { onTap: () {
// show manage ota screen // show manage ota screen
@ -235,14 +281,18 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
}, },
), ),
ListTile( ListTile(
trailing: const Icon(Icons.chevron_right), trailing:
const Icon(Icons.chevron_right),
title: Text( title: Text(
AppLocalizations.of(context)!.manageRoomInvites), AppLocalizations.of(context)!
subtitle: Text(AppLocalizations.of(context)! .manageRoomInvites),
subtitle: Text(
AppLocalizations.of(context)!
.manageRoomInvitesSubtitle), .manageRoomInvitesSubtitle),
onTap: () { onTap: () {
// show manage ota screen // show manage ota screen
context.goNamed('room-invite', params: { context
.goNamed('room-invite', params: {
'server': (widget.room?.serverTag)!, 'server': (widget.room?.serverTag)!,
'id': (widget.room?.id)! 'id': (widget.room?.id)!
}); });
@ -259,20 +309,30 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: FilledButton.tonal( child: FilledButton.tonal(
child: Text(((widget.info?.isOwner)!) child: Text(((widget.info?.isOwner)!)
? AppLocalizations.of(context)!.deleteRoom ? AppLocalizations.of(context)!
: AppLocalizations.of(context)!.leaveRoom), .deleteRoom
: AppLocalizations.of(context)!
.leaveRoom),
onPressed: () { onPressed: () {
// show confirm dialog // show confirm dialog
showDialog( showDialog(
context: context, context: context,
builder: (ctx) => AlertDialog( builder: (ctx) => AlertDialog(
title: Text(((widget.info?.isOwner)!) title: Text(
? AppLocalizations.of(context)!.deleteRoom ((widget.info?.isOwner)!)
: AppLocalizations.of(context)!.leaveRoom), ? AppLocalizations.of(
content: Text(((widget.info?.isOwner)!) context)!
? AppLocalizations.of(context)! .deleteRoom
: AppLocalizations.of(
context)!
.leaveRoom),
content: Text(
((widget.info?.isOwner)!)
? AppLocalizations.of(
context)!
.deleteRoomConfirm .deleteRoomConfirm
: AppLocalizations.of(context)! : AppLocalizations.of(
context)!
.leaveRoomConfirm), .leaveRoomConfirm),
actions: [ actions: [
TextButton( TextButton(
@ -281,47 +341,67 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
Navigator.of(ctx).pop(); Navigator.of(ctx).pop();
}, },
child: Text( child: Text(
AppLocalizations.of(context)!.cancel), AppLocalizations.of(
context)!
.cancel),
), ),
FilledButton( FilledButton(
onPressed: () async { onPressed: () async {
// send request // send request
final scaffMgr = final scaffMgr =
ScaffoldMessenger.of(ctx); ScaffoldMessenger.of(
final nav = Navigator.of(ctx); ctx);
final router = GoRouter.of(context); final nav =
final user = context.read<User>(); Navigator.of(ctx);
final router =
GoRouter.of(context);
final user =
context.read<User>();
doNetworkRequest(scaffMgr, doNetworkRequest(scaffMgr,
req: () => postWithCreadentials( req: () =>
path: ((widget.info?.isOwner)!) postWithCreadentials(
path: ((widget
.info
?.isOwner)!)
? 'deleteRoom' ? 'deleteRoom'
: 'leaveRoom', : 'leaveRoom',
target: user.server, target: user
.server,
body: { body: {
'room': widget.room?.id, 'room': widget
'server': .room
(widget.room?.serverTag)!, ?.id,
'server': (widget
.room
?.serverTag)!,
}, },
credentials: user), credentials:
user),
onOK: (_) async { onOK: (_) async {
// try delete room from disk // try delete room from disk
try { try {
await widget.room?.removeDisk(); await widget.room
?.removeDisk();
} catch (_) {} } catch (_) {}
// go back home // go back home
router.pushReplacementNamed('home'); router
.pushReplacementNamed(
'home');
}, },
after: () { after: () {
// close popup // close popup
nav.pop(); nav.pop();
}); });
}, },
child: Text(((widget.info?.isOwner)!) child: Text(((widget
? AppLocalizations.of(context)! .info?.isOwner)!)
? AppLocalizations.of(
context)!
.deleteRoomShort .deleteRoomShort
: AppLocalizations.of(context)! : AppLocalizations.of(
context)!
.leaveRoomShort), .leaveRoomShort),
) )
], ],
@ -330,6 +410,6 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
)) ))
] ]
: [], : [],
]))); ])))));
} }
} }

View file

@ -61,7 +61,12 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
.apply(displayColor: Theme.of(context).colorScheme.onSurface); .apply(displayColor: Theme.of(context).colorScheme.onSurface);
return Scaffold( return Scaffold(
body: ReorderableListView.builder( body: Center(
child: Padding(
padding: const EdgeInsets.all(14),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ReorderableListView.builder(
buildDefaultDragHandles: false, buildDefaultDragHandles: false,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = list[index]; final item = list[index];
@ -75,7 +80,8 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
RoomPermission.editRoomContent != RoomPermission.editRoomContent !=
0)) 0))
? ReorderableDragStartListener( ? ReorderableDragStartListener(
index: index, child: const Icon(Icons.drag_handle)) index: index,
child: const Icon(Icons.drag_handle))
: null, : null,
title: Text(item.name), title: Text(item.name),
onTap: () { onTap: () {
@ -99,20 +105,24 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
Padding( Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment:
crossAxisAlignment: CrossAxisAlignment.center, MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [ children: [
Icon(Icons.square_rounded, Icon(Icons.square_rounded,
size: 48.0, color: item.color), size: 48.0, color: item.color),
Text(item.name, style: textTheme.titleLarge) Text(item.name,
style: textTheme.titleLarge)
], ],
)), )),
// edit category // edit category
ListTile( ListTile(
leading: const Icon(Icons.edit), leading: const Icon(Icons.edit),
title: Text(AppLocalizations.of(context)!.editCategory), title: Text(AppLocalizations.of(context)!
subtitle: .editCategory),
Text(AppLocalizations.of(context)!.editCategoryLong), subtitle: Text(AppLocalizations.of(context)!
.editCategoryLong),
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
onTap: () { onTap: () {
// close the modal bottom sheet // close the modal bottom sheet
@ -131,9 +141,10 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
// delete category // delete category
ListTile( ListTile(
leading: const Icon(Icons.delete), leading: const Icon(Icons.delete),
title: Text(AppLocalizations.of(context)!.deleteCategory), title: Text(AppLocalizations.of(context)!
subtitle: Text( .deleteCategory),
AppLocalizations.of(context)!.deleteCategoryLong), subtitle: Text(AppLocalizations.of(context)!
.deleteCategoryLong),
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
onTap: () { onTap: () {
// show popup // show popup
@ -141,10 +152,13 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
context: context, context: context,
builder: (ctx) => AlertDialog( builder: (ctx) => AlertDialog(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
title: Text(AppLocalizations.of(context)! title: Text(
AppLocalizations.of(context)!
.deleteCategory), .deleteCategory),
content: Text(AppLocalizations.of(context)! content: Text(
.deleteCategoryConfirm(item.name)), AppLocalizations.of(context)!
.deleteCategoryConfirm(
item.name)),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
@ -152,30 +166,43 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
Navigator.of(ctx).pop(); Navigator.of(ctx).pop();
}, },
child: Text( child: Text(
AppLocalizations.of(context)!.cancel), AppLocalizations.of(
context)!
.cancel),
), ),
FilledButton( FilledButton(
onPressed: () async { onPressed: () async {
// send request // send request
final scaffMgr = final scaffMgr =
ScaffoldMessenger.of(ctx); ScaffoldMessenger.of(
ctx);
// popup context // popup context
final navInner = Navigator.of(ctx); final navInner =
Navigator.of(ctx);
// bottomsheet context // bottomsheet context
final nav = Navigator.of(context); final nav =
final user = context.read<User>(); Navigator.of(context);
final user =
context.read<User>();
doNetworkRequest(scaffMgr, doNetworkRequest(scaffMgr,
req: () => postWithCreadentials( req: () =>
path: 'deleteCategory', postWithCreadentials(
target: user.server, path:
'deleteCategory',
target:
user.server,
body: { body: {
'room': widget.room?.id, 'room': widget
'server': .room?.id,
widget.room?.serverTag, 'server': widget
'listCatID': item.id .room
?.serverTag,
'listCatID':
item.id
}, },
credentials: user), credentials:
user),
onOK: (_) async { onOK: (_) async {
// TODO: remove cached category // TODO: remove cached category
fetchCategories(); fetchCategories();
@ -187,7 +214,9 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
nav.pop(); nav.pop();
}); });
}, },
child: Text(AppLocalizations.of(context)! child: Text(
AppLocalizations.of(
context)!
.deleteCategory), .deleteCategory),
) )
], ],
@ -205,7 +234,8 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
onReorder: (int oldIndex, int newIndex) { onReorder: (int oldIndex, int newIndex) {
if (!((widget.info?.isAdmin ?? false) || if (!((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) || (widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & RoomPermission.editRoomContent != ((widget.info?.permissions)! &
RoomPermission.editRoomContent !=
0))) { 0))) {
// user is not allowed to edit or delete categories // user is not allowed to edit or delete categories
return; return;
@ -228,11 +258,12 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
body: { body: {
'room': widget.room?.id, 'room': widget.room?.id,
'server': widget.room?.serverTag, 'server': widget.room?.serverTag,
'listCatIDs': list.map((item) => item.id).toList() 'listCatIDs':
list.map((item) => item.id).toList()
})); }));
}); });
}, },
), )))),
floatingActionButton: (widget.info != null && floatingActionButton: (widget.info != null &&
((widget.info?.isAdmin ?? false) || ((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) || (widget.info?.isOwner ?? false) ||

View file

@ -194,7 +194,12 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: ListView(children: [ body: Center(
child: Padding(
padding: const EdgeInsets.all(14),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ListView(children: [
LabeledDivider(AppLocalizations.of(context)!.shoppingList), LabeledDivider(AppLocalizations.of(context)!.shoppingList),
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
@ -202,8 +207,8 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
itemCount: list.length, itemCount: list.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = list[index]; final item = list[index];
final cat = final cat = categories[item.category] ??
categories[item.category] ?? RoomCategory.other(context); RoomCategory.other(context);
return ShoppingListItem( return ShoppingListItem(
name: item.name, name: item.name,
description: item.description, description: item.description,
@ -253,8 +258,8 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = cart[index]; final item = cart[index];
final cat = final cat = categories[item.category] ??
categories[item.category] ?? RoomCategory.other(context); RoomCategory.other(context);
return ShoppingListItem( return ShoppingListItem(
name: item.name, name: item.name,
@ -297,7 +302,7 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
}); });
}, },
) )
]), ])))),
floatingActionButton: (widget.info != null && floatingActionButton: (widget.info != null &&
((widget.info?.isAdmin ?? false) || ((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) || (widget.info?.isOwner ?? false) ||
@ -459,7 +464,60 @@ class ShoppingListItemInfo extends StatelessWidget {
AppLocalizations.of(context)!.deleteItemSubtitle), AppLocalizations.of(context)!.deleteItemSubtitle),
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
onTap: () { onTap: () {
// TODO: show confirm dialog // show popup
showDialog(
context: context,
builder: (ctx) => AlertDialog(
icon: const Icon(Icons.delete),
title: Text(
AppLocalizations.of(context)!.deleteItem),
content: Text(AppLocalizations.of(context)!
.deleteItemConfirm(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: 'deleteItem',
target: user.server,
body: {
'room': room,
'server': server,
'listItemID': item.id
},
credentials: user),
onOK: (_) async {
// TODO: remove cached item
},
after: () {
// close popup
navInner.pop();
// close modal bottom sheet
nav.pop();
});
},
child: Text(AppLocalizations.of(context)!
.deleteItem),
)
],
));
}), }),
] ]
: [], : [],

View file

@ -55,7 +55,12 @@ class _RoomProductsPageState extends State<RoomProductsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: ListView.builder( body: Center(
child: Padding(
padding: const EdgeInsets.all(14),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ListView.builder(
itemCount: products.length, itemCount: products.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = products[index]; final item = products[index];
@ -80,7 +85,7 @@ class _RoomProductsPageState extends State<RoomProductsPage> {
}, },
); );
}, },
), )))),
floatingActionButton: (widget.info != null && floatingActionButton: (widget.info != null &&
((widget.info?.isAdmin ?? false) || ((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) || (widget.info?.isOwner ?? false) ||

View file

@ -207,6 +207,76 @@ class _ViewProductPageState extends State<ViewProductPage> {
}, },
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
), ),
...(info != null &&
((info?.isAdmin ?? false) ||
(info?.isOwner ?? false) ||
((info?.permissions)! & RoomPermission.editRoomContent !=
0)))
? [
// delete product
ListTile(
title: Text(AppLocalizations.of(context)!.deleteProductTitle),
subtitle:
Text(AppLocalizations.of(context)!.deleteProductSubtitle),
onTap: () {
// show popup
showDialog(
context: context,
builder: (ctx) => AlertDialog(
icon: const Icon(Icons.delete),
title: Text(
AppLocalizations.of(context)!.deleteProduct),
content: Text(AppLocalizations.of(context)!
.deleteProductConfirm(product?.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: 'deleteProduct',
target: user.server,
body: {
'room': widget.room,
'server': widget.server,
'listProdID': product?.id ?? ""
},
credentials: user),
onOK: (_) async {
// TODO: remove cached product
},
after: () {
// close popup
navInner.pop();
// close modal bottom sheet
nav.pop();
});
},
child: Text(AppLocalizations.of(context)!
.deleteProduct),
)
],
));
},
trailing: const Icon(Icons.chevron_right),
),
]
: []
])), ])),
); );
} }