diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index b2ac93ac1..97e051318 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2895,5 +2895,11 @@ } }, "deletePushRuleCanNotBeUndone": "If you delete this notification setting, this can not be undone.", - "more": "More" + "more": "More", + "shareKeysWith": "Share keys with...", + "shareKeysWithDescription": "Which devices should be trusted so that they can read along your messages in encrypted chats?", + "allDevices": "All devices", + "crossVerifiedDevicesIfEnabled": "Cross verified devices if enabled", + "crossVerifiedDevices": "Cross verified devices", + "verifiedDevicesOnly": "Verified devices only" } diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index 7c0e50df8..6d30298dd 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -34,4 +34,5 @@ abstract class SettingKeys { 'chat.fluffy.display_chat_details_column'; static const String noEncryptionWarningShown = 'chat.fluffy.no_encryption_warning_shown'; + static const String shareKeysWith = 'chat.fluffy.share_keys_with'; } diff --git a/lib/pages/settings_security/settings_security.dart b/lib/pages/settings_security/settings_security.dart index 18625dc04..a9026e472 100644 --- a/lib/pages/settings_security/settings_security.dart +++ b/lib/pages/settings_security/settings_security.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/setting_keys.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/app_lock.dart'; @@ -108,6 +109,16 @@ class SettingsSecurityController extends State { Future dehydrateAction() => Matrix.of(context).dehydrateAction(context); + void changeShareKeysWith(ShareKeysWith? shareKeysWith) async { + if (shareKeysWith == null) return; + Matrix.of(context).store.setString( + SettingKeys.shareKeysWith, + shareKeysWith.name, + ); + Matrix.of(context).client.shareKeysWith = shareKeysWith; + setState(() {}); + } + @override Widget build(BuildContext context) => SettingsSecurityView(this); } diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index e26f1deef..e21240323 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; @@ -88,6 +89,41 @@ class SettingsSecurityView extends StatelessWidget { ), }, Divider(color: theme.dividerColor), + ListTile( + title: Text( + L10n.of(context).shareKeysWith, + style: TextStyle( + color: theme.colorScheme.secondary, + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(L10n.of(context).shareKeysWithDescription), + ), + ListTile( + title: Material( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius / 2), + color: theme.colorScheme.onInverseSurface, + child: DropdownButton( + isExpanded: true, + padding: const EdgeInsets.symmetric(horizontal: 8.0), + borderRadius: + BorderRadius.circular(AppConfig.borderRadius / 2), + underline: const SizedBox.shrink(), + value: Matrix.of(context).client.shareKeysWith, + items: ShareKeysWith.values + .map( + (share) => DropdownMenuItem( + value: share, + child: Text(share.localized(L10n.of(context))), + ), + ) + .toList(), + onChanged: controller.changeShareKeysWith, + ), + ), + ), + Divider(color: theme.dividerColor), ListTile( title: Text( L10n.of(context).account, @@ -142,3 +178,18 @@ class SettingsSecurityView extends StatelessWidget { ); } } + +extension on ShareKeysWith { + String localized(L10n l10n) { + switch (this) { + case ShareKeysWith.all: + return l10n.allDevices; + case ShareKeysWith.crossVerifiedIfEnabled: + return l10n.crossVerifiedDevicesIfEnabled; + case ShareKeysWith.crossVerified: + return l10n.crossVerifiedDevices; + case ShareKeysWith.directlyVerifiedOnly: + return l10n.verifiedDevicesOnly; + } + } +} diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index d6bc69669..b83567346 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; +import 'package:collection/collection.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; @@ -13,6 +14,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:universal_html/html.dart' as html; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/custom_http_client.dart'; import 'package:fluffychat/utils/custom_image_resizer.dart'; import 'package:fluffychat/utils/init_with_restore.dart'; @@ -43,7 +45,8 @@ abstract class ClientManager { clientNames.add(PlatformInfos.clientName); await store.setStringList(clientNamespace, clientNames.toList()); } - final clients = clientNames.map(createClient).toList(); + final clients = + clientNames.map((name) => createClient(name, store)).toList(); if (initialize) { await Future.wait( clients.map( @@ -97,7 +100,9 @@ abstract class ClientManager { ? const NativeImplementationsDummy() : NativeImplementationsIsolate(compute); - static Client createClient(String clientName) { + static Client createClient(String clientName, SharedPreferences store) { + final shareKeysWith = store.getString(SettingKeys.shareKeysWith) ?? 'all'; + return Client( clientName, httpClient: @@ -122,6 +127,9 @@ abstract class ClientManager { customImageResizer: PlatformInfos.isMobile ? customImageResizer : null, defaultNetworkRequestTimeout: const Duration(minutes: 30), enableDehydratedDevices: true, + shareKeysWith: ShareKeysWith.values + .singleWhereOrNull((share) => share.name == shareKeysWith) ?? + ShareKeysWith.all, ); } diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 677bfb744..125ec0b49 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -154,6 +154,7 @@ class MatrixState extends State with WidgetsBindingObserver { } final candidate = _loginClientCandidate ??= ClientManager.createClient( '${AppConfig.applicationName}-${DateTime.now().millisecondsSinceEpoch}', + store, )..onLoginStateChanged .stream .where((l) => l == LoginState.loggedIn) diff --git a/pubspec.lock b/pubspec.lock index b423ecb40..7904afb57 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1126,10 +1126,10 @@ packages: dependency: "direct main" description: name: matrix - sha256: "519e1d18623f741de5aa984a9d04cf3f660149d1e167fa06e17ddec8cec5f52d" + sha256: d0da69e5ee8dfc1692c02e4b460a1bc136120f0dcf5e02cf604b23cd39d76903 url: "https://pub.dev" source: hosted - version: "0.37.0" + version: "0.38.0" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 40e1c488c..3379c55e0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -60,7 +60,7 @@ dependencies: just_audio: ^0.9.39 latlong2: ^0.9.1 linkify: ^5.0.0 - matrix: ^0.37.0 + matrix: ^0.38.0 mime: ^1.0.6 native_imaging: ^0.1.1 opus_caf_converter_dart: ^1.0.1 @@ -154,4 +154,4 @@ msix_config: install_certificate: false dependency_overrides: - win32: 5.5.3 \ No newline at end of file + win32: 5.5.3