refactor: MVC chat permission settings

This commit is contained in:
Christian Pauly 2021-04-15 08:48:26 +02:00
parent 7658425a7d
commit 001e0ee48f
5 changed files with 325 additions and 294 deletions

View File

@ -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';

View File

@ -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<ChatPermissionsSettings> {
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<String, dynamic>.from(
room.getState(EventTypes.RoomPowerLevels).content);
if (category != null) {
if (!content.containsKey(category)) {
content[category] = <String, dynamic>{};
}
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<String>(
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);
}

View File

@ -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<String, dynamic>.from(
room.getState(EventTypes.RoomPowerLevels).content);
if (category != null) {
if (!content.containsKey(category)) {
content[category] = <String, dynamic>{};
}
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<String, dynamic>.from(
room.getState(EventTypes.RoomPowerLevels).content);
final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
..removeWhere((k, v) => !(v is int));
final eventsPowerLevels =
Map<String, dynamic>.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<ServerCapabilities>(
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<String>(
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;
}
}

View File

@ -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<String, dynamic>.from(
room.getState(EventTypes.RoomPowerLevels).content);
final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
..removeWhere((k, v) => !(v is int));
final eventsPowerLevels =
Map<String, dynamic>.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<ServerCapabilities>(
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),
);
},
),
},
],
),
],
);
},
),
),
);
}
}

View File

@ -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;
}
}