8fffafde47
Translations are provided in *.arb* format. Some keys have descriptions (indicated by leading @-symbol). Descriptions should not be copied into the translation itself. Currently only English is supported (app_en.arb), but German is planned. Apparently weblate merged .arb support at some time, so it would be nice to enable community translations at some point.
339 lines
14 KiB
Dart
339 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';
|
|
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:
|
|
(context) =>
|
|
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(context)
|
|
.pop();
|
|
},
|
|
child: Text(AppLocalizations.of(context)!.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: 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 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: Text(
|
|
AppLocalizations.of(context)!.ok),
|
|
)
|
|
],
|
|
));
|
|
},
|
|
)
|
|
]
|
|
: [],
|
|
],
|
|
),
|
|
),
|
|
FilledButton(
|
|
child: Text(AppLocalizations.of(context)!.close),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
)
|
|
],
|
|
),
|
|
));
|
|
},
|
|
);
|
|
},
|
|
itemCount: list.length,
|
|
),
|
|
);
|
|
}
|
|
}
|