301 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
| import 'package:flutter/material.dart' hide Visibility;
 | |
| 
 | |
| import 'package:fluffychat/generated/l10n/l10n.dart';
 | |
| import 'package:go_router/go_router.dart';
 | |
| import 'package:matrix/matrix.dart';
 | |
| 
 | |
| import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_page.dart';
 | |
| import 'package:fluffychat/utils/localized_exception_extension.dart';
 | |
| import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
 | |
| import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
 | |
| import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
 | |
| import 'package:fluffychat/widgets/future_loading_dialog.dart';
 | |
| import 'package:fluffychat/widgets/matrix.dart';
 | |
| 
 | |
| class ChatAccessSettings extends StatefulWidget {
 | |
|   final String roomId;
 | |
|   const ChatAccessSettings({required this.roomId, super.key});
 | |
| 
 | |
|   @override
 | |
|   State<ChatAccessSettings> createState() => ChatAccessSettingsController();
 | |
| }
 | |
| 
 | |
| class ChatAccessSettingsController extends State<ChatAccessSettings> {
 | |
|   bool joinRulesLoading = false;
 | |
|   bool visibilityLoading = false;
 | |
|   bool historyVisibilityLoading = false;
 | |
|   bool guestAccessLoading = false;
 | |
|   Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!;
 | |
| 
 | |
|   String get roomVersion =>
 | |
|       room
 | |
|           .getState(EventTypes.RoomCreate)!
 | |
|           .content
 | |
|           .tryGet<String>('room_version') ??
 | |
|       'Unknown';
 | |
| 
 | |
|   /// Calculates which join rules are available based on the information on
 | |
|   /// https://spec.matrix.org/v1.11/rooms/#feature-matrix
 | |
|   List<JoinRules> get availableJoinRules {
 | |
|     final joinRules = Set<JoinRules>.from(JoinRules.values);
 | |
| 
 | |
|     final roomVersionInt = int.tryParse(roomVersion);
 | |
| 
 | |
|     // Knock is only supported for rooms up from version 7:
 | |
|     if (roomVersionInt != null && roomVersionInt <= 6) {
 | |
|       joinRules.remove(JoinRules.knock);
 | |
|     }
 | |
| 
 | |
|     // Not yet supported in FluffyChat:
 | |
|     joinRules.remove(JoinRules.restricted);
 | |
|     joinRules.remove(JoinRules.knockRestricted);
 | |
| 
 | |
|     // If an unsupported join rule is the current join rule, display it:
 | |
|     final currentJoinRule = room.joinRules;
 | |
|     if (currentJoinRule != null) joinRules.add(currentJoinRule);
 | |
| 
 | |
|     return joinRules.toList();
 | |
|   }
 | |
| 
 | |
|   void setJoinRule(JoinRules? newJoinRules) async {
 | |
|     if (newJoinRules == null) return;
 | |
|     setState(() {
 | |
|       joinRulesLoading = true;
 | |
|     });
 | |
| 
 | |
|     try {
 | |
|       await room.setJoinRules(newJoinRules);
 | |
|     } catch (e, s) {
 | |
|       Logs().w('Unable to change join rules', e, s);
 | |
|       if (mounted) {
 | |
|         ScaffoldMessenger.of(context).showSnackBar(
 | |
|           SnackBar(
 | |
|             content: Text(
 | |
|               e.toLocalizedString(context),
 | |
|             ),
 | |
|           ),
 | |
|         );
 | |
|       }
 | |
|     } finally {
 | |
|       if (mounted) {
 | |
|         setState(() {
 | |
|           joinRulesLoading = false;
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void setHistoryVisibility(HistoryVisibility? historyVisibility) async {
 | |
|     if (historyVisibility == null) return;
 | |
|     setState(() {
 | |
|       historyVisibilityLoading = true;
 | |
|     });
 | |
| 
 | |
|     try {
 | |
|       await room.setHistoryVisibility(historyVisibility);
 | |
|     } catch (e, s) {
 | |
|       Logs().w('Unable to change history visibility', e, s);
 | |
|       if (mounted) {
 | |
|         ScaffoldMessenger.of(context).showSnackBar(
 | |
|           SnackBar(
 | |
|             content: Text(
 | |
|               e.toLocalizedString(context),
 | |
|             ),
 | |
|           ),
 | |
|         );
 | |
|       }
 | |
|     } finally {
 | |
|       if (mounted) {
 | |
|         setState(() {
 | |
|           historyVisibilityLoading = false;
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void setGuestAccess(GuestAccess? guestAccess) async {
 | |
|     if (guestAccess == null) return;
 | |
|     setState(() {
 | |
|       guestAccessLoading = true;
 | |
|     });
 | |
| 
 | |
|     try {
 | |
|       await room.setGuestAccess(guestAccess);
 | |
|     } catch (e, s) {
 | |
|       Logs().w('Unable to change guest access', e, s);
 | |
|       if (mounted) {
 | |
|         ScaffoldMessenger.of(context).showSnackBar(
 | |
|           SnackBar(
 | |
|             content: Text(
 | |
|               e.toLocalizedString(context),
 | |
|             ),
 | |
|           ),
 | |
|         );
 | |
|       }
 | |
|     } finally {
 | |
|       if (mounted) {
 | |
|         setState(() {
 | |
|           guestAccessLoading = false;
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void updateRoomAction() async {
 | |
|     final roomVersion = room
 | |
|         .getState(EventTypes.RoomCreate)!
 | |
|         .content
 | |
|         .tryGet<String>('room_version');
 | |
|     final capabilitiesResult = await showFutureLoadingDialog(
 | |
|       context: context,
 | |
|       future: () => room.client.getCapabilities(),
 | |
|     );
 | |
|     final capabilities = capabilitiesResult.result;
 | |
|     if (capabilities == null) return;
 | |
|     final newVersion = await showModalActionPopup<String>(
 | |
|       context: context,
 | |
|       title: L10n.of(context).replaceRoomWithNewerVersion,
 | |
|       cancelLabel: L10n.of(context).cancel,
 | |
|       actions: capabilities.mRoomVersions!.available.entries
 | |
|           .where((r) => r.key != roomVersion)
 | |
|           .map(
 | |
|             (version) => AdaptiveModalAction(
 | |
|               value: version.key,
 | |
|               label:
 | |
|                   '${version.key} (${version.value.toString().split('.').last})',
 | |
|             ),
 | |
|           )
 | |
|           .toList(),
 | |
|     );
 | |
|     if (newVersion == null ||
 | |
|         OkCancelResult.cancel ==
 | |
|             await showOkCancelAlertDialog(
 | |
|               useRootNavigator: false,
 | |
|               context: context,
 | |
|               okLabel: L10n.of(context).yes,
 | |
|               cancelLabel: L10n.of(context).cancel,
 | |
|               title: L10n.of(context).areYouSure,
 | |
|               message: L10n.of(context).roomUpgradeDescription,
 | |
|               isDestructive: true,
 | |
|             )) {
 | |
|       return;
 | |
|     }
 | |
|     final result = await showFutureLoadingDialog(
 | |
|       context: context,
 | |
|       future: () => room.client.upgradeRoom(room.id, newVersion),
 | |
|     );
 | |
|     if (result.error != null) return;
 | |
|     if (!mounted) return;
 | |
|     context.go('/rooms/${room.id}');
 | |
|   }
 | |
| 
 | |
|   Future<void> addAlias() async {
 | |
|     final domain = room.client.userID?.domain;
 | |
|     if (domain == null) {
 | |
|       throw Exception('userID or domain is null! This should never happen.');
 | |
|     }
 | |
| 
 | |
|     final input = await showTextInputDialog(
 | |
|       context: context,
 | |
|       title: L10n.of(context).editRoomAliases,
 | |
|       prefixText: '#',
 | |
|       suffixText: domain,
 | |
|       hintText: L10n.of(context).alias,
 | |
|     );
 | |
|     final aliasLocalpart = input?.trim();
 | |
|     if (aliasLocalpart == null || aliasLocalpart.isEmpty) return;
 | |
|     final alias = '#$aliasLocalpart:$domain';
 | |
| 
 | |
|     final result = await showFutureLoadingDialog(
 | |
|       context: context,
 | |
|       future: () => room.client.setRoomAlias(alias, room.id),
 | |
|     );
 | |
|     if (result.error != null) return;
 | |
|     setState(() {});
 | |
| 
 | |
|     if (!room.canChangeStateEvent(EventTypes.RoomCanonicalAlias)) return;
 | |
| 
 | |
|     final canonicalAliasConsent = await showOkCancelAlertDialog(
 | |
|       context: context,
 | |
|       title: L10n.of(context).setAsCanonicalAlias,
 | |
|       message: alias,
 | |
|       okLabel: L10n.of(context).yes,
 | |
|       cancelLabel: L10n.of(context).no,
 | |
|     );
 | |
| 
 | |
|     final altAliases = room
 | |
|             .getState(EventTypes.RoomCanonicalAlias)
 | |
|             ?.content
 | |
|             .tryGetList<String>('alt_aliases')
 | |
|             ?.toSet() ??
 | |
|         {};
 | |
|     if (room.canonicalAlias.isNotEmpty) altAliases.add(room.canonicalAlias);
 | |
|     altAliases.add(alias);
 | |
|     if (canonicalAliasConsent == OkCancelResult.ok) {
 | |
|       altAliases.remove(alias);
 | |
|     } else {
 | |
|       altAliases.remove(room.canonicalAlias);
 | |
|     }
 | |
| 
 | |
|     await showFutureLoadingDialog(
 | |
|       context: context,
 | |
|       future: () => room.client.setRoomStateWithKey(
 | |
|         room.id,
 | |
|         EventTypes.RoomCanonicalAlias,
 | |
|         '',
 | |
|         {
 | |
|           'alias': canonicalAliasConsent == OkCancelResult.ok
 | |
|               ? alias
 | |
|               : room.canonicalAlias,
 | |
|           if (altAliases.isNotEmpty) 'alt_aliases': altAliases.toList(),
 | |
|         },
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   void deleteAlias(String alias) async {
 | |
|     await showFutureLoadingDialog(
 | |
|       context: context,
 | |
|       future: () => room.client.deleteRoomAlias(alias),
 | |
|     );
 | |
|     setState(() {});
 | |
|   }
 | |
| 
 | |
|   void setChatVisibilityOnDirectory(bool? visibility) async {
 | |
|     if (visibility == null) return;
 | |
|     setState(() {
 | |
|       visibilityLoading = true;
 | |
|     });
 | |
| 
 | |
|     try {
 | |
|       await room.client.setRoomVisibilityOnDirectory(
 | |
|         room.id,
 | |
|         visibility: visibility == true ? Visibility.public : Visibility.private,
 | |
|       );
 | |
|       setState(() {});
 | |
|     } catch (e, s) {
 | |
|       Logs().w('Unable to change visibility', e, s);
 | |
|       if (mounted) {
 | |
|         ScaffoldMessenger.of(context).showSnackBar(
 | |
|           SnackBar(
 | |
|             content: Text(
 | |
|               e.toLocalizedString(context),
 | |
|             ),
 | |
|           ),
 | |
|         );
 | |
|       }
 | |
|     } finally {
 | |
|       if (mounted) {
 | |
|         setState(() {
 | |
|           visibilityLoading = false;
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return ChatAccessSettingsPageView(this);
 | |
|   }
 | |
| }
 |