From 001e0ee48fcc8be6eaf66d114be7e9e7d47b8367 Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Thu, 15 Apr 2021 08:48:26 +0200 Subject: [PATCH] refactor: MVC chat permission settings --- lib/config/routes.dart | 2 +- .../chat_permissions_settings_controller.dart | 96 ++++++ lib/views/chat_permissions_settings.dart | 293 ------------------ lib/views/chat_permissions_settings_view.dart | 125 ++++++++ .../list_items/permission_list_tile.dart | 103 ++++++ 5 files changed, 325 insertions(+), 294 deletions(-) create mode 100644 lib/controllers/chat_permissions_settings_controller.dart delete mode 100644 lib/views/chat_permissions_settings.dart create mode 100644 lib/views/chat_permissions_settings_view.dart create mode 100644 lib/views/widgets/list_items/permission_list_tile.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 91b7acbe..c72c5350 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/views/chat.dart'; import 'package:fluffychat/controllers/chat_details_controller.dart'; import 'package:fluffychat/controllers/chat_encryption_settings_controller.dart'; import 'package:fluffychat/controllers/chat_list_controller.dart'; -import 'package:fluffychat/views/chat_permissions_settings.dart'; +import 'package:fluffychat/controllers/chat_permissions_settings_controller.dart'; import 'package:fluffychat/views/empty_page.dart'; import 'package:fluffychat/views/loading_view.dart'; import 'package:fluffychat/views/log_view.dart'; diff --git a/lib/controllers/chat_permissions_settings_controller.dart b/lib/controllers/chat_permissions_settings_controller.dart new file mode 100644 index 00000000..741dc6dc --- /dev/null +++ b/lib/controllers/chat_permissions_settings_controller.dart @@ -0,0 +1,96 @@ +import 'dart:developer'; + +import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:adaptive_page_layout/adaptive_page_layout.dart'; +import 'package:fluffychat/views/chat_permissions_settings_view.dart'; +import 'package:fluffychat/views/widgets/dialogs/permission_slider_dialog.dart'; +import 'package:future_loading_dialog/future_loading_dialog.dart'; +import 'package:fluffychat/views/widgets/matrix.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:famedlysdk/famedlysdk.dart'; + +class ChatPermissionsSettings extends StatefulWidget { + final String roomId; + + const ChatPermissionsSettings(this.roomId, {Key key}) : super(key: key); + + @override + ChatPermissionsSettingsController createState() => + ChatPermissionsSettingsController(); +} + +class ChatPermissionsSettingsController extends State { + void editPowerLevel(BuildContext context, String key, int currentLevel, + {String category}) async { + final room = Matrix.of(context).client.getRoomById(widget.roomId); + if (!room.canSendEvent(EventTypes.RoomPowerLevels)) { + AdaptivePageLayout.of(context) + .showSnackBar(SnackBar(content: Text(L10n.of(context).noPermission))); + return; + } + final newLevel = + await PermissionSliderDialog(initialPermission: currentLevel) + .show(context); + if (newLevel == null) return; + final content = Map.from( + room.getState(EventTypes.RoomPowerLevels).content); + if (category != null) { + if (!content.containsKey(category)) { + content[category] = {}; + } + content[category][key] = newLevel; + } else { + content[key] = newLevel; + } + inspect(content); + await showFutureLoadingDialog( + context: context, + future: () => + room.client.sendState(room.id, EventTypes.RoomPowerLevels, content), + ); + } + + Stream get onChanged => Matrix.of(context).client.onSync.stream.where( + (e) => + (e?.rooms?.join?.containsKey(widget.roomId) ?? false) && + (e.rooms.join[widget.roomId]?.timeline?.events + ?.any((s) => s.type == EventTypes.RoomPowerLevels) ?? + false), + ); + + void updateRoomAction(ServerCapabilities capabilities) async { + final room = Matrix.of(context).client.getRoomById(widget.roomId); + final String roomVersion = + room.getState(EventTypes.RoomCreate).content['room_version'] ?? '1'; + final newVersion = await showConfirmationDialog( + context: context, + title: L10n.of(context).replaceRoomWithNewerVersion, + actions: capabilities.mRoomVersions.available.entries + .where((r) => r.key != roomVersion) + .map((version) => AlertDialogAction( + key: version.key, + label: + '${version.key} (${version.value.toString().split('.').last})')) + .toList(), + ); + if (newVersion == null || + OkCancelResult.cancel == + await showOkCancelAlertDialog( + context: context, + okLabel: L10n.of(context).yes, + cancelLabel: L10n.of(context).cancel, + title: L10n.of(context).areYouSure, + )) { + return; + } + await showFutureLoadingDialog( + context: context, + future: () => room.client.upgradeRoom(widget.roomId, newVersion), + ).then((_) => AdaptivePageLayout.of(context).pop()); + } + + @override + Widget build(BuildContext context) => ChatPermissionsSettingsView(this); +} diff --git a/lib/views/chat_permissions_settings.dart b/lib/views/chat_permissions_settings.dart deleted file mode 100644 index d9238b7c..00000000 --- a/lib/views/chat_permissions_settings.dart +++ /dev/null @@ -1,293 +0,0 @@ -import 'dart:developer'; - -import 'package:adaptive_dialog/adaptive_dialog.dart'; -import 'package:adaptive_page_layout/adaptive_page_layout.dart'; -import 'package:fluffychat/views/widgets/dialogs/permission_slider_dialog.dart'; -import 'package:fluffychat/views/widgets/max_width_body.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; -import 'package:fluffychat/views/widgets/matrix.dart'; - -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:famedlysdk/famedlysdk.dart'; - -class ChatPermissionsSettings extends StatelessWidget { - final String roomId; - - const ChatPermissionsSettings(this.roomId, {Key key}) : super(key: key); - - void _editPowerLevel(BuildContext context, String key, int currentLevel, - {String category}) async { - final room = Matrix.of(context).client.getRoomById(roomId); - if (!room.canSendEvent(EventTypes.RoomPowerLevels)) { - AdaptivePageLayout.of(context) - .showSnackBar(SnackBar(content: Text(L10n.of(context).noPermission))); - return; - } - final newLevel = - await PermissionSliderDialog(initialPermission: currentLevel) - .show(context); - if (newLevel == null) return; - final content = Map.from( - room.getState(EventTypes.RoomPowerLevels).content); - if (category != null) { - if (!content.containsKey(category)) { - content[category] = {}; - } - content[category][key] = newLevel; - } else { - content[key] = newLevel; - } - inspect(content); - await showFutureLoadingDialog( - context: context, - future: () => - room.client.sendState(room.id, EventTypes.RoomPowerLevels, content), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: BackButton(), - title: Text(L10n.of(context).editChatPermissions), - ), - body: MaxWidthBody( - withScrolling: true, - child: StreamBuilder( - stream: Matrix.of(context).client.onSync.stream.where( - (e) => - (e?.rooms?.join?.containsKey(roomId) ?? false) && - (e.rooms.join[roomId]?.timeline?.events?.any( - (s) => s.type == EventTypes.RoomPowerLevels) ?? - false), - ), - builder: (context, _) { - final room = Matrix.of(context).client.getRoomById(roomId); - final powerLevelsContent = Map.from( - room.getState(EventTypes.RoomPowerLevels).content); - final powerLevels = Map.from(powerLevelsContent) - ..removeWhere((k, v) => !(v is int)); - final eventsPowerLevels = - Map.from(powerLevelsContent['events'] ?? {}) - ..removeWhere((k, v) => !(v is int)); - - return Column( - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - for (var entry in powerLevels.entries) - PermissionsListTile( - permissionKey: entry.key, - permission: entry.value, - onTap: () => - _editPowerLevel(context, entry.key, entry.value), - ), - Divider(thickness: 1), - ListTile( - title: Text( - L10n.of(context).notifications, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ), - Builder(builder: (context) { - final key = 'rooms'; - final int value = powerLevelsContent - .containsKey('notifications') - ? powerLevelsContent['notifications']['rooms'] ?? 0 - : 0; - return PermissionsListTile( - permissionKey: key, - permission: value, - category: 'notifications', - onTap: () => _editPowerLevel(context, key, value, - category: 'notifications'), - ); - }), - Divider(thickness: 1), - ListTile( - title: Text( - L10n.of(context).configureChat, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ), - if (eventsPowerLevels != null) - for (var entry in eventsPowerLevels.entries) - PermissionsListTile( - permissionKey: entry.key, - category: 'events', - permission: entry.value, - onTap: () => _editPowerLevel( - context, entry.key, entry.value, - category: 'events'), - ), - if (room.canSendEvent(EventTypes.RoomTombstone)) ...{ - Divider(thickness: 1), - FutureBuilder( - future: room.client.requestServerCapabilities(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return Center(child: CircularProgressIndicator()); - } - final String roomVersion = room - .getState(EventTypes.RoomCreate) - .content['room_version'] ?? - '1'; - - return ListTile( - title: Text( - '${L10n.of(context).roomVersion}: $roomVersion'), - onTap: () async { - final newVersion = - await showConfirmationDialog( - context: context, - title: L10n.of(context) - .replaceRoomWithNewerVersion, - actions: snapshot - .data.mRoomVersions.available.entries - .where((r) => r.key != roomVersion) - .map((version) => AlertDialogAction( - key: version.key, - label: - '${version.key} (${version.value.toString().split('.').last})')) - .toList(), - ); - if (newVersion == null || - OkCancelResult.cancel == - await showOkCancelAlertDialog( - context: context, - okLabel: L10n.of(context).yes, - cancelLabel: L10n.of(context).cancel, - title: L10n.of(context).areYouSure, - )) { - return; - } - await showFutureLoadingDialog( - context: context, - future: () => - room.client.upgradeRoom(roomId, newVersion), - ).then( - (_) => AdaptivePageLayout.of(context).pop()); - }, - ); - }, - ), - }, - ], - ), - ], - ); - }, - ), - ), - ); - } -} - -class PermissionsListTile extends StatelessWidget { - final String permissionKey; - final int permission; - final String category; - final void Function() onTap; - - const PermissionsListTile({ - Key key, - @required this.permissionKey, - @required this.permission, - this.category, - this.onTap, - }) : super(key: key); - - String getLocalizedPowerLevelString(BuildContext context) { - if (category == null) { - switch (permissionKey) { - case 'users_default': - return L10n.of(context).defaultPermissionLevel; - case 'events_default': - return L10n.of(context).sendMessages; - case 'state_default': - return L10n.of(context).configureChat; - case 'ban': - return L10n.of(context).banFromChat; - case 'kick': - return L10n.of(context).kickFromChat; - case 'redact': - return L10n.of(context).deleteMessage; - case 'invite': - return L10n.of(context).inviteContact; - } - } else if (category == 'notifications') { - switch (permissionKey) { - case 'rooms': - return L10n.of(context).notifications; - } - } else if (category == 'events') { - switch (permissionKey) { - case EventTypes.RoomName: - return L10n.of(context).changeTheNameOfTheGroup; - case EventTypes.RoomPowerLevels: - return L10n.of(context).editChatPermissions; - case EventTypes.HistoryVisibility: - return L10n.of(context).visibilityOfTheChatHistory; - case EventTypes.RoomCanonicalAlias: - return L10n.of(context).setInvitationLink; - case EventTypes.RoomAvatar: - return L10n.of(context).editRoomAvatar; - case EventTypes.RoomTombstone: - return L10n.of(context).replaceRoomWithNewerVersion; - case EventTypes.Encryption: - return L10n.of(context).enableEncryption; - case 'm.room.server_acl': - return L10n.of(context).editBlockedServers; - } - } - return permissionKey; - } - - @override - Widget build(BuildContext context) { - return ListTile( - onTap: onTap, - leading: CircleAvatar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.edit_attributes_outlined), - ), - title: Text(getLocalizedPowerLevelString(context)), - subtitle: Row( - children: [ - Container( - padding: EdgeInsets.all(4), - decoration: BoxDecoration( - color: Theme.of(context).secondaryHeaderColor, - borderRadius: BorderRadius.circular(8), - ), - child: Center( - child: Text(permission.toString()), - ), - ), - SizedBox(width: 8), - Text(permission.toLocalizedPowerLevelString(context)), - ], - ), - ); - } -} - -extension on int { - String toLocalizedPowerLevelString(BuildContext context) { - return this == 100 - ? L10n.of(context).admin - : this >= 50 - ? L10n.of(context).moderator - : L10n.of(context).participant; - } -} diff --git a/lib/views/chat_permissions_settings_view.dart b/lib/views/chat_permissions_settings_view.dart new file mode 100644 index 00000000..b448c09e --- /dev/null +++ b/lib/views/chat_permissions_settings_view.dart @@ -0,0 +1,125 @@ +import 'package:fluffychat/controllers/chat_permissions_settings_controller.dart'; +import 'package:fluffychat/views/widgets/list_items/permission_list_tile.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; +import 'package:fluffychat/views/widgets/matrix.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:famedlysdk/famedlysdk.dart'; + +class ChatPermissionsSettingsView extends StatelessWidget { + final ChatPermissionsSettingsController controller; + + const ChatPermissionsSettingsView(this.controller, {Key key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: BackButton(), + title: Text(L10n.of(context).editChatPermissions), + ), + body: MaxWidthBody( + withScrolling: true, + child: StreamBuilder( + stream: controller.onChanged, + builder: (context, _) { + final room = + Matrix.of(context).client.getRoomById(controller.widget.roomId); + final powerLevelsContent = Map.from( + room.getState(EventTypes.RoomPowerLevels).content); + final powerLevels = Map.from(powerLevelsContent) + ..removeWhere((k, v) => !(v is int)); + final eventsPowerLevels = + Map.from(powerLevelsContent['events'] ?? {}) + ..removeWhere((k, v) => !(v is int)); + return Column( + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (var entry in powerLevels.entries) + PermissionsListTile( + permissionKey: entry.key, + permission: entry.value, + onTap: () => controller.editPowerLevel( + context, entry.key, entry.value), + ), + Divider(thickness: 1), + ListTile( + title: Text( + L10n.of(context).notifications, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ), + ), + ), + Builder(builder: (context) { + final key = 'rooms'; + final int value = powerLevelsContent + .containsKey('notifications') + ? powerLevelsContent['notifications']['rooms'] ?? 0 + : 0; + return PermissionsListTile( + permissionKey: key, + permission: value, + category: 'notifications', + onTap: () => controller.editPowerLevel( + context, key, value, + category: 'notifications'), + ); + }), + Divider(thickness: 1), + ListTile( + title: Text( + L10n.of(context).configureChat, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ), + ), + ), + if (eventsPowerLevels != null) + for (var entry in eventsPowerLevels.entries) + PermissionsListTile( + permissionKey: entry.key, + category: 'events', + permission: entry.value, + onTap: () => controller.editPowerLevel( + context, entry.key, entry.value, + category: 'events'), + ), + if (room.canSendEvent(EventTypes.RoomTombstone)) ...{ + Divider(thickness: 1), + FutureBuilder( + future: room.client.requestServerCapabilities(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center(child: CircularProgressIndicator()); + } + final String roomVersion = room + .getState(EventTypes.RoomCreate) + .content['room_version'] ?? + '1'; + + return ListTile( + title: Text( + '${L10n.of(context).roomVersion}: $roomVersion'), + onTap: () => + controller.updateRoomAction(snapshot.data), + ); + }, + ), + }, + ], + ), + ], + ); + }, + ), + ), + ); + } +} diff --git a/lib/views/widgets/list_items/permission_list_tile.dart b/lib/views/widgets/list_items/permission_list_tile.dart new file mode 100644 index 00000000..d850eeb5 --- /dev/null +++ b/lib/views/widgets/list_items/permission_list_tile.dart @@ -0,0 +1,103 @@ +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class PermissionsListTile extends StatelessWidget { + final String permissionKey; + final int permission; + final String category; + final void Function() onTap; + + const PermissionsListTile({ + Key key, + @required this.permissionKey, + @required this.permission, + this.category, + this.onTap, + }) : super(key: key); + + String getLocalizedPowerLevelString(BuildContext context) { + if (category == null) { + switch (permissionKey) { + case 'users_default': + return L10n.of(context).defaultPermissionLevel; + case 'events_default': + return L10n.of(context).sendMessages; + case 'state_default': + return L10n.of(context).configureChat; + case 'ban': + return L10n.of(context).banFromChat; + case 'kick': + return L10n.of(context).kickFromChat; + case 'redact': + return L10n.of(context).deleteMessage; + case 'invite': + return L10n.of(context).inviteContact; + } + } else if (category == 'notifications') { + switch (permissionKey) { + case 'rooms': + return L10n.of(context).notifications; + } + } else if (category == 'events') { + switch (permissionKey) { + case EventTypes.RoomName: + return L10n.of(context).changeTheNameOfTheGroup; + case EventTypes.RoomPowerLevels: + return L10n.of(context).editChatPermissions; + case EventTypes.HistoryVisibility: + return L10n.of(context).visibilityOfTheChatHistory; + case EventTypes.RoomCanonicalAlias: + return L10n.of(context).setInvitationLink; + case EventTypes.RoomAvatar: + return L10n.of(context).editRoomAvatar; + case EventTypes.RoomTombstone: + return L10n.of(context).replaceRoomWithNewerVersion; + case EventTypes.Encryption: + return L10n.of(context).enableEncryption; + case 'm.room.server_acl': + return L10n.of(context).editBlockedServers; + } + } + return permissionKey; + } + + @override + Widget build(BuildContext context) { + return ListTile( + onTap: onTap, + leading: CircleAvatar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.edit_attributes_outlined), + ), + title: Text(getLocalizedPowerLevelString(context)), + subtitle: Row( + children: [ + Container( + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + color: Theme.of(context).secondaryHeaderColor, + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Text(permission.toString()), + ), + ), + SizedBox(width: 8), + Text(permission.toLocalizedPowerLevelString(context)), + ], + ), + ); + } +} + +extension on int { + String toLocalizedPowerLevelString(BuildContext context) { + return this == 100 + ? L10n.of(context).admin + : this >= 50 + ? L10n.of(context).moderator + : L10n.of(context).participant; + } +}