Bug fixes (see description)

- Fixed bug where switching room pages
  (list,products,categories,about),
  would result an unknown error,
  due to setState being called on a widget that isn't mounted.
  This was solved by surrounding the setState function,
  with a condition to check if the widget is mounted

- Fixed bug where room members weren't recognized as admins
  This was caused by a typedifference between the server and the app
  (The server now returns booleans,
  where as before a ==1 comparison was
  needed)
- Fixed bug where successfully closing the admin/kick member dialog,
  would crash the application.
  This was caused by popping the same context twice.
  We are now using two navigator (the outer and the inner one)
This commit is contained in:
Jakob Meier 2023-04-05 08:49:46 +02:00
parent 89c0b069e1
commit 5b398eb2ae
No known key found for this signature in database
GPG key ID: 66BDC7E6A01A6152
5 changed files with 113 additions and 63 deletions

View file

@ -319,15 +319,14 @@ class RoomMember {
final String id; final String id;
final String serverTag; final String serverTag;
final bool isAdmin; final bool isAdmin;
final bool isInvitePending;
const RoomMember( const RoomMember(
{required this.id, required this.serverTag, required this.isAdmin}); {required this.id, required this.serverTag, required this.isAdmin, this.isInvitePending=false});
factory RoomMember.fromJSON(dynamic json) { factory RoomMember.fromJSON(dynamic json) {
return RoomMember( return RoomMember(
id: json['name'], id: json['name'], serverTag: json['server'], isAdmin: json['admin'], isInvitePending: json['confirmed']);
serverTag: json['server'],
isAdmin: json['admin'] == 1);
} }
String get humanReadableName { String get humanReadableName {

View file

@ -96,8 +96,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title:
AppLocalizations.of(context)!.roomMembersTitle(list.length)), Text(AppLocalizations.of(context)!.roomMembersTitle(list.length)),
//actions: [ //actions: [
// // NOTE: Maybe add a search icon // // NOTE: Maybe add a search icon
// // and general search functionality here // // and general search functionality here
@ -166,29 +166,46 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
leading: const Icon( leading: const Icon(
Icons.supervisor_account), Icons.supervisor_account),
title: Text(item.isAdmin title: Text(item.isAdmin
? AppLocalizations.of(context)!.removeAdminTitle ? AppLocalizations.of(
: AppLocalizations.of(context)!.makeAdminTitle), context)!
.removeAdminTitle
: AppLocalizations.of(
context)!
.makeAdminTitle),
subtitle: Text(item.isAdmin subtitle: Text(item.isAdmin
? AppLocalizations.of(context)!.removeAdminSubtitle ? AppLocalizations.of(
: AppLocalizations.of(context)!.makeAdminSubtitle), context)!
.removeAdminSubtitle
: AppLocalizations.of(
context)!
.makeAdminSubtitle),
onTap: () { onTap: () {
// make user admin // make user admin
showDialog( showDialog(
context: context, context: context,
builder: builder:
(context) => (ctx) =>
AlertDialog( AlertDialog(
icon: const Icon( icon: const Icon(
Icons Icons
.supervisor_account), .supervisor_account),
title: Text(item title: Text(item.isAdmin
.isAdmin ? AppLocalizations.of(
? AppLocalizations.of(context)!.removeAdminTitle context)!
: AppLocalizations.of(context)!.makeAdminTitle), .removeAdminTitle
: AppLocalizations.of(
context)!
.makeAdminTitle),
content: Text(item content: Text(item
.isAdmin .isAdmin
? AppLocalizations.of(context)!.removeAdminConfirm(item.humanReadableName) ? AppLocalizations.of(
: AppLocalizations.of(context)!.makeAdminConfirm(item.humanReadableName)), context)!
.removeAdminConfirm(item
.humanReadableName)
: AppLocalizations.of(
context)!
.makeAdminConfirm(
item.humanReadableName)),
actions: [ actions: [
TextButton( TextButton(
onPressed: onPressed:
@ -196,10 +213,12 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
// close popup // close popup
// NOTE: cancel only closes the dialog // NOTE: cancel only closes the dialog
// whilst OK closes both // whilst OK closes both
Navigator.of(context) Navigator.of(ctx)
.pop(); .pop();
}, },
child: Text(AppLocalizations.of(context)!.cancel), child: Text(
AppLocalizations.of(context)!
.cancel),
), ),
FilledButton( FilledButton(
onPressed: onPressed:
@ -208,6 +227,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
final scaffMgr = final scaffMgr =
ScaffoldMessenger.of(context); ScaffoldMessenger.of(context);
final nav = final nav =
Navigator.of(ctx);
final nav2 =
Navigator.of(context); Navigator.of(context);
final user = final user =
context.read<User>(); context.read<User>();
@ -228,10 +249,13 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
after: () { after: () {
// close popup // close popup
nav.pop(); nav.pop();
nav.pop(); // close bottom sheet
nav2.pop();
}); });
}, },
child: Text(AppLocalizations.of(context)!.ok), child: Text(
AppLocalizations.of(context)!
.ok),
) )
], ],
)); ));
@ -249,8 +273,14 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
ListTile( ListTile(
leading: const Icon( leading: const Icon(
Icons.person_remove), Icons.person_remove),
title: Text(AppLocalizations.of(context)!.kickUserTitle), title: Text(
subtitle: Text(AppLocalizations.of(context)!.kickUserSubtitle), AppLocalizations.of(
context)!
.kickUserTitle),
subtitle: Text(
AppLocalizations.of(
context)!
.kickUserSubtitle),
onTap: () { onTap: () {
// remove user from room // remove user from room
showDialog( showDialog(
@ -261,10 +291,13 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
icon: const Icon( icon: const Icon(
Icons Icons
.person_remove), .person_remove),
title: Text( title: Text(AppLocalizations.of(
AppLocalizations.of(context)!.kickUserTitle), context)!
content: Text( .kickUserTitle),
AppLocalizations.of(context)!.kichUserConfirm(item.humanReadableName)), content: Text(AppLocalizations.of(
context)!
.kichUserConfirm(
item.humanReadableName)),
actions: [ actions: [
TextButton( TextButton(
onPressed: onPressed:
@ -277,7 +310,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
.pop(); .pop();
}, },
child: Text( child: Text(
AppLocalizations.of(context)!.cancel), AppLocalizations.of(context)!
.cancel),
), ),
FilledButton( FilledButton(
onPressed: onPressed:
@ -287,6 +321,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
ScaffoldMessenger.of(ctx); ScaffoldMessenger.of(ctx);
final nav = final nav =
Navigator.of(ctx); Navigator.of(ctx);
final nav2 =
Navigator.of(context);
final user = final user =
context.read<User>(); context.read<User>();
@ -305,11 +341,13 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
after: () { after: () {
// close popup // close popup
nav.pop(); nav.pop();
nav.pop(); // close bottom sheet
nav2.pop();
}); });
}, },
child: Text( child: Text(
AppLocalizations.of(context)!.ok), AppLocalizations.of(context)!
.ok),
) )
], ],
)); ));
@ -321,7 +359,8 @@ class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
), ),
), ),
FilledButton( FilledButton(
child: Text(AppLocalizations.of(context)!.close), child: Text(
AppLocalizations.of(context)!.close),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },

View file

@ -45,9 +45,12 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
final resp = json['data'] final resp = json['data']
.map<RoomCategory>((raw) => RoomCategory.fromJSON(raw)) .map<RoomCategory>((raw) => RoomCategory.fromJSON(raw))
.toList(); .toList();
if (mounted) {
setState(() { setState(() {
list = resp; list = resp;
}); });
}
}); });
} }
@ -72,9 +75,7 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
RoomPermission.editRoomContent != RoomPermission.editRoomContent !=
0)) 0))
? ReorderableDragStartListener( ? ReorderableDragStartListener(
index: index, index: index, child: const Icon(Icons.drag_handle))
child: const Icon(Icons.drag_handle)
)
: null, : null,
title: Text(item.name), title: Text(item.name),
onTap: () { onTap: () {
@ -201,11 +202,9 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
}, },
itemCount: list.length, itemCount: list.length,
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)! & ((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;
@ -235,9 +234,9 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
), ),
floatingActionButton: ((widget.info?.isAdmin ?? false) || floatingActionButton: ((widget.info?.isAdmin ?? false) ||
(widget.info?.isOwner ?? false) || (widget.info?.isOwner ?? false) ||
((widget.info?.permissions)! & ((widget.info?.permissions)! & RoomPermission.editRoomContent !=
RoomPermission.editRoomContent != 0))
0))?FloatingActionButton.extended( ? FloatingActionButton.extended(
icon: const Icon(Icons.add), icon: const Icon(Icons.add),
label: Text(AppLocalizations.of(context)!.newCategoryShort), label: Text(AppLocalizations.of(context)!.newCategoryShort),
tooltip: AppLocalizations.of(context)!.newCategoryLong, tooltip: AppLocalizations.of(context)!.newCategoryLong,
@ -248,7 +247,8 @@ class _RoomCategoriesPageState extends State<RoomCategoriesPage> {
'id': widget.room!.id, 'id': widget.room!.id,
}); });
}, },
):null, )
: null,
); );
} }
} }

View file

@ -58,12 +58,14 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
// TODO: cache items // TODO: cache items
if (mounted) {
setState(() { setState(() {
list = l; list = l;
cart = c; cart = c;
sortAll(); sortAll();
}); });
}
}); });
} }
@ -125,11 +127,14 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
for (int i = 0; i < resp.length; i++) { for (int i = 0; i < resp.length; i++) {
map[resp[i].id] = i; map[resp[i].id] = i;
} }
if (mounted) {
setState(() { setState(() {
weights = map; weights = map;
categories = resp; categories = resp;
sortAll(); sortAll();
}); });
}
}); });
} }
@ -149,9 +154,11 @@ class _ShoppingListPageState extends State<ShoppingListPage> {
.map<RoomProduct>((raw) => RoomProduct.fromJSON(raw)) .map<RoomProduct>((raw) => RoomProduct.fromJSON(raw))
.toList(); .toList();
if (mounted) {
setState(() { setState(() {
products = resp; products = resp;
}); });
}
}); });
} }
@ -352,7 +359,9 @@ class ShoppingListItem extends StatelessWidget {
enabled: !inCart, enabled: !inCart,
title: Text(name), title: Text(name),
subtitle: Text(description), subtitle: Text(description),
trailing: CategoryChip(category: category,), trailing: CategoryChip(
category: category,
),
onTap: () { onTap: () {
if (onTap != null) { if (onTap != null) {
onTap!(); onTap!();
@ -396,8 +405,9 @@ class ShoppingListItemInfo extends StatelessWidget {
selected: item.category, selected: item.category,
enabled: false), enabled: false),
ProductPicker( ProductPicker(
label:AppLocalizations.of(context)!.selectLinkedProductLabel, label:
help:AppLocalizations.of(context)!.selectLinkedProductHelp, AppLocalizations.of(context)!.selectLinkedProductLabel,
help: AppLocalizations.of(context)!.selectLinkedProductHelp,
products: products, products: products,
selected: item.link, selected: item.link,
enabled: false) enabled: false)

View file

@ -37,9 +37,11 @@ class _RoomProductsPageState extends State<RoomProductsPage> {
// TODO: cache products // TODO: cache products
if (mounted) {
setState(() { setState(() {
products = resp; products = resp;
}); });
}
}); });
} }