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:
parent
384fbb0573
commit
13c071b8ca
8 changed files with 872 additions and 611 deletions
|
@ -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}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,38 +123,48 @@ class _HomePageState extends State<HomePage> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: Center(
|
||||||
itemCount: rooms.length,
|
child: Padding(
|
||||||
itemBuilder: (ctx, i) {
|
padding: const EdgeInsets.all(14),
|
||||||
final room = rooms[i];
|
child: ConstrainedBox(
|
||||||
return Card(
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
margin: const EdgeInsets.all(8.0),
|
child: ListView.builder(
|
||||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
itemCount: rooms.length,
|
||||||
semanticContainer: true,
|
itemBuilder: (ctx, i) {
|
||||||
child: InkWell(
|
final room = rooms[i];
|
||||||
onTap: () {
|
return Card(
|
||||||
// open room
|
margin: const EdgeInsets.all(8.0),
|
||||||
context.goNamed('room',
|
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||||
params: {'server': room.serverTag, 'id': room.id});
|
semanticContainer: true,
|
||||||
},
|
child: InkWell(
|
||||||
onLongPress: () {
|
onTap: () {
|
||||||
// open bottom sheet
|
// open room
|
||||||
// NOTE: feature yet to be confirmed
|
context.goNamed('room', params: {
|
||||||
},
|
'server': room.serverTag,
|
||||||
child: Container(
|
'id': room.id
|
||||||
padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
|
});
|
||||||
child: ListTile(
|
},
|
||||||
title: Text(room.name),
|
onLongPress: () {
|
||||||
visualDensity: const VisualDensity(vertical: 3),
|
// open bottom sheet
|
||||||
subtitle: Text(room.description),
|
// NOTE: feature yet to be confirmed
|
||||||
leading: AspectRatio(
|
},
|
||||||
aspectRatio: 1 / 1,
|
child: Container(
|
||||||
child: SvgPicture.asset("${room.icon?.img}"),
|
padding:
|
||||||
),
|
const EdgeInsets.fromLTRB(10, 5, 5, 10),
|
||||||
hoverColor: Colors.transparent,
|
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(
|
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),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -33,303 +33,383 @@ class _AboutRoomPageState extends State<AboutRoomPage> {
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(children: [
|
child: Padding(
|
||||||
// room meta display
|
|
||||||
...(widget.room != null)
|
|
||||||
? [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(14),
|
||||||
child: Column(
|
child: ConstrainedBox(
|
||||||
children: [
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
SvgPicture.asset(
|
child: Column(children: [
|
||||||
(widget.room?.icon?.img)!,
|
// room meta display
|
||||||
width: smallest * 0.2,
|
...(widget.room != null)
|
||||||
height: smallest * 0.2,
|
? [
|
||||||
),
|
Padding(
|
||||||
Text(
|
padding: const EdgeInsets.all(14),
|
||||||
widget.room?.name ?? '',
|
child: Column(
|
||||||
style: textTheme.displayMedium,
|
children: [
|
||||||
),
|
SvgPicture.asset(
|
||||||
Text(
|
(widget.room?.icon?.img)!,
|
||||||
'${widget.room?.id}@${widget.room?.serverTag}',
|
width: smallest * 0.2,
|
||||||
style: textTheme.bodySmall,
|
height: smallest * 0.2,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.room?.description ?? '',
|
widget.room?.name ?? '',
|
||||||
style: textTheme.bodyMedium,
|
style: textTheme.displayMedium,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
Text(
|
||||||
Padding(
|
'${widget.room?.id}@${widget.room?.serverTag}',
|
||||||
padding: const EdgeInsets.all(8),
|
style: textTheme.bodySmall,
|
||||||
child: SegmentedButton<int>(
|
),
|
||||||
showSelectedIcon: true,
|
Text(
|
||||||
multiSelectionEnabled: false,
|
widget.room?.description ?? '',
|
||||||
emptySelectionAllowed: false,
|
style: textTheme.bodyMedium,
|
||||||
segments: RoomVisibility.list().map((vis) {
|
textAlign: TextAlign.center,
|
||||||
return ButtonSegment<int>(
|
),
|
||||||
value: vis.type,
|
Padding(
|
||||||
label: Text(vis.text(context)),
|
padding: const EdgeInsets.all(8),
|
||||||
icon: Icon(vis.icon));
|
child: SegmentedButton<int>(
|
||||||
}).toList(),
|
showSelectedIcon: true,
|
||||||
onSelectionChanged: ((vset) {
|
multiSelectionEnabled: false,
|
||||||
// check permission
|
emptySelectionAllowed: false,
|
||||||
// only show confirm dialog when user
|
segments:
|
||||||
// is admin, owner or has CHANGE_ADMIN permission
|
RoomVisibility.list().map((vis) {
|
||||||
if (widget.info == null ||
|
return ButtonSegment<int>(
|
||||||
(!(widget.info?.isAdmin ?? false) &&
|
value: vis.type,
|
||||||
!(widget.info?.isOwner ?? false) &&
|
label: Text(vis.text(context)),
|
||||||
((widget.info?.permissions)! &
|
icon: Icon(vis.icon));
|
||||||
RoomPermission.ota ==
|
}).toList(),
|
||||||
0))) {
|
onSelectionChanged: ((vset) {
|
||||||
// action not permitted
|
// check permission
|
||||||
// NOTE: no error dialog should be shown
|
// only show confirm dialog when user
|
||||||
// because the action is supposed to be hidden
|
// is admin, owner or has CHANGE_ADMIN permission
|
||||||
return;
|
if (widget.info == null ||
|
||||||
}
|
(!(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
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final vis = RoomVisibility(vset.first);
|
final vis =
|
||||||
showDialog(
|
RoomVisibility(vset.first);
|
||||||
context: context,
|
showDialog(
|
||||||
builder: (ctx) => AlertDialog(
|
context: context,
|
||||||
title: Text(AppLocalizations.of(context)!
|
builder: (ctx) => AlertDialog(
|
||||||
.changeRoomVisibilityTitle),
|
title: Text(AppLocalizations
|
||||||
content: Text(
|
.of(context)!
|
||||||
AppLocalizations.of(context)!
|
.changeRoomVisibilityTitle),
|
||||||
.changeRoomVisibilitySubtitle(
|
content: Text(AppLocalizations
|
||||||
vis.text(context))),
|
.of(context)!
|
||||||
actions: [
|
.changeRoomVisibilitySubtitle(
|
||||||
TextButton(
|
vis.text(
|
||||||
onPressed: () {
|
context))),
|
||||||
context.pop();
|
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<User>();
|
||||||
|
|
||||||
|
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)!
|
||||||
},
|
},
|
||||||
child: Text(
|
selectedIcon: Icon(
|
||||||
AppLocalizations.of(context)!
|
(widget.room?.visibility?.icon)!),
|
||||||
.cancel),
|
)),
|
||||||
),
|
],
|
||||||
FilledButton(
|
),
|
||||||
onPressed: () async {
|
)
|
||||||
final scaffMgr =
|
]
|
||||||
ScaffoldMessenger.of(context);
|
: [],
|
||||||
final nav = Navigator.of(context);
|
|
||||||
final user = context.read<User>();
|
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
Padding(
|
||||||
req: () => postWithCreadentials(
|
padding: const EdgeInsets.all(14),
|
||||||
path: 'setVisibility',
|
child: Column(
|
||||||
target: user.server,
|
children: [
|
||||||
body: {
|
// edit room meta button
|
||||||
'room': widget.room?.id,
|
...(widget.info != null &&
|
||||||
'server': (widget
|
((widget.info?.isAdmin ?? false) ||
|
||||||
.room?.serverTag)!,
|
(widget.info?.isOwner ?? false) ||
|
||||||
'visibility': vset.first
|
((widget.info?.permissions)! &
|
||||||
},
|
RoomPermission.changeMeta !=
|
||||||
credentials: user),
|
0)))
|
||||||
onOK: (_) {
|
? [
|
||||||
Room r = widget.room!;
|
ListTile(
|
||||||
r.visibility = vis;
|
trailing:
|
||||||
r.toDisk();
|
const Icon(Icons.chevron_right),
|
||||||
},
|
title: Text(
|
||||||
after: () {
|
AppLocalizations.of(context)!
|
||||||
nav.pop();
|
.editRoomMetadata),
|
||||||
});
|
subtitle: Text(
|
||||||
},
|
AppLocalizations.of(context)!
|
||||||
child: Text(
|
.editRoomMetadataSubtitle),
|
||||||
AppLocalizations.of(context)!.ok),
|
onTap: () {
|
||||||
)
|
// show edit room screen
|
||||||
],
|
context.goNamed('edit-room', params: {
|
||||||
));
|
'server': (widget.room?.serverTag)!,
|
||||||
}),
|
'id': (widget.room?.id)!
|
||||||
selected: {(widget.room?.visibility?.type)!},
|
});
|
||||||
selectedIcon: Icon((widget.room?.visibility?.icon)!),
|
},
|
||||||
)),
|
),
|
||||||
],
|
]
|
||||||
),
|
: [],
|
||||||
)
|
// open members view
|
||||||
]
|
ListTile(
|
||||||
: [],
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
title: Text(AppLocalizations.of(context)!
|
||||||
Padding(
|
.showRoomMembers),
|
||||||
padding: const EdgeInsets.all(14),
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
child: Column(
|
.showRoomMembersSubtitle),
|
||||||
children: [
|
onTap: () {
|
||||||
// edit room meta button
|
// open member view screen
|
||||||
...(widget.info != null &&
|
context.goNamed('room-members', params: {
|
||||||
((widget.info?.isAdmin ?? false) ||
|
'server': (widget.room?.serverTag)!,
|
||||||
(widget.info?.isOwner ?? false) ||
|
'id': (widget.room?.id)!
|
||||||
((widget.info?.permissions)! &
|
});
|
||||||
RoomPermission.changeMeta !=
|
},
|
||||||
0)))
|
),
|
||||||
? [
|
// edit default member permission
|
||||||
ListTile(
|
...(widget.info != null &&
|
||||||
trailing: const Icon(Icons.chevron_right),
|
((widget.info?.isAdmin ?? false) ||
|
||||||
title: Text(
|
(widget.info?.isOwner ?? false) ||
|
||||||
AppLocalizations.of(context)!.editRoomMetadata),
|
((widget.info?.permissions)! &
|
||||||
subtitle: Text(AppLocalizations.of(context)!
|
RoomPermission.changeAdmin !=
|
||||||
.editRoomMetadataSubtitle),
|
0)))
|
||||||
onTap: () {
|
? [
|
||||||
// show edit room screen
|
ListTile(
|
||||||
context.goNamed('edit-room', params: {
|
trailing:
|
||||||
'server': (widget.room?.serverTag)!,
|
const Icon(Icons.chevron_right),
|
||||||
'id': (widget.room?.id)!
|
title: Text(
|
||||||
});
|
AppLocalizations.of(context)!
|
||||||
},
|
.editRoomPermissions),
|
||||||
),
|
subtitle: Text(
|
||||||
]
|
AppLocalizations.of(context)!
|
||||||
: [],
|
.editRoomPermissionsSubtitle),
|
||||||
// open members view
|
onTap: () {
|
||||||
ListTile(
|
// show checkbox screen
|
||||||
trailing: const Icon(Icons.chevron_right),
|
context.goNamed('room-permissions',
|
||||||
title: Text(AppLocalizations.of(context)!.showRoomMembers),
|
params: {
|
||||||
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: {
|
|
||||||
'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)))
|
|
||||||
? [
|
|
||||||
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: {
|
|
||||||
'server': (widget.room?.serverTag)!,
|
|
||||||
'id': (widget.room?.id)!
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
|
|
||||||
...(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<User>();
|
|
||||||
|
|
||||||
doNetworkRequest(scaffMgr,
|
|
||||||
req: () => postWithCreadentials(
|
|
||||||
path: ((widget.info?.isOwner)!)
|
|
||||||
? 'deleteRoom'
|
|
||||||
: 'leaveRoom',
|
|
||||||
target: user.server,
|
|
||||||
body: {
|
|
||||||
'room': widget.room?.id,
|
|
||||||
'server':
|
'server':
|
||||||
(widget.room?.serverTag)!,
|
(widget.room?.serverTag)!,
|
||||||
},
|
'id': (widget.room?.id)!
|
||||||
credentials: user),
|
});
|
||||||
onOK: (_) async {
|
},
|
||||||
// try delete room from disk
|
),
|
||||||
try {
|
]
|
||||||
await widget.room?.removeDisk();
|
: [],
|
||||||
} catch (_) {}
|
...(widget.info != null &&
|
||||||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
// go back home
|
(widget.info?.isOwner ?? false) ||
|
||||||
router.pushReplacementNamed('home');
|
((widget.info?.permissions)! &
|
||||||
},
|
RoomPermission.ota !=
|
||||||
after: () {
|
0)))
|
||||||
// close popup
|
? [
|
||||||
nav.pop();
|
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: {
|
||||||
|
'server': (widget.room?.serverTag)!,
|
||||||
|
'id': (widget.room?.id)!
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
|
||||||
|
...(widget.info != null)
|
||||||
|
? [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: FilledButton.tonal(
|
||||||
child: Text(((widget.info?.isOwner)!)
|
child: Text(((widget.info?.isOwner)!)
|
||||||
? AppLocalizations.of(context)!
|
? AppLocalizations.of(context)!
|
||||||
.deleteRoomShort
|
.deleteRoom
|
||||||
: AppLocalizations.of(context)!
|
: AppLocalizations.of(context)!
|
||||||
.leaveRoomShort),
|
.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<User>();
|
||||||
|
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
},
|
||||||
|
))
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
])))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,178 +61,209 @@ 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(
|
||||||
buildDefaultDragHandles: false,
|
child: Padding(
|
||||||
itemBuilder: (context, index) {
|
padding: const EdgeInsets.all(14),
|
||||||
final item = list[index];
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
|
child: ReorderableListView.builder(
|
||||||
|
buildDefaultDragHandles: false,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = list[index];
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
key: Key('cat-${item.id}'),
|
key: Key('cat-${item.id}'),
|
||||||
leading: Icon(Icons.square_rounded, color: item.color),
|
leading: Icon(Icons.square_rounded, color: item.color),
|
||||||
trailing: ((widget.info?.isAdmin ?? false) ||
|
trailing: ((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! &
|
((widget.info?.permissions)! &
|
||||||
RoomPermission.editRoomContent !=
|
RoomPermission.editRoomContent !=
|
||||||
0))
|
0))
|
||||||
? ReorderableDragStartListener(
|
? ReorderableDragStartListener(
|
||||||
index: index, child: const Icon(Icons.drag_handle))
|
index: index,
|
||||||
: null,
|
child: const Icon(Icons.drag_handle))
|
||||||
title: Text(item.name),
|
: null,
|
||||||
onTap: () {
|
title: Text(item.name),
|
||||||
// TODO show edit category popup
|
onTap: () {
|
||||||
// NOTE: maybe use ModalBottomSheet
|
// TODO show edit category popup
|
||||||
// and show delete button in there
|
// NOTE: maybe use ModalBottomSheet
|
||||||
|
// and show delete button in there
|
||||||
|
|
||||||
if (!((widget.info?.isAdmin ?? false) ||
|
if (!((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
((widget.info?.permissions)! &
|
((widget.info?.permissions)! &
|
||||||
RoomPermission.editRoomContent !=
|
RoomPermission.editRoomContent !=
|
||||||
0))) {
|
0))) {
|
||||||
// user is not allowed to edit or delete categories
|
// user is not allowed to edit or delete categories
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
showModalBottomSheet(
|
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,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (context) => BottomSheet(
|
||||||
icon: const Icon(Icons.delete),
|
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)!
|
title: Text(AppLocalizations.of(context)!
|
||||||
.deleteCategory),
|
.deleteCategory),
|
||||||
content: Text(AppLocalizations.of(context)!
|
subtitle: Text(AppLocalizations.of(context)!
|
||||||
.deleteCategoryConfirm(item.name)),
|
.deleteCategoryLong),
|
||||||
actions: [
|
trailing: const Icon(Icons.chevron_right),
|
||||||
TextButton(
|
onTap: () {
|
||||||
onPressed: () {
|
// show popup
|
||||||
// close popup
|
showDialog(
|
||||||
Navigator.of(ctx).pop();
|
context: context,
|
||||||
},
|
builder: (ctx) => AlertDialog(
|
||||||
child: Text(
|
icon: const Icon(Icons.delete),
|
||||||
AppLocalizations.of(context)!.cancel),
|
title: Text(
|
||||||
),
|
AppLocalizations.of(context)!
|
||||||
FilledButton(
|
.deleteCategory),
|
||||||
onPressed: () async {
|
content: Text(
|
||||||
// send request
|
AppLocalizations.of(context)!
|
||||||
final scaffMgr =
|
.deleteCategoryConfirm(
|
||||||
ScaffoldMessenger.of(ctx);
|
item.name)),
|
||||||
// popup context
|
actions: [
|
||||||
final navInner = Navigator.of(ctx);
|
TextButton(
|
||||||
// bottomsheet context
|
onPressed: () {
|
||||||
final nav = Navigator.of(context);
|
// close popup
|
||||||
final user = context.read<User>();
|
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,
|
doNetworkRequest(scaffMgr,
|
||||||
req: () => postWithCreadentials(
|
req: () =>
|
||||||
path: 'deleteCategory',
|
postWithCreadentials(
|
||||||
target: user.server,
|
path:
|
||||||
body: {
|
'deleteCategory',
|
||||||
'room': widget.room?.id,
|
target:
|
||||||
'server':
|
user.server,
|
||||||
widget.room?.serverTag,
|
body: {
|
||||||
'listCatID': item.id
|
'room': widget
|
||||||
},
|
.room?.id,
|
||||||
credentials: user),
|
'server': widget
|
||||||
onOK: (_) async {
|
.room
|
||||||
// TODO: remove cached category
|
?.serverTag,
|
||||||
fetchCategories();
|
'listCatID':
|
||||||
},
|
item.id
|
||||||
after: () {
|
},
|
||||||
// close popup
|
credentials:
|
||||||
navInner.pop();
|
user),
|
||||||
// close modal bottom sheet
|
onOK: (_) async {
|
||||||
nav.pop();
|
// TODO: remove cached category
|
||||||
});
|
fetchCategories();
|
||||||
},
|
},
|
||||||
child: Text(AppLocalizations.of(context)!
|
after: () {
|
||||||
.deleteCategory),
|
// close popup
|
||||||
)
|
navInner.pop();
|
||||||
],
|
// close modal bottom sheet
|
||||||
));
|
nav.pop();
|
||||||
},
|
});
|
||||||
),
|
},
|
||||||
]),
|
child: Text(
|
||||||
onClosing: () {},
|
AppLocalizations.of(
|
||||||
),
|
context)!
|
||||||
);
|
.deleteCategory),
|
||||||
},
|
)
|
||||||
);
|
],
|
||||||
},
|
));
|
||||||
itemCount: list.length,
|
},
|
||||||
onReorder: (int oldIndex, int newIndex) {
|
),
|
||||||
if (!((widget.info?.isAdmin ?? false) ||
|
]),
|
||||||
(widget.info?.isOwner ?? false) ||
|
onClosing: () {},
|
||||||
((widget.info?.permissions)! & RoomPermission.editRoomContent !=
|
),
|
||||||
0))) {
|
);
|
||||||
// user is not allowed to edit or delete categories
|
},
|
||||||
return;
|
);
|
||||||
}
|
},
|
||||||
|
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(() {
|
setState(() {
|
||||||
if (oldIndex < newIndex) {
|
if (oldIndex < newIndex) {
|
||||||
newIndex -= 1;
|
newIndex -= 1;
|
||||||
}
|
}
|
||||||
final item = list.removeAt(oldIndex);
|
final item = list.removeAt(oldIndex);
|
||||||
list.insert(newIndex, item);
|
list.insert(newIndex, item);
|
||||||
|
|
||||||
// network request
|
// network request
|
||||||
final user = context.read<User>();
|
final user = context.read<User>();
|
||||||
doNetworkRequest(ScaffoldMessenger.of(context),
|
doNetworkRequest(ScaffoldMessenger.of(context),
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
credentials: user,
|
credentials: user,
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'changeCategoriesOrder',
|
path: 'changeCategoriesOrder',
|
||||||
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) ||
|
||||||
|
|
|
@ -194,110 +194,115 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: ListView(children: [
|
body: Center(
|
||||||
LabeledDivider(AppLocalizations.of(context)!.shoppingList),
|
child: Padding(
|
||||||
ListView.builder(
|
padding: const EdgeInsets.all(14),
|
||||||
shrinkWrap: true,
|
child: ConstrainedBox(
|
||||||
physics: const ClampingScrollPhysics(),
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
itemCount: list.length,
|
child: ListView(children: [
|
||||||
itemBuilder: (context, index) {
|
LabeledDivider(AppLocalizations.of(context)!.shoppingList),
|
||||||
final item = list[index];
|
ListView.builder(
|
||||||
final cat =
|
shrinkWrap: true,
|
||||||
categories[item.category] ?? RoomCategory.other(context);
|
physics: const ClampingScrollPhysics(),
|
||||||
return ShoppingListItem(
|
itemCount: list.length,
|
||||||
name: item.name,
|
itemBuilder: (context, index) {
|
||||||
description: item.description,
|
final item = list[index];
|
||||||
category: cat,
|
final cat = categories[item.category] ??
|
||||||
key: Key(item.id.toString()),
|
RoomCategory.other(context);
|
||||||
inCart: item.state != 0,
|
return ShoppingListItem(
|
||||||
onDismiss: () {
|
name: item.name,
|
||||||
// move to cart
|
description: item.description,
|
||||||
item.state = 1;
|
category: cat,
|
||||||
|
key: Key(item.id.toString()),
|
||||||
|
inCart: item.state != 0,
|
||||||
|
onDismiss: () {
|
||||||
|
// move to cart
|
||||||
|
item.state = 1;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
list.removeAt(index);
|
list.removeAt(index);
|
||||||
cart.add(item);
|
cart.add(item);
|
||||||
sortAll();
|
sortAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
// network request
|
// network request
|
||||||
// NOTE: for now we do not care if it is successfull,
|
// NOTE: for now we do not care if it is successfull,
|
||||||
// because the shopping cart is supposed to work even
|
// because the shopping cart is supposed to work even
|
||||||
// without network
|
// without network
|
||||||
changeItemState(item);
|
changeItemState(item);
|
||||||
},
|
},
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// TODO: show modal bottom sheet
|
// TODO: show modal bottom sheet
|
||||||
// containing
|
// containing
|
||||||
// - item info
|
// - item info
|
||||||
// - option to view linked product (if available)
|
// - option to view linked product (if available)
|
||||||
// - edit item (if allowed)
|
// - edit item (if allowed)
|
||||||
// - delete item (if allowed)
|
// - delete item (if allowed)
|
||||||
// - move to/from shopping cart?
|
// - move to/from shopping cart?
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ShoppingListItemInfo(
|
builder: (context) => ShoppingListItemInfo(
|
||||||
products: products,
|
products: products,
|
||||||
category: cat,
|
category: cat,
|
||||||
info: widget.info,
|
info: widget.info,
|
||||||
item: item,
|
item: item,
|
||||||
room: widget.room!.id,
|
room: widget.room!.id,
|
||||||
server: widget.room!.serverTag));
|
server: widget.room!.serverTag));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDivider(AppLocalizations.of(context)!.shoppingCart),
|
LabeledDivider(AppLocalizations.of(context)!.shoppingCart),
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
itemCount: cart.length,
|
itemCount: cart.length,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
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,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
category: cat,
|
category: cat,
|
||||||
key: Key(item.id.toString()),
|
key: Key(item.id.toString()),
|
||||||
inCart: item.state != 0,
|
inCart: item.state != 0,
|
||||||
onDismiss: () {
|
onDismiss: () {
|
||||||
// move back to list
|
// move back to list
|
||||||
item.state = 0;
|
item.state = 0;
|
||||||
setState(() {
|
setState(() {
|
||||||
cart.removeAt(index);
|
cart.removeAt(index);
|
||||||
list.add(item);
|
list.add(item);
|
||||||
sortAll();
|
sortAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
// network request
|
// network request
|
||||||
// NOTE: for now we do not care if it is successfull,
|
// NOTE: for now we do not care if it is successfull,
|
||||||
// because the shopping cart is supposed to work even
|
// because the shopping cart is supposed to work even
|
||||||
// without network
|
// without network
|
||||||
changeItemState(item);
|
changeItemState(item);
|
||||||
},
|
},
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// show modal bottom sheet
|
// show modal bottom sheet
|
||||||
// containing
|
// containing
|
||||||
// - item info
|
// - item info
|
||||||
// - option to view linked product (if available)
|
// - option to view linked product (if available)
|
||||||
// - edit item (if allowed)
|
// - edit item (if allowed)
|
||||||
// - delete item (if allowed)
|
// - delete item (if allowed)
|
||||||
// - move to/from shopping cart?
|
// - move to/from shopping cart?
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ShoppingListItemInfo(
|
builder: (context) => ShoppingListItemInfo(
|
||||||
products: products,
|
products: products,
|
||||||
category: cat,
|
category: cat,
|
||||||
item: item,
|
item: item,
|
||||||
info: widget.info,
|
info: widget.info,
|
||||||
room: widget.room!.id,
|
room: widget.room!.id,
|
||||||
server: widget.room!.serverTag));
|
server: widget.room!.serverTag));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]),
|
])))),
|
||||||
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),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
|
|
|
@ -55,32 +55,37 @@ class _RoomProductsPageState extends State<RoomProductsPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: ListView.builder(
|
body: Center(
|
||||||
itemCount: products.length,
|
child: Padding(
|
||||||
itemBuilder: (context, index) {
|
padding: const EdgeInsets.all(14),
|
||||||
final item = products[index];
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: products.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = products[index];
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(item.name),
|
title: Text(item.name),
|
||||||
subtitle: Text(item.description),
|
subtitle: Text(item.description),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// NOTE: we could also show a bottom sheet here,
|
// NOTE: we could also show a bottom sheet here,
|
||||||
// but because we need a seperate page/route either way
|
// but because we need a seperate page/route either way
|
||||||
// (for viewing item-links and exploring the product-tree)
|
// (for viewing item-links and exploring the product-tree)
|
||||||
// we might as well use the view-product page here
|
// we might as well use the view-product page here
|
||||||
// NOTE: This might seem inconsistent,
|
// NOTE: This might seem inconsistent,
|
||||||
// but you probably wont ever need to read a product description,
|
// but you probably wont ever need to read a product description,
|
||||||
// where as reading the shopping item description,
|
// where as reading the shopping item description,
|
||||||
// might be a good idea
|
// might be a good idea
|
||||||
context.pushNamed('view-product', params: {
|
context.pushNamed('view-product', params: {
|
||||||
'server': widget.room!.serverTag,
|
'server': widget.room!.serverTag,
|
||||||
'id': widget.room!.id,
|
'id': widget.room!.id,
|
||||||
'product': item.id.toString()
|
'product': item.id.toString()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
)))),
|
||||||
floatingActionButton: (widget.info != null &&
|
floatingActionButton: (widget.info != null &&
|
||||||
((widget.info?.isAdmin ?? false) ||
|
((widget.info?.isAdmin ?? false) ||
|
||||||
(widget.info?.isOwner ?? false) ||
|
(widget.info?.isOwner ?? false) ||
|
||||||
|
|
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: []
|
||||||
])),
|
])),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue