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",
"newItemQueryHint": "Type item or product name",
"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,
itemBuilder: (ctx, i) {
final room = rooms[i];
@ -134,27 +139,32 @@ class _HomePageState extends State<HomePage> {
child: InkWell(
onTap: () {
// open room
context.goNamed('room',
params: {'server': room.serverTag, 'id': room.id});
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),
padding:
const EdgeInsets.fromLTRB(10, 5, 5, 10),
child: ListTile(
title: Text(room.name),
visualDensity: const VisualDensity(vertical: 3),
visualDensity:
const VisualDensity(vertical: 3),
subtitle: Text(room.description),
leading: AspectRatio(
aspectRatio: 1 / 1,
child: SvgPicture.asset("${room.icon?.img}"),
child:
SvgPicture.asset("${room.icon?.img}"),
),
hoverColor: Colors.transparent,
))));
},
),
)))),
floatingActionButton: FloatingActionButton.extended(
label: Text(AppLocalizations.of(context)!.addRoom),
icon: const Icon(Icons.add),

View file

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

View file

@ -33,6 +33,10 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
return SingleChildScrollView(
child: Center(
child: Padding(
padding: const EdgeInsets.all(14),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: Column(children: [
// room meta display
...(widget.room != null)
@ -65,7 +69,8 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
showSelectedIcon: true,
multiSelectionEnabled: false,
emptySelectionAllowed: false,
segments: RoomVisibility.list().map((vis) {
segments:
RoomVisibility.list().map((vis) {
return ButtonSegment<int>(
value: vis.type,
label: Text(vis.text(context)),
@ -76,10 +81,14 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
// 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 ==
(!(widget.info?.isAdmin ??
false) &&
!(widget.info?.isOwner ??
false) &&
((widget.info
?.permissions)! &
RoomPermission
.ota ==
0))) {
// action not permitted
// NOTE: no error dialog should be shown
@ -87,46 +96,65 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
return;
}
final vis = RoomVisibility(vset.first);
final vis =
RoomVisibility(vset.first);
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(AppLocalizations.of(context)!
title: Text(AppLocalizations
.of(context)!
.changeRoomVisibilityTitle),
content: Text(
AppLocalizations.of(context)!
content: Text(AppLocalizations
.of(context)!
.changeRoomVisibilitySubtitle(
vis.text(context))),
vis.text(
context))),
actions: [
TextButton(
onPressed: () {
context.pop();
},
child: Text(
AppLocalizations.of(context)!
AppLocalizations.of(
context)!
.cancel),
),
FilledButton(
onPressed: () async {
final scaffMgr =
ScaffoldMessenger.of(context);
final nav = Navigator.of(context);
final user = context.read<User>();
ScaffoldMessenger
.of(context);
final nav =
Navigator.of(
context);
final user = context
.read<User>();
doNetworkRequest(scaffMgr,
req: () => postWithCreadentials(
path: 'setVisibility',
target: user.server,
doNetworkRequest(
scaffMgr,
req: () =>
postWithCreadentials(
path:
'setVisibility',
target: user
.server,
body: {
'room': widget.room?.id,
'room': widget
.room
?.id,
'server': (widget
.room?.serverTag)!,
'visibility': vset.first
.room
?.serverTag)!,
'visibility':
vset.first
},
credentials: user),
credentials:
user),
onOK: (_) {
Room r = widget.room!;
r.visibility = vis;
Room r = widget
.room!;
r.visibility =
vis;
r.toDisk();
},
after: () {
@ -134,13 +162,18 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
});
},
child: Text(
AppLocalizations.of(context)!.ok),
AppLocalizations.of(
context)!
.ok),
)
],
));
}),
selected: {(widget.room?.visibility?.type)!},
selectedIcon: Icon((widget.room?.visibility?.icon)!),
selected: {
(widget.room?.visibility?.type)!
},
selectedIcon: Icon(
(widget.room?.visibility?.icon)!),
)),
],
),
@ -161,10 +194,13 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
0)))
? [
ListTile(
trailing: const Icon(Icons.chevron_right),
trailing:
const Icon(Icons.chevron_right),
title: Text(
AppLocalizations.of(context)!.editRoomMetadata),
subtitle: Text(AppLocalizations.of(context)!
AppLocalizations.of(context)!
.editRoomMetadata),
subtitle: Text(
AppLocalizations.of(context)!
.editRoomMetadataSubtitle),
onTap: () {
// show edit room screen
@ -179,9 +215,10 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
// open members view
ListTile(
trailing: const Icon(Icons.chevron_right),
title: Text(AppLocalizations.of(context)!.showRoomMembers),
subtitle:
Text(AppLocalizations.of(context)!.showRoomMembersSubtitle),
title: Text(AppLocalizations.of(context)!
.showRoomMembers),
subtitle: Text(AppLocalizations.of(context)!
.showRoomMembersSubtitle),
onTap: () {
// open member view screen
context.goNamed('room-members', params: {
@ -199,15 +236,20 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
0)))
? [
ListTile(
trailing: const Icon(Icons.chevron_right),
trailing:
const Icon(Icons.chevron_right),
title: Text(
AppLocalizations.of(context)!.editRoomPermissions),
subtitle: Text(AppLocalizations.of(context)!
AppLocalizations.of(context)!
.editRoomPermissions),
subtitle: Text(
AppLocalizations.of(context)!
.editRoomPermissionsSubtitle),
onTap: () {
// show checkbox screen
context.goNamed('room-permissions', params: {
'server': (widget.room?.serverTag)!,
context.goNamed('room-permissions',
params: {
'server':
(widget.room?.serverTag)!,
'id': (widget.room?.id)!
});
},
@ -217,14 +259,18 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
...(widget.info != null &&
((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & RoomPermission.ota !=
((widget.info?.permissions)! &
RoomPermission.ota !=
0)))
? [
ListTile(
trailing: const Icon(Icons.chevron_right),
title:
Text(AppLocalizations.of(context)!.manageRoomOTA),
subtitle: Text(AppLocalizations.of(context)!
trailing:
const Icon(Icons.chevron_right),
title: Text(
AppLocalizations.of(context)!
.manageRoomOTA),
subtitle: Text(
AppLocalizations.of(context)!
.manageRoomOTASubtitle),
onTap: () {
// show manage ota screen
@ -235,14 +281,18 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
},
),
ListTile(
trailing: const Icon(Icons.chevron_right),
trailing:
const Icon(Icons.chevron_right),
title: Text(
AppLocalizations.of(context)!.manageRoomInvites),
subtitle: Text(AppLocalizations.of(context)!
AppLocalizations.of(context)!
.manageRoomInvites),
subtitle: Text(
AppLocalizations.of(context)!
.manageRoomInvitesSubtitle),
onTap: () {
// show manage ota screen
context.goNamed('room-invite', params: {
context
.goNamed('room-invite', params: {
'server': (widget.room?.serverTag)!,
'id': (widget.room?.id)!
});
@ -259,20 +309,30 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
padding: const EdgeInsets.all(8),
child: FilledButton.tonal(
child: Text(((widget.info?.isOwner)!)
? AppLocalizations.of(context)!.deleteRoom
: AppLocalizations.of(context)!.leaveRoom),
? 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)!
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)!
: AppLocalizations.of(
context)!
.leaveRoomConfirm),
actions: [
TextButton(
@ -281,47 +341,67 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
Navigator.of(ctx).pop();
},
child: Text(
AppLocalizations.of(context)!.cancel),
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<User>();
ScaffoldMessenger.of(
ctx);
final nav =
Navigator.of(ctx);
final router =
GoRouter.of(context);
final user =
context.read<User>();
doNetworkRequest(scaffMgr,
req: () => postWithCreadentials(
path: ((widget.info?.isOwner)!)
req: () =>
postWithCreadentials(
path: ((widget
.info
?.isOwner)!)
? 'deleteRoom'
: 'leaveRoom',
target: user.server,
target: user
.server,
body: {
'room': widget.room?.id,
'server':
(widget.room?.serverTag)!,
'room': widget
.room
?.id,
'server': (widget
.room
?.serverTag)!,
},
credentials: user),
credentials:
user),
onOK: (_) async {
// try delete room from disk
try {
await widget.room?.removeDisk();
await widget.room
?.removeDisk();
} catch (_) {}
// go back home
router.pushReplacementNamed('home');
router
.pushReplacementNamed(
'home');
},
after: () {
// close popup
nav.pop();
});
},
child: Text(((widget.info?.isOwner)!)
? AppLocalizations.of(context)!
child: Text(((widget
.info?.isOwner)!)
? AppLocalizations.of(
context)!
.deleteRoomShort
: AppLocalizations.of(context)!
: AppLocalizations.of(
context)!
.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);
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,
itemBuilder: (context, index) {
final item = list[index];
@ -75,7 +80,8 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
RoomPermission.editRoomContent !=
0))
? ReorderableDragStartListener(
index: index, child: const Icon(Icons.drag_handle))
index: index,
child: const Icon(Icons.drag_handle))
: null,
title: Text(item.name),
onTap: () {
@ -99,20 +105,24 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
Padding(
padding: const EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Icon(Icons.square_rounded,
size: 48.0, color: item.color),
Text(item.name, style: textTheme.titleLarge)
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),
title: Text(AppLocalizations.of(context)!
.editCategory),
subtitle: Text(AppLocalizations.of(context)!
.editCategoryLong),
trailing: const Icon(Icons.chevron_right),
onTap: () {
// close the modal bottom sheet
@ -131,9 +141,10 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
// delete category
ListTile(
leading: const Icon(Icons.delete),
title: Text(AppLocalizations.of(context)!.deleteCategory),
subtitle: Text(
AppLocalizations.of(context)!.deleteCategoryLong),
title: Text(AppLocalizations.of(context)!
.deleteCategory),
subtitle: Text(AppLocalizations.of(context)!
.deleteCategoryLong),
trailing: const Icon(Icons.chevron_right),
onTap: () {
// show popup
@ -141,10 +152,13 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
context: context,
builder: (ctx) => AlertDialog(
icon: const Icon(Icons.delete),
title: Text(AppLocalizations.of(context)!
title: Text(
AppLocalizations.of(context)!
.deleteCategory),
content: Text(AppLocalizations.of(context)!
.deleteCategoryConfirm(item.name)),
content: Text(
AppLocalizations.of(context)!
.deleteCategoryConfirm(
item.name)),
actions: [
TextButton(
onPressed: () {
@ -152,30 +166,43 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
Navigator.of(ctx).pop();
},
child: Text(
AppLocalizations.of(context)!.cancel),
AppLocalizations.of(
context)!
.cancel),
),
FilledButton(
onPressed: () async {
// send request
final scaffMgr =
ScaffoldMessenger.of(ctx);
ScaffoldMessenger.of(
ctx);
// popup context
final navInner = Navigator.of(ctx);
final navInner =
Navigator.of(ctx);
// bottomsheet context
final nav = Navigator.of(context);
final user = context.read<User>();
final nav =
Navigator.of(context);
final user =
context.read<User>();
doNetworkRequest(scaffMgr,
req: () => postWithCreadentials(
path: 'deleteCategory',
target: user.server,
req: () =>
postWithCreadentials(
path:
'deleteCategory',
target:
user.server,
body: {
'room': widget.room?.id,
'server':
widget.room?.serverTag,
'listCatID': item.id
'room': widget
.room?.id,
'server': widget
.room
?.serverTag,
'listCatID':
item.id
},
credentials: user),
credentials:
user),
onOK: (_) async {
// TODO: remove cached category
fetchCategories();
@ -187,7 +214,9 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
nav.pop();
});
},
child: Text(AppLocalizations.of(context)!
child: Text(
AppLocalizations.of(
context)!
.deleteCategory),
)
],
@ -205,7 +234,8 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
onReorder: (int oldIndex, int newIndex) {
if (!((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & RoomPermission.editRoomContent !=
((widget.info?.permissions)! &
RoomPermission.editRoomContent !=
0))) {
// user is not allowed to edit or delete categories
return;
@ -228,11 +258,12 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
body: {
'room': widget.room?.id,
'server': widget.room?.serverTag,
'listCatIDs': list.map((item) => item.id).toList()
'listCatIDs':
list.map((item) => item.id).toList()
}));
});
},
),
)))),
floatingActionButton: (widget.info != null &&
((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) ||

View file

@ -194,7 +194,12 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
@override
Widget build(BuildContext context) {
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),
ListView.builder(
shrinkWrap: true,
@ -202,8 +207,8 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
itemCount: list.length,
itemBuilder: (context, index) {
final item = list[index];
final cat =
categories[item.category] ?? RoomCategory.other(context);
final cat = categories[item.category] ??
RoomCategory.other(context);
return ShoppingListItem(
name: item.name,
description: item.description,
@ -253,8 +258,8 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) {
final item = cart[index];
final cat =
categories[item.category] ?? RoomCategory.other(context);
final cat = categories[item.category] ??
RoomCategory.other(context);
return ShoppingListItem(
name: item.name,
@ -297,7 +302,7 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
});
},
)
]),
])))),
floatingActionButton: (widget.info != null &&
((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) ||
@ -459,7 +464,60 @@ class ShoppingListItemInfo extends StatelessWidget {
AppLocalizations.of(context)!.deleteItemSubtitle),
trailing: const Icon(Icons.chevron_right),
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
Widget build(BuildContext context) {
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,
itemBuilder: (context, index) {
final item = products[index];
@ -80,7 +85,7 @@ class _RoomProductsPageState extends State<RoomProductsPage> {
},
);
},
),
)))),
floatingActionButton: (widget.info != null &&
((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) ||

View file

@ -207,6 +207,76 @@ class _ViewProductPageState extends State<ViewProductPage> {
},
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),
),
]
: []
])),
);
}