design: Improve user permission settings

This commit is contained in:
krille-chan 2024-04-15 09:50:50 +02:00
parent ac7e424b7b
commit 216d3bd403
No known key found for this signature in database
5 changed files with 312 additions and 325 deletions

View file

@ -2525,6 +2525,7 @@
"@thisDevice": {},
"initAppError": "An error occured while init the app",
"@initAppError": {},
"userRole": "User role",
"minimumPowerLevel": "{level} is the minimum power level.",
"@minimumPowerLevel": {
"type": "text",

View file

@ -2,7 +2,6 @@ import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:go_router/go_router.dart';
@ -10,6 +9,7 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/chat_permissions_settings/chat_permissions_settings_view.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/permission_slider_dialog.dart';
class ChatPermissionsSettings extends StatefulWidget {
const ChatPermissionsSettings({super.key});
@ -35,30 +35,9 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
);
return;
}
newLevel ??= int.tryParse(
(await showTextInputDialog(
context: context,
title: L10n.of(context)!.setPermissionsLevel,
textFields: [
DialogTextField(
initialText: currentLevel.toString(),
keyboardType: TextInputType.number,
autocorrect: false,
validator: (text) {
if (text == null) {
return L10n.of(context)!.pleaseEnterANumber;
}
final level = int.tryParse(text);
if (level == null || level < 0) {
return L10n.of(context)!.pleaseEnterANumber;
}
return null;
},
),
],
))
?.singleOrNull ??
'',
newLevel ??= await showPermissionChooser(
context,
currentLevel: currentLevel,
);
if (newLevel == null) return;
final content = Map<String, dynamic>.from(

View file

@ -17,7 +17,6 @@ enum UserBottomSheetAction {
ban,
kick,
unban,
permission,
message,
ignore,
}
@ -201,30 +200,6 @@ class UserBottomSheetController extends State<UserBottomSheet> {
Navigator.of(context).pop();
}
break;
case UserBottomSheetAction.permission:
if (user == null) throw ('User must not be null for this action!');
final newPermission = await showPermissionChooser(
context,
currentLevel: user.powerLevel,
);
if (newPermission != null) {
if (newPermission == 100 &&
await showOkCancelAlertDialog(
useRootNavigator: false,
context: context,
title: L10n.of(context)!.areYouSure,
okLabel: L10n.of(context)!.yes,
cancelLabel: L10n.of(context)!.no,
message: L10n.of(context)!.makeAdminDescription,
) !=
OkCancelResult.ok) break;
await showFutureLoadingDialog(
context: context,
future: () => user.setPower(newPermission),
);
Navigator.of(context).pop();
}
break;
case UserBottomSheetAction.message:
Navigator.of(context).pop();
// Workaround for https://github.com/flutter/flutter/issues/27495
@ -270,6 +245,35 @@ class UserBottomSheetController extends State<UserBottomSheet> {
Navigator.of(context).pop();
}
void setPowerLevel(int? newLevel) async {
final user = widget.user;
if (user == null) throw ('User must not be null for this action!');
final level = newLevel ??
await showPermissionChooser(
context,
currentLevel: user.powerLevel,
);
if (level == null) return;
if (level == 100) {
final consent = await showOkCancelAlertDialog(
useRootNavigator: false,
context: context,
title: L10n.of(context)!.areYouSure,
okLabel: L10n.of(context)!.yes,
cancelLabel: L10n.of(context)!.no,
message: L10n.of(context)!.makeAdminDescription,
);
if (consent != OkCancelResult.ok) return;
}
await showFutureLoadingDialog(
context: context,
future: () => user.setPower(level),
);
}
@override
Widget build(BuildContext context) => UserBottomSheetView(this);
}

View file

@ -104,14 +104,24 @@ class UserBottomSheetView extends StatelessWidget {
),
],
),
body: ListView(
body: StreamBuilder<Object>(
stream: user?.room.client.onSync.stream.where(
(syncUpdate) =>
syncUpdate.rooms?.join?[user.room.id]?.timeline?.events?.any(
(state) => state.type == EventTypes.RoomPowerLevels,
) ??
false,
),
builder: (context, snapshot) {
return ListView(
children: [
if (user?.membership == Membership.knock)
Padding(
padding: const EdgeInsets.all(12.0),
child: Material(
color: Theme.of(context).colorScheme.surfaceVariant,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
borderRadius:
BorderRadius.circular(AppConfig.borderRadius),
child: ListTile(
minVerticalPadding: 16,
title: Padding(
@ -137,10 +147,12 @@ class UserBottomSheetView extends StatelessWidget {
const SizedBox(width: 12),
TextButton.icon(
style: TextButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.errorContainer,
foregroundColor:
Theme.of(context).colorScheme.onErrorContainer,
backgroundColor: Theme.of(context)
.colorScheme
.errorContainer,
foregroundColor: Theme.of(context)
.colorScheme
.onErrorContainer,
),
onPressed: controller.knockDecline,
icon: const Icon(Icons.cancel_outlined),
@ -156,8 +168,9 @@ class UserBottomSheetView extends StatelessWidget {
Padding(
padding: const EdgeInsets.all(16.0),
child: Material(
elevation:
Theme.of(context).appBarTheme.scrolledUnderElevation ??
elevation: Theme.of(context)
.appBarTheme
.scrolledUnderElevation ??
4,
shadowColor: Theme.of(context).appBarTheme.shadowColor,
shape: RoundedRectangleBorder(
@ -261,7 +274,8 @@ class UserBottomSheetView extends StatelessWidget {
color: Colors.blueAccent,
decorationColor: Colors.blueAccent,
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
onOpen: (url) =>
UrlLauncher(context, url.url).launchUrl(),
),
);
},
@ -270,27 +284,60 @@ class UserBottomSheetView extends StatelessWidget {
ListTile(
leading: const Icon(Icons.alternate_email_outlined),
title: Text(L10n.of(context)!.mention),
onTap: () =>
controller.participantAction(UserBottomSheetAction.mention),
),
if (user != null && user.canChangePowerLevel)
ListTile(
title: Text(L10n.of(context)!.setPermissionsLevel),
leading: const Icon(Icons.edit_attributes_outlined),
onTap: () => controller
.participantAction(UserBottomSheetAction.permission),
.participantAction(UserBottomSheetAction.mention),
),
if (user != null) ...[
Divider(height: 1, color: Theme.of(context).dividerColor),
ListTile(
title: Text(
'${L10n.of(context)!.userRole} (${user.powerLevel})',
),
leading: const Icon(Icons.person_outlined),
trailing: DropdownButton<int>(
onChanged: user.canChangePowerLevel
? controller.setPowerLevel
: null,
value: {0, 50, 100}.contains(user.powerLevel)
? user.powerLevel
: null,
items: [
DropdownMenuItem(
value: 0,
child: Text(L10n.of(context)!.user),
),
DropdownMenuItem(
value: 50,
child: Text(L10n.of(context)!.moderator),
),
DropdownMenuItem(
value: 100,
child: Text(L10n.of(context)!.admin),
),
DropdownMenuItem(
value: null,
child: Text(L10n.of(context)!.custom),
),
],
),
),
Divider(height: 1, color: Theme.of(context).dividerColor),
],
if (user != null && user.canKick)
ListTile(
textColor: Theme.of(context).colorScheme.error,
iconColor: Theme.of(context).colorScheme.error,
title: Text(L10n.of(context)!.kickFromChat),
leading: const Icon(Icons.exit_to_app_outlined),
onTap: () =>
controller.participantAction(UserBottomSheetAction.kick),
onTap: () => controller
.participantAction(UserBottomSheetAction.kick),
),
if (user != null &&
user.canBan &&
user.membership != Membership.ban)
ListTile(
textColor: Theme.of(context).colorScheme.onErrorContainer,
iconColor: Theme.of(context).colorScheme.onErrorContainer,
title: Text(L10n.of(context)!.banFromChat),
leading: const Icon(Icons.warning_sharp),
onTap: () =>
@ -302,8 +349,8 @@ class UserBottomSheetView extends StatelessWidget {
ListTile(
title: Text(L10n.of(context)!.unbanFromChat),
leading: const Icon(Icons.warning_outlined),
onTap: () =>
controller.participantAction(UserBottomSheetAction.unban),
onTap: () => controller
.participantAction(UserBottomSheetAction.unban),
),
if (user != null && user.id != client.userID)
ListTile(
@ -311,8 +358,8 @@ class UserBottomSheetView extends StatelessWidget {
iconColor: Theme.of(context).colorScheme.onErrorContainer,
title: Text(L10n.of(context)!.reportUser),
leading: const Icon(Icons.report_outlined),
onTap: () =>
controller.participantAction(UserBottomSheetAction.report),
onTap: () => controller
.participantAction(UserBottomSheetAction.report),
),
if (profileSearchError != null)
ListTile(
@ -326,6 +373,8 @@ class UserBottomSheetView extends StatelessWidget {
),
),
],
);
},
),
),
);

View file

@ -4,55 +4,10 @@ import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
enum PermissionLevel {
user,
moderator,
admin,
custom,
}
extension on PermissionLevel {
String toLocalizedString(BuildContext context) {
switch (this) {
case PermissionLevel.user:
return L10n.of(context)!.user;
case PermissionLevel.moderator:
return L10n.of(context)!.moderator;
case PermissionLevel.admin:
return L10n.of(context)!.admin;
case PermissionLevel.custom:
default:
return L10n.of(context)!.custom;
}
}
}
Future<int?> showPermissionChooser(
BuildContext context, {
int currentLevel = 0,
}) async {
final permissionLevel = await showConfirmationDialog(
context: context,
title: L10n.of(context)!.setPermissionsLevel,
actions: PermissionLevel.values
.map(
(level) => AlertDialogAction(
key: level,
label: level.toLocalizedString(context),
),
)
.toList(),
);
if (permissionLevel == null) return null;
switch (permissionLevel) {
case PermissionLevel.user:
return 0;
case PermissionLevel.moderator:
return 50;
case PermissionLevel.admin:
return 100;
case PermissionLevel.custom:
final customLevel = await showTextInputDialog(
context: context,
title: L10n.of(context)!.setPermissionsLevel,
@ -77,4 +32,3 @@ Future<int?> showPermissionChooser(
if (customLevel == null) return null;
return int.tryParse(customLevel.first);
}
}