actions-test/lib/screens/room/about/members.dart
Jakob Meier 5b398eb2ae
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)
2023-04-05 08:54:48 +02:00

378 lines
16 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:outbag_app/backend/permissions.dart';
import 'package:outbag_app/backend/request.dart';
import 'package:outbag_app/backend/room.dart';
import 'package:outbag_app/backend/user.dart';
import 'package:outbag_app/tools/fetch_wrapper.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class ManageRoomMembersPage extends StatefulWidget {
final String server;
final String tag;
const ManageRoomMembersPage(this.server, this.tag, {super.key});
@override
State<StatefulWidget> createState() => _ManageRoomMembersPageState();
}
class _ManageRoomMembersPageState extends State<ManageRoomMembersPage> {
List<RoomMember> list = [];
RoomInfo? info;
void fetchUserInfo() {
final router = GoRouter.of(context);
final sm = ScaffoldMessenger.of(context);
final user = context.read<User>();
doNetworkRequest(
sm,
req: () => postWithCreadentials(
path: 'getRoomInfo',
credentials: user,
target: user.server,
body: {'room': widget.tag, 'server': widget.server}),
onAnyErr: () {
// user should not be here
// close screen
router.pushReplacementNamed('home');
return false;
},
onOK: (body) async {
final info = RoomInfo.fromJSON(body['data']);
setState(() {
this.info = info;
});
return true;
},
);
}
void fetchMembers() {
final router = GoRouter.of(context);
final sm = ScaffoldMessenger.of(context);
final user = context.read<User>();
doNetworkRequest(sm,
req: () => postWithCreadentials(
credentials: user,
target: user.server,
path: 'getRoomMembers',
body: {'room': widget.tag, 'server': widget.server}),
onAnyErr: () {
// user should not be here
// close screen
router.pushReplacementNamed('home');
return false;
},
onOK: (body) {
final List<RoomMember> list = body['data'].map<RoomMember>((json) {
return RoomMember.fromJSON(json);
}).toList();
setState(() {
this.list = list;
});
});
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
fetchUserInfo();
fetchMembers();
});
}
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context)
.textTheme
.apply(displayColor: Theme.of(context).colorScheme.onSurface);
return Scaffold(
appBar: AppBar(
title:
Text(AppLocalizations.of(context)!.roomMembersTitle(list.length)),
//actions: [
// // NOTE: Maybe add a search icon
// // and general search functionality here
//],
),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
final item = list[index];
String role = AppLocalizations.of(context)!.roleMember;
if (info != null &&
(info?.owner)! == item.id &&
widget.server == item.serverTag) {
role = AppLocalizations.of(context)!.roleOwner;
} else if (item.isAdmin) {
role = AppLocalizations.of(context)!.roleAdmin;
}
bool enable = true;
// perform permission check
if (info == null ||
!((info?.isAdmin)! ||
(info?.isOwner)! ||
((info?.permissions)! & oB("1100000") != 0))) {
// NOTE: do not show error message
// user should assume,
// that it wasn't even possible
// to click on ListTile
enable = false;
} else if (info != null &&
item.id == info?.owner &&
widget.server == item.serverTag) {
// cannot kick admin
enable = false;
}
return ListTile(
title: Text(item.humanReadableName),
subtitle: Text(role),
leading: const Icon(Icons.person),
onTap: !enable
? null
: () {
showModalBottomSheet(
context: context,
builder: (context) => BottomSheet(
onClosing: () {},
builder: (context) => Column(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Text(item.humanReadableName,
style: textTheme.displaySmall)),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
...((info?.isAdmin)! ||
(info?.isOwner)! ||
((info?.permissions)! &
RoomPermission
.changeAdmin !=
0))
? [
ListTile(
leading: const Icon(
Icons.supervisor_account),
title: Text(item.isAdmin
? AppLocalizations.of(
context)!
.removeAdminTitle
: AppLocalizations.of(
context)!
.makeAdminTitle),
subtitle: Text(item.isAdmin
? AppLocalizations.of(
context)!
.removeAdminSubtitle
: AppLocalizations.of(
context)!
.makeAdminSubtitle),
onTap: () {
// make user admin
showDialog(
context: context,
builder:
(ctx) =>
AlertDialog(
icon: const Icon(
Icons
.supervisor_account),
title: Text(item.isAdmin
? AppLocalizations.of(
context)!
.removeAdminTitle
: AppLocalizations.of(
context)!
.makeAdminTitle),
content: Text(item
.isAdmin
? AppLocalizations.of(
context)!
.removeAdminConfirm(item
.humanReadableName)
: AppLocalizations.of(
context)!
.makeAdminConfirm(
item.humanReadableName)),
actions: [
TextButton(
onPressed:
() {
// close popup
// NOTE: cancel only closes the dialog
// whilst OK closes both
Navigator.of(ctx)
.pop();
},
child: Text(
AppLocalizations.of(context)!
.cancel),
),
FilledButton(
onPressed:
() async {
// send request
final scaffMgr =
ScaffoldMessenger.of(context);
final nav =
Navigator.of(ctx);
final nav2 =
Navigator.of(context);
final user =
context.read<User>();
doNetworkRequest(
scaffMgr,
req: () =>
postWithCreadentials(path: 'setAdminStatus', credentials: user, target: user.server, body: {
'room': widget.tag,
'roomServer': widget.server,
'server': item.serverTag,
'name': item.id,
'admin': !item.isAdmin
}),
onOK: (_) {
fetchMembers();
},
after: () {
// close popup
nav.pop();
// close bottom sheet
nav2.pop();
});
},
child: Text(
AppLocalizations.of(context)!
.ok),
)
],
));
},
)
]
: [],
...((info?.isAdmin)! ||
(info?.isOwner)! ||
((info?.permissions)! &
RoomPermission
.manageMembers !=
0))
? [
ListTile(
leading: const Icon(
Icons.person_remove),
title: Text(
AppLocalizations.of(
context)!
.kickUserTitle),
subtitle: Text(
AppLocalizations.of(
context)!
.kickUserSubtitle),
onTap: () {
// remove user from room
showDialog(
context: context,
builder:
(ctx) =>
AlertDialog(
icon: const Icon(
Icons
.person_remove),
title: Text(AppLocalizations.of(
context)!
.kickUserTitle),
content: Text(AppLocalizations.of(
context)!
.kichUserConfirm(
item.humanReadableName)),
actions: [
TextButton(
onPressed:
() {
// close popup
// NOTE: cancel only closes the dialog
// whilst OK closes both
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 nav2 =
Navigator.of(context);
final user =
context.read<User>();
doNetworkRequest(
scaffMgr,
req: () =>
postWithCreadentials(path: 'kickMember', credentials: user, target: user.server, body: {
'room': widget.tag,
'roomServer': widget.server,
'name': item.id,
'server': item.serverTag
}),
onOK: (_) {
fetchMembers();
},
after: () {
// close popup
nav.pop();
// close bottom sheet
nav2.pop();
});
},
child: Text(
AppLocalizations.of(context)!
.ok),
)
],
));
},
)
]
: [],
],
),
),
FilledButton(
child: Text(
AppLocalizations.of(context)!.close),
onPressed: () {
Navigator.of(context).pop();
},
)
],
),
));
},
);
},
itemCount: list.length,
),
);
}
}