actions-test/lib/screens/room/members.dart
Jakob Meier 90adcc6bb1
Fixed context bugs caused by moving to GoRouter
NOTE: Some bugs have not been fixed, and the member screen needs fixing,
but before that can happen, localstore has to be replaced,
because it causes loops on android and linux.
It also seems like it might lead to memory leaks
2023-03-28 17:21:35 +02:00

349 lines
14 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';
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('Room Members (${list.length})'),
leading: IconButton(
onPressed: () {
// go back
Navigator.of(context).pop();
},
icon: const Icon(Icons.arrow_back),
tooltip: "Go back",
),
//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 = "Member";
if (info != null &&
(info?.owner)! == item.id &&
widget.server == item.serverTag) {
role = "Owner";
} else if (item.isAdmin) {
role = "Admin";
}
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
? 'Remove admin privileges'
: 'Make Admin'),
subtitle: Text(item.isAdmin
? 'Revokes admin privileges from the user'
: 'Grants the user the permission to do everything'),
onTap: () {
// make user admin
showDialog(
context: context,
builder:
(context) =>
AlertDialog(
icon: const Icon(
Icons
.supervisor_account),
title: Text(item
.isAdmin
? 'Remove admin privileges'
: 'Make Admin'),
content: Text(item
.isAdmin
? "Do you really want to remove ${item.humanReadableName}'s admin privileges"
: 'Do you really want to make ${item.humanReadableName} admin?'),
actions: [
TextButton(
onPressed:
() {
// close popup
// NOTE: cancel only closes the dialog
// whilst OK closes both
Navigator.of(context)
.pop();
},
child: const Text(
'Cancel'),
),
FilledButton(
onPressed:
() async {
// send request
final scaffMgr =
ScaffoldMessenger.of(context);
final nav =
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();
nav.pop();
});
},
child: const Text(
'OK'),
)
],
));
},
)
]
: [],
...((info?.isAdmin)! ||
(info?.isOwner)! ||
((info?.permissions)! &
RoomPermission
.manageMembers !=
0))
? [
ListTile(
leading: const Icon(
Icons.person_remove),
title:
const Text('Kick User'),
subtitle: const Text(
"Temporarrily remove user from server (they'll be able to join the room again)"),
onTap: () {
// remove user from room
showDialog(
context: context,
builder:
(ctx) =>
AlertDialog(
icon: const Icon(
Icons
.person_remove),
title: const Text(
'Kick User'),
content: Text(
'Do you really want to kick ${item.humanReadableName}?'),
actions: [
TextButton(
onPressed:
() {
// close popup
// NOTE: cancel only closes the dialog
// whilst OK closes both
Navigator.of(ctx)
.pop();
},
child: const Text(
'Cancel'),
),
FilledButton(
onPressed:
() async {
// send request
final scaffMgr =
ScaffoldMessenger.of(ctx);
final nav =
Navigator.of(ctx);
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();
nav.pop();
});
},
child: const Text(
'Kick User'),
)
],
));
},
)
]
: [],
],
),
),
FilledButton(
child: const Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
),
));
},
);
},
itemCount: list.length,
),
);
}
}