mirror of
https://github.com/krille-chan/fluffychat
synced 2024-09-17 08:55:12 +00:00
design: Improve user permission settings
This commit is contained in:
parent
ac7e424b7b
commit
216d3bd403
5 changed files with 312 additions and 325 deletions
|
@ -2525,6 +2525,7 @@
|
||||||
"@thisDevice": {},
|
"@thisDevice": {},
|
||||||
"initAppError": "An error occured while init the app",
|
"initAppError": "An error occured while init the app",
|
||||||
"@initAppError": {},
|
"@initAppError": {},
|
||||||
|
"userRole": "User role",
|
||||||
"minimumPowerLevel": "{level} is the minimum power level.",
|
"minimumPowerLevel": "{level} is the minimum power level.",
|
||||||
"@minimumPowerLevel": {
|
"@minimumPowerLevel": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:developer';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:go_router/go_router.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/pages/chat_permissions_settings/chat_permissions_settings_view.dart';
|
||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
import 'package:fluffychat/widgets/permission_slider_dialog.dart';
|
||||||
|
|
||||||
class ChatPermissionsSettings extends StatefulWidget {
|
class ChatPermissionsSettings extends StatefulWidget {
|
||||||
const ChatPermissionsSettings({super.key});
|
const ChatPermissionsSettings({super.key});
|
||||||
|
@ -35,30 +35,9 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
newLevel ??= int.tryParse(
|
newLevel ??= await showPermissionChooser(
|
||||||
(await showTextInputDialog(
|
context,
|
||||||
context: context,
|
currentLevel: currentLevel,
|
||||||
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 ??
|
|
||||||
'',
|
|
||||||
);
|
);
|
||||||
if (newLevel == null) return;
|
if (newLevel == null) return;
|
||||||
final content = Map<String, dynamic>.from(
|
final content = Map<String, dynamic>.from(
|
||||||
|
|
|
@ -17,7 +17,6 @@ enum UserBottomSheetAction {
|
||||||
ban,
|
ban,
|
||||||
kick,
|
kick,
|
||||||
unban,
|
unban,
|
||||||
permission,
|
|
||||||
message,
|
message,
|
||||||
ignore,
|
ignore,
|
||||||
}
|
}
|
||||||
|
@ -201,30 +200,6 @@ class UserBottomSheetController extends State<UserBottomSheet> {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case UserBottomSheetAction.message:
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
// Workaround for https://github.com/flutter/flutter/issues/27495
|
// Workaround for https://github.com/flutter/flutter/issues/27495
|
||||||
|
@ -270,6 +245,35 @@ class UserBottomSheetController extends State<UserBottomSheet> {
|
||||||
Navigator.of(context).pop();
|
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
|
@override
|
||||||
Widget build(BuildContext context) => UserBottomSheetView(this);
|
Widget build(BuildContext context) => UserBottomSheetView(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,228 +104,277 @@ class UserBottomSheetView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: StreamBuilder<Object>(
|
||||||
children: [
|
stream: user?.room.client.onSync.stream.where(
|
||||||
if (user?.membership == Membership.knock)
|
(syncUpdate) =>
|
||||||
Padding(
|
syncUpdate.rooms?.join?[user.room.id]?.timeline?.events?.any(
|
||||||
padding: const EdgeInsets.all(12.0),
|
(state) => state.type == EventTypes.RoomPowerLevels,
|
||||||
child: Material(
|
) ??
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
false,
|
||||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
),
|
||||||
child: ListTile(
|
builder: (context, snapshot) {
|
||||||
minVerticalPadding: 16,
|
return ListView(
|
||||||
title: Padding(
|
children: [
|
||||||
padding: const EdgeInsets.only(bottom: 12.0),
|
if (user?.membership == Membership.knock)
|
||||||
child: Text(
|
Padding(
|
||||||
L10n.of(context)!
|
padding: const EdgeInsets.all(12.0),
|
||||||
.userWouldLikeToChangeTheChat(displayname),
|
child: Material(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(AppConfig.borderRadius),
|
||||||
|
child: ListTile(
|
||||||
|
minVerticalPadding: 16,
|
||||||
|
title: Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 12.0),
|
||||||
|
child: Text(
|
||||||
|
L10n.of(context)!
|
||||||
|
.userWouldLikeToChangeTheChat(displayname),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Row(
|
||||||
|
children: [
|
||||||
|
TextButton.icon(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).colorScheme.background,
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
onPressed: controller.knockAccept,
|
||||||
|
icon: const Icon(Icons.check_outlined),
|
||||||
|
label: Text(L10n.of(context)!.accept),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
TextButton.icon(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.errorContainer,
|
||||||
|
foregroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onErrorContainer,
|
||||||
|
),
|
||||||
|
onPressed: controller.knockDecline,
|
||||||
|
icon: const Icon(Icons.cancel_outlined),
|
||||||
|
label: Text(L10n.of(context)!.decline),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: Row(
|
),
|
||||||
children: [
|
Row(
|
||||||
TextButton.icon(
|
children: [
|
||||||
style: TextButton.styleFrom(
|
Padding(
|
||||||
backgroundColor:
|
padding: const EdgeInsets.all(16.0),
|
||||||
Theme.of(context).colorScheme.background,
|
child: Material(
|
||||||
foregroundColor:
|
elevation: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.primary,
|
.appBarTheme
|
||||||
|
.scrolledUnderElevation ??
|
||||||
|
4,
|
||||||
|
shadowColor: Theme.of(context).appBarTheme.shadowColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
side: BorderSide(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Avatar.defaultSize * 2.5,
|
||||||
),
|
),
|
||||||
onPressed: controller.knockAccept,
|
|
||||||
icon: const Icon(Icons.check_outlined),
|
|
||||||
label: Text(L10n.of(context)!.accept),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
child: Avatar(
|
||||||
TextButton.icon(
|
mxContent: avatarUrl,
|
||||||
style: TextButton.styleFrom(
|
name: displayname,
|
||||||
backgroundColor:
|
size: Avatar.defaultSize * 2.5,
|
||||||
Theme.of(context).colorScheme.errorContainer,
|
fontSize: 18 * 2.5,
|
||||||
foregroundColor:
|
),
|
||||||
Theme.of(context).colorScheme.onErrorContainer,
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () => FluffyShare.share(
|
||||||
|
'https://matrix.to/#/$userId',
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
icon: Icon(
|
||||||
|
Icons.adaptive.share_outlined,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.onBackground,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
displayname,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
// style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: controller.knockDecline,
|
TextButton.icon(
|
||||||
icon: const Icon(Icons.cancel_outlined),
|
onPressed: () => FluffyShare.share(
|
||||||
label: Text(L10n.of(context)!.decline),
|
userId,
|
||||||
|
context,
|
||||||
|
copyOnly: true,
|
||||||
|
),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.copy_outlined,
|
||||||
|
size: 14,
|
||||||
|
),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
userId,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
// style: const TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (userId != client.userID)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 8.0,
|
||||||
|
),
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () => controller
|
||||||
|
.participantAction(UserBottomSheetAction.message),
|
||||||
|
icon: const Icon(Icons.forum_outlined),
|
||||||
|
label: Text(
|
||||||
|
controller.widget.user == null
|
||||||
|
? L10n.of(context)!.startConversation
|
||||||
|
: L10n.of(context)!.sendAMessage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceBuilder(
|
||||||
|
userId: userId,
|
||||||
|
client: client,
|
||||||
|
builder: (context, presence) {
|
||||||
|
final status = presence?.statusMsg;
|
||||||
|
if (status == null || status.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return ListTile(
|
||||||
|
title: SelectableLinkify(
|
||||||
|
text: status,
|
||||||
|
style: const TextStyle(fontSize: 16),
|
||||||
|
options: const LinkifyOptions(humanize: false),
|
||||||
|
linkStyle: const TextStyle(
|
||||||
|
color: Colors.blueAccent,
|
||||||
|
decorationColor: Colors.blueAccent,
|
||||||
|
),
|
||||||
|
onOpen: (url) =>
|
||||||
|
UrlLauncher(context, url.url).launchUrl(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (controller.widget.onMention != null)
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.alternate_email_outlined),
|
||||||
|
title: Text(L10n.of(context)!.mention),
|
||||||
|
onTap: () => controller
|
||||||
|
.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),
|
||||||
),
|
],
|
||||||
Row(
|
if (user != null && user.canKick)
|
||||||
children: [
|
ListTile(
|
||||||
Padding(
|
textColor: Theme.of(context).colorScheme.error,
|
||||||
padding: const EdgeInsets.all(16.0),
|
iconColor: Theme.of(context).colorScheme.error,
|
||||||
child: Material(
|
title: Text(L10n.of(context)!.kickFromChat),
|
||||||
elevation:
|
leading: const Icon(Icons.exit_to_app_outlined),
|
||||||
Theme.of(context).appBarTheme.scrolledUnderElevation ??
|
onTap: () => controller
|
||||||
4,
|
.participantAction(UserBottomSheetAction.kick),
|
||||||
shadowColor: Theme.of(context).appBarTheme.shadowColor,
|
),
|
||||||
shape: RoundedRectangleBorder(
|
if (user != null &&
|
||||||
side: BorderSide(
|
user.canBan &&
|
||||||
color: Theme.of(context).dividerColor,
|
user.membership != Membership.ban)
|
||||||
),
|
ListTile(
|
||||||
borderRadius: BorderRadius.circular(
|
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
Avatar.defaultSize * 2.5,
|
iconColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
),
|
title: Text(L10n.of(context)!.banFromChat),
|
||||||
|
leading: const Icon(Icons.warning_sharp),
|
||||||
|
onTap: () =>
|
||||||
|
controller.participantAction(UserBottomSheetAction.ban),
|
||||||
|
)
|
||||||
|
else if (user != null &&
|
||||||
|
user.canBan &&
|
||||||
|
user.membership == Membership.ban)
|
||||||
|
ListTile(
|
||||||
|
title: Text(L10n.of(context)!.unbanFromChat),
|
||||||
|
leading: const Icon(Icons.warning_outlined),
|
||||||
|
onTap: () => controller
|
||||||
|
.participantAction(UserBottomSheetAction.unban),
|
||||||
|
),
|
||||||
|
if (user != null && user.id != client.userID)
|
||||||
|
ListTile(
|
||||||
|
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
|
iconColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
|
title: Text(L10n.of(context)!.reportUser),
|
||||||
|
leading: const Icon(Icons.report_outlined),
|
||||||
|
onTap: () => controller
|
||||||
|
.participantAction(UserBottomSheetAction.report),
|
||||||
|
),
|
||||||
|
if (profileSearchError != null)
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(
|
||||||
|
Icons.warning_outlined,
|
||||||
|
color: Colors.orange,
|
||||||
),
|
),
|
||||||
child: Avatar(
|
subtitle: Text(
|
||||||
mxContent: avatarUrl,
|
L10n.of(context)!.profileNotFound,
|
||||||
name: displayname,
|
style: const TextStyle(color: Colors.orange),
|
||||||
size: Avatar.defaultSize * 2.5,
|
|
||||||
fontSize: 18 * 2.5,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
TextButton.icon(
|
|
||||||
onPressed: () => FluffyShare.share(
|
|
||||||
'https://matrix.to/#/$userId',
|
|
||||||
context,
|
|
||||||
),
|
|
||||||
icon: Icon(
|
|
||||||
Icons.adaptive.share_outlined,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).colorScheme.onBackground,
|
|
||||||
),
|
|
||||||
label: Text(
|
|
||||||
displayname,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
// style: const TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton.icon(
|
|
||||||
onPressed: () => FluffyShare.share(
|
|
||||||
userId,
|
|
||||||
context,
|
|
||||||
copyOnly: true,
|
|
||||||
),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.copy_outlined,
|
|
||||||
size: 14,
|
|
||||||
),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
label: Text(
|
|
||||||
userId,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
// style: const TextStyle(fontSize: 12),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
);
|
||||||
if (userId != client.userID)
|
},
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16.0,
|
|
||||||
vertical: 8.0,
|
|
||||||
),
|
|
||||||
child: ElevatedButton.icon(
|
|
||||||
onPressed: () => controller
|
|
||||||
.participantAction(UserBottomSheetAction.message),
|
|
||||||
icon: const Icon(Icons.forum_outlined),
|
|
||||||
label: Text(
|
|
||||||
controller.widget.user == null
|
|
||||||
? L10n.of(context)!.startConversation
|
|
||||||
: L10n.of(context)!.sendAMessage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PresenceBuilder(
|
|
||||||
userId: userId,
|
|
||||||
client: client,
|
|
||||||
builder: (context, presence) {
|
|
||||||
final status = presence?.statusMsg;
|
|
||||||
if (status == null || status.isEmpty) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
return ListTile(
|
|
||||||
title: SelectableLinkify(
|
|
||||||
text: status,
|
|
||||||
style: const TextStyle(fontSize: 16),
|
|
||||||
options: const LinkifyOptions(humanize: false),
|
|
||||||
linkStyle: const TextStyle(
|
|
||||||
color: Colors.blueAccent,
|
|
||||||
decorationColor: Colors.blueAccent,
|
|
||||||
),
|
|
||||||
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (controller.widget.onMention != null)
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
if (user != null && user.canKick)
|
|
||||||
ListTile(
|
|
||||||
title: Text(L10n.of(context)!.kickFromChat),
|
|
||||||
leading: const Icon(Icons.exit_to_app_outlined),
|
|
||||||
onTap: () =>
|
|
||||||
controller.participantAction(UserBottomSheetAction.kick),
|
|
||||||
),
|
|
||||||
if (user != null &&
|
|
||||||
user.canBan &&
|
|
||||||
user.membership != Membership.ban)
|
|
||||||
ListTile(
|
|
||||||
title: Text(L10n.of(context)!.banFromChat),
|
|
||||||
leading: const Icon(Icons.warning_sharp),
|
|
||||||
onTap: () =>
|
|
||||||
controller.participantAction(UserBottomSheetAction.ban),
|
|
||||||
)
|
|
||||||
else if (user != null &&
|
|
||||||
user.canBan &&
|
|
||||||
user.membership == Membership.ban)
|
|
||||||
ListTile(
|
|
||||||
title: Text(L10n.of(context)!.unbanFromChat),
|
|
||||||
leading: const Icon(Icons.warning_outlined),
|
|
||||||
onTap: () =>
|
|
||||||
controller.participantAction(UserBottomSheetAction.unban),
|
|
||||||
),
|
|
||||||
if (user != null && user.id != client.userID)
|
|
||||||
ListTile(
|
|
||||||
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
|
||||||
iconColor: Theme.of(context).colorScheme.onErrorContainer,
|
|
||||||
title: Text(L10n.of(context)!.reportUser),
|
|
||||||
leading: const Icon(Icons.report_outlined),
|
|
||||||
onTap: () =>
|
|
||||||
controller.participantAction(UserBottomSheetAction.report),
|
|
||||||
),
|
|
||||||
if (profileSearchError != null)
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(
|
|
||||||
Icons.warning_outlined,
|
|
||||||
color: Colors.orange,
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
L10n.of(context)!.profileNotFound,
|
|
||||||
style: const TextStyle(color: Colors.orange),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,77 +4,31 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.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(
|
Future<int?> showPermissionChooser(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
int currentLevel = 0,
|
int currentLevel = 0,
|
||||||
}) async {
|
}) async {
|
||||||
final permissionLevel = await showConfirmationDialog(
|
final customLevel = await showTextInputDialog(
|
||||||
context: context,
|
context: context,
|
||||||
title: L10n.of(context)!.setPermissionsLevel,
|
title: L10n.of(context)!.setPermissionsLevel,
|
||||||
actions: PermissionLevel.values
|
textFields: [
|
||||||
.map(
|
DialogTextField(
|
||||||
(level) => AlertDialogAction(
|
initialText: currentLevel.toString(),
|
||||||
key: level,
|
keyboardType: TextInputType.number,
|
||||||
label: level.toLocalizedString(context),
|
autocorrect: false,
|
||||||
),
|
validator: (text) {
|
||||||
)
|
if (text == null) {
|
||||||
.toList(),
|
return L10n.of(context)!.pleaseEnterANumber;
|
||||||
|
}
|
||||||
|
final level = int.tryParse(text);
|
||||||
|
if (level == null || level < 0) {
|
||||||
|
return L10n.of(context)!.pleaseEnterANumber;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
if (permissionLevel == null) return null;
|
if (customLevel == null) return null;
|
||||||
|
return int.tryParse(customLevel.first);
|
||||||
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,
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
if (customLevel == null) return null;
|
|
||||||
return int.tryParse(customLevel.first);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue