design: Improve user permission settings
This commit is contained in:
parent
ac7e424b7b
commit
216d3bd403
5 changed files with 304 additions and 317 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue