From ece9ad4c47a371cf01e0ac2e75b03741cc1a1b9f Mon Sep 17 00:00:00 2001 From: root Date: Tue, 19 Sep 2023 17:44:23 +0200 Subject: [PATCH 01/93] use locally hosted canvaskit instead of calling google --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7a696ce54..7859f2468 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ COPY . /app WORKDIR /app RUN ./scripts/prepare-web.sh RUN flutter pub get -RUN flutter build web --release --source-maps +RUN flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps FROM docker.io/nginx:alpine RUN rm -rf /usr/share/nginx/html From 3fb4ad0a83283e44f8d04efba5dd0c308ecad8dd Mon Sep 17 00:00:00 2001 From: Krille Date: Tue, 24 Oct 2023 09:55:52 +0200 Subject: [PATCH 02/93] refactor: Change audio codec to opus where supported to have better compatibility with Element --- lib/pages/chat/recording_dialog.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 86d6cbb61..1057ce6a1 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -46,7 +46,16 @@ class RecordingDialogState extends State { return; } await WakelockPlus.enable(); + + // We try to pick Opus where supported, since that is a codec optimized + // for speech as well as what the voice messages MSC uses. + final audioCodec = + (await _audioRecorder.isEncoderSupported(AudioEncoder.opus)) + ? AudioEncoder.opus + : AudioEncoder.aacLc; + await _audioRecorder.start( + encoder: audioCodec, path: _recordedPath, bitRate: bitRate, samplingRate: samplingRate, From 59b357681edd734bac0ddc1cd865111041734138 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 09:47:25 +0200 Subject: [PATCH 03/93] build: Update matrix dart sdk --- assets/l10n/intl_en.arb | 6 ++++++ lib/utils/matrix_sdk_extensions/matrix_locals.dart | 3 +++ pubspec.lock | 8 ++++---- pubspec.yaml | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 78a315041..0fa4c022c 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2402,6 +2402,12 @@ "user": {} } }, + "hasKnocked": "{user} has knocked", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, "noEmailWarning": "Please enter a valid email address. Otherwise you won't be able to reset your password. If you don't want to, tap again on the button to continue.", "@noEmailWarning": {}, "stories": "Stories", diff --git a/lib/utils/matrix_sdk_extensions/matrix_locals.dart b/lib/utils/matrix_sdk_extensions/matrix_locals.dart index 44eab72dc..4a9b68274 100644 --- a/lib/utils/matrix_sdk_extensions/matrix_locals.dart +++ b/lib/utils/matrix_sdk_extensions/matrix_locals.dart @@ -317,4 +317,7 @@ class MatrixLocals extends MatrixLocalizations { @override String get unknownUser => l10n.user; + + @override + String hasKnocked(String targetName) => l10n.hasKnocked(targetName); } diff --git a/pubspec.lock b/pubspec.lock index 7155d6332..b9b79888e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1081,10 +1081,10 @@ packages: dependency: transitive description: name: markdown - sha256: "01512006c8429f604eb10f9848717baeaedf99e991d14a50d540d9beff08e5c6" + sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "7.1.1" matcher: dependency: transitive description: @@ -1105,10 +1105,10 @@ packages: dependency: "direct main" description: name: matrix - sha256: "10389562a4562db6150291b538e025a9a1b7a79998a71d38cb5c78a34ca6b007" + sha256: "1e4bef4923fa1e33124843aa59932739e69a5507178f18313ec1067c046156f3" url: "https://pub.dev" source: hosted - version: "0.22.3" + version: "0.22.6" matrix_api_lite: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2c4f3f9fe..2aaa52836 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,7 +61,7 @@ dependencies: keyboard_shortcuts: ^0.1.4 latlong2: ^0.8.1 linkify: ^5.0.0 - matrix: ^0.22.3 + matrix: ^0.22.6 matrix_homeserver_recommendations: ^0.3.0 native_imaging: ^0.1.0 package_info_plus: ^4.0.0 From 5665cac15cb1d6cecc0b0f4f771b65f5e862c65f Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 10:09:40 +0200 Subject: [PATCH 04/93] fix: Create chat dialog crashes sometimes and power level textfield does not validate input --- assets/l10n/intl_en.arb | 3 ++- lib/pages/user_bottom_sheet/user_bottom_sheet.dart | 8 ++++---- lib/widgets/permission_slider_dialog.dart | 10 ++++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 0fa4c022c..cb1dcbe5a 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2533,5 +2533,6 @@ "placeholders": { "seconds": {} } - } + }, + "pleaseEnterANumber": "Please enter a number greater than 0" } diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index 491f86ef9..b38efa214 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -34,7 +34,7 @@ class LoadProfileBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { return FutureBuilder( - future: Matrix.of(context) + future: Matrix.of(outerContext) .client .getUserProfile(userId) .timeout(const Duration(seconds: 3)), @@ -138,7 +138,7 @@ class UserBottomSheetController extends State { if (reason == null || reason.single.isEmpty) return; final result = await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context).client.reportContent( + future: () => Matrix.of(widget.outerContext).client.reportContent( user.roomId!, user.eventId, reason: reason.single, @@ -203,7 +203,7 @@ class UserBottomSheetController extends State { case UserBottomSheetAction.message: final roomIdResult = await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context) + future: () => Matrix.of(widget.outerContext) .client .startDirectChat(user?.id ?? widget.profile!.userId), ); @@ -215,7 +215,7 @@ class UserBottomSheetController extends State { if (await askConfirmation()) { await showFutureLoadingDialog( context: context, - future: () => Matrix.of(context) + future: () => Matrix.of(widget.outerContext) .client .ignoreUser(user?.id ?? widget.profile!.userId), ); diff --git a/lib/widgets/permission_slider_dialog.dart b/lib/widgets/permission_slider_dialog.dart index 8f8958982..cc6e9303e 100644 --- a/lib/widgets/permission_slider_dialog.dart +++ b/lib/widgets/permission_slider_dialog.dart @@ -61,6 +61,16 @@ Future showPermissionChooser( 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; + }, ), ], ); From 5dfa4c132dc8514df735d9ea4fc4f654b30343d5 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 10:20:37 +0200 Subject: [PATCH 05/93] design: Make key verification an adaptive dialog --- .../key_verification_dialog.dart | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/pages/key_verification/key_verification_dialog.dart b/lib/pages/key_verification/key_verification_dialog.dart index 7d9633ccd..bfed12be7 100644 --- a/lib/pages/key_verification/key_verification_dialog.dart +++ b/lib/pages/key_verification/key_verification_dialog.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:ui'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -10,14 +11,13 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/widgets/avatar.dart'; class KeyVerificationDialog extends StatefulWidget { - Future show(BuildContext context) => showAdaptiveBottomSheet( + Future show(BuildContext context) => showDialog( context: context, builder: (context) => this, - isDismissible: false, + barrierDismissible: false, ); final KeyVerification request; @@ -342,24 +342,32 @@ class KeyVerificationPageState extends State { ); break; } - return Scaffold( - appBar: AppBar( - leading: const Center(child: CloseButton()), + if ({TargetPlatform.iOS, TargetPlatform.macOS} + .contains(Theme.of(context).platform)) { + return CupertinoAlertDialog( title: title, - ), - body: ListView( - padding: const EdgeInsets.all(12.0), - children: [body], - ), - bottomNavigationBar: SafeArea( - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: buttons, + content: SizedBox( + height: 256, + width: 256, + child: ListView( + padding: const EdgeInsets.only(top: 16), + children: [body], ), ), + actions: buttons, + ); + } + + return AlertDialog( + title: title, + content: SizedBox( + height: 256, + width: 256, + child: ListView( + children: [body], + ), ), + actions: buttons, ); } } From 991bba253509cf3427f1cd6892e161311768c2ba Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 10:29:22 +0200 Subject: [PATCH 06/93] build: Update record package --- lib/pages/chat/chat.dart | 2 +- lib/pages/chat/recording_dialog.dart | 17 ++++--- macos/Flutter/GeneratedPluginRegistrant.swift | 4 +- pubspec.lock | 44 +++++++++++-------- pubspec.yaml | 2 +- 5 files changed, 41 insertions(+), 28 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index e17226fae..23b77c70e 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -626,7 +626,7 @@ class ChatController extends State { } } - if (await Record().hasPermission() == false) return; + if (await AudioRecorder().hasPermission() == false) return; final result = await showDialog( context: context, useRootNavigator: false, diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 1057ce6a1..0629240df 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -28,7 +28,7 @@ class RecordingDialogState extends State { bool error = false; String? _recordedPath; - final _audioRecorder = Record(); + final _audioRecorder = AudioRecorder(); final List amplitudeTimeline = []; static const int bitRate = 64000; @@ -37,7 +37,7 @@ class RecordingDialogState extends State { Future startRecording() async { try { final tempDir = await getTemporaryDirectory(); - _recordedPath = + final path = _recordedPath = '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}'; final result = await _audioRecorder.hasPermission(); @@ -55,10 +55,15 @@ class RecordingDialogState extends State { : AudioEncoder.aacLc; await _audioRecorder.start( - encoder: audioCodec, - path: _recordedPath, - bitRate: bitRate, - samplingRate: samplingRate, + RecordConfig( + encoder: audioCodec, + autoGain: true, + noiseSuppress: true, + echoCancel: true, + bitRate: bitRate, + sampleRate: samplingRate, + ), + path: path, ); setState(() => _duration = Duration.zero); _recorderSubscription?.cancel(); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e47880af4..8f5de20fb 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -24,7 +24,7 @@ import macos_window_utils import package_info_plus import pasteboard import path_provider_foundation -import record_macos +import record_darwin import share_plus import shared_preferences_foundation import sqflite @@ -53,7 +53,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - RecordMacosPlugin.register(with: registry.registrar(forPlugin: "RecordMacosPlugin")) + RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index b9b79888e..225d998a7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1473,50 +1473,58 @@ packages: dependency: "direct main" description: name: record - sha256: f703397f5a60d9b2b655b3acc94ba079b2d9a67dc0725bdb90ef2fee2441ebf7 + sha256: be9b710f42edf94f939dda1a1688e82a68dcd391be0a836c01e639a249f133d3 url: "https://pub.dev" source: hosted - version: "4.4.4" + version: "5.0.1" + record_android: + dependency: transitive + description: + name: record_android + sha256: f2d72c8e86d275967b9e9d1ced58c775958b94d3e2dac3d5416df46d23924ade + url: "https://pub.dev" + source: hosted + version: "1.0.2" + record_darwin: + dependency: transitive + description: + name: record_darwin + sha256: "78dba641ae271e555035ee68b637f7605ba9f8c60ccfd5c03b835e0b77ea201f" + url: "https://pub.dev" + source: hosted + version: "1.0.0" record_linux: dependency: transitive description: name: record_linux - sha256: "348db92c4ec1b67b1b85d791381c8c99d7c6908de141e7c9edc20dad399b15ce" + sha256: "7d0e70cd51635128fe9d37d89bafd6011d7cbba9af8dc323079ae60f23546aef" url: "https://pub.dev" source: hosted - version: "0.4.1" - record_macos: - dependency: transitive - description: - name: record_macos - sha256: d1d0199d1395f05e218207e8cacd03eb9dc9e256ddfe2cfcbbb90e8edea06057 - url: "https://pub.dev" - source: hosted - version: "0.2.2" + version: "0.7.1" record_platform_interface: dependency: transitive description: name: record_platform_interface - sha256: "7a2d4ce7ac3752505157e416e4e0d666a54b1d5d8601701b7e7e5e30bec181b4" + sha256: "3a4b56e94ecd2a0b2b43eb1fa6f94c5b8484334f5d38ef43959c4bf97fb374cf" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "1.0.2" record_web: dependency: transitive description: name: record_web - sha256: "219ffb4ca59b4338117857db56d3ffadbde3169bcaf1136f5f4d4656f4a2372d" + sha256: f1c0427dcf5af68dcfe06439d04771d65f73bd040eb0dcd98fbe4c02263a7d35 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "1.0.0" record_windows: dependency: transitive description: name: record_windows - sha256: "42d545155a26b20d74f5107648dbb3382dbbc84dc3f1adc767040359e57a1345" + sha256: "326bfbe6f5232dd773ad6b848cd94f78148f02557abff1dd4627d056b688dbdb" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "1.0.0" remove_emoji: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2aaa52836..48c764378 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -73,7 +73,7 @@ dependencies: qr_code_scanner: ^1.0.0 qr_flutter: ^4.0.0 receive_sharing_intent: ^1.4.5 - record: ^4.4.4 + record: ^5.0.1 scroll_to_index: ^3.0.1 share_plus: ^7.0.0 shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401 From 969dbef09ad6f2026aa272225652caf9e5670b4d Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 10:34:25 +0200 Subject: [PATCH 07/93] design: Make incoming messages color more light --- lib/pages/chat/events/message.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 72a637aee..d25785a96 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -70,7 +70,7 @@ class Message extends StatelessWidget { final client = Matrix.of(context).client; final ownMessage = event.senderId == client.userID; final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft; - var color = Theme.of(context).colorScheme.surfaceVariant; + var color = Theme.of(context).colorScheme.onInverseSurface; final displayTime = event.type == EventTypes.RoomCreate || nextEvent == null || !event.originServerTs.sameEnvironment(nextEvent!.originServerTs); @@ -84,7 +84,7 @@ class Message extends StatelessWidget { nextEvent!.senderId == event.senderId && !displayTime; final textColor = ownMessage - ? Theme.of(context).colorScheme.onPrimary + ? Theme.of(context).colorScheme.onPrimaryContainer : Theme.of(context).colorScheme.onBackground; final rowMainAxisAlignment = ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start; @@ -114,7 +114,7 @@ class Message extends StatelessWidget { if (ownMessage) { color = displayEvent.status.isError ? Colors.redAccent - : Theme.of(context).colorScheme.primary; + : Theme.of(context).colorScheme.primaryContainer; } final row = Row( From 67a4bc71ab5bef8043437f001066b3d2301eec10 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 10:54:41 +0200 Subject: [PATCH 08/93] chore: Add descriptions in the areYouSure dialogs for better UX --- assets/l10n/intl_en.arb | 9 ++- lib/pages/chat_list/chat_list.dart | 1 + lib/pages/chat_list/chat_list_item.dart | 1 + .../chat_permissions_settings.dart | 1 + .../device_settings/device_settings.dart | 1 + .../user_bottom_sheet/user_bottom_sheet.dart | 60 ++++++++++++------- lib/widgets/chat_settings_popup_menu.dart | 1 + 7 files changed, 52 insertions(+), 22 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index cb1dcbe5a..088f7802f 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2534,5 +2534,12 @@ "seconds": {} } }, - "pleaseEnterANumber": "Please enter a number greater than 0" + "pleaseEnterANumber": "Please enter a number greater than 0", + "archiveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.", + "roomUpgradeDescription": "The chat will then be recreated with the new room version. All participants will be notified that they need to switch to the new chat. You can find out more about room versions at https://spec.matrix.org/latest/rooms/", + "removeDevicesDescription": "You will be logged out of this device and will no longer be able to receive messages.", + "banUserDescription": "The user will be banned from the chat and will not be able to enter the chat again until they are unbanned.", + "unbanUserDescription": "The user will be able to enter the chat again if they try.", + "kickUserDescription": "The user is kicked out of the chat but not banned. In public chats, the user can rejoin at any time.", + "makeAdminDescription": "Once you make this user admin, you may not be able to undo this as they will then have the same permissions as you." } diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index b972847fd..79542fd81 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -471,6 +471,7 @@ class ChatListController extends State title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.yes, cancelLabel: L10n.of(context)!.cancel, + message: L10n.of(context)!.archiveRoomDescription, ) == OkCancelResult.ok; if (!confirmed) return; diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 9475b8a48..41a4aec35 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -132,6 +132,7 @@ class ChatListItem extends StatelessWidget { title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.yes, cancelLabel: L10n.of(context)!.no, + message: L10n.of(context)!.archiveRoomDescription, ); if (confirmed == OkCancelResult.cancel) return; await showFutureLoadingDialog( diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart index 394206f17..45983574e 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart @@ -99,6 +99,7 @@ class ChatPermissionsSettingsController extends State { okLabel: L10n.of(context)!.yes, cancelLabel: L10n.of(context)!.cancel, title: L10n.of(context)!.areYouSure, + message: L10n.of(context)!.roomUpgradeDescription, )) { return; } diff --git a/lib/pages/device_settings/device_settings.dart b/lib/pages/device_settings/device_settings.dart index 59e9e6583..25011c5fc 100644 --- a/lib/pages/device_settings/device_settings.dart +++ b/lib/pages/device_settings/device_settings.dart @@ -38,6 +38,7 @@ class DevicesSettingsController extends State { title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.yes, cancelLabel: L10n.of(context)!.cancel, + message: L10n.of(context)!.removeDevicesDescription, ) == OkCancelResult.cancel) return; final matrix = Matrix.of(context); diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index b38efa214..7234317a8 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -92,15 +92,7 @@ class UserBottomSheetController extends State { final user = widget.user; final userId = user?.id ?? widget.profile?.userId; if (userId == null) throw ('user or profile must not be null!'); - // ignore: prefer_function_declarations_over_variables - final Function askConfirmation = () async => (await showOkCancelAlertDialog( - useRootNavigator: false, - context: context, - title: L10n.of(context)!.areYouSure, - okLabel: L10n.of(context)!.yes, - cancelLabel: L10n.of(context)!.no, - ) == - OkCancelResult.ok); + switch (action) { case UserBottomSheetAction.report: if (user == null) throw ('User must not be null for this action!'); @@ -157,7 +149,15 @@ class UserBottomSheetController extends State { break; case UserBottomSheetAction.ban: if (user == null) throw ('User must not be null for this action!'); - if (await askConfirmation()) { + if (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)!.banUserDescription, + ) == + OkCancelResult.ok) { await showFutureLoadingDialog( context: context, future: () => user.ban(), @@ -167,7 +167,15 @@ class UserBottomSheetController extends State { break; case UserBottomSheetAction.unban: if (user == null) throw ('User must not be null for this action!'); - if (await askConfirmation()) { + if (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)!.unbanUserDescription, + ) == + OkCancelResult.ok) { await showFutureLoadingDialog( context: context, future: () => user.unban(), @@ -177,7 +185,15 @@ class UserBottomSheetController extends State { break; case UserBottomSheetAction.kick: if (user == null) throw ('User must not be null for this action!'); - if (await askConfirmation()) { + if (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)!.kickUserDescription, + ) == + OkCancelResult.ok) { await showFutureLoadingDialog( context: context, future: () => user.kick(), @@ -192,7 +208,16 @@ class UserBottomSheetController extends State { currentLevel: user.powerLevel, ); if (newPermission != null) { - if (newPermission == 100 && await askConfirmation() == false) break; + 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), @@ -212,14 +237,7 @@ class UserBottomSheetController extends State { Navigator.of(context, rootNavigator: false).pop(); break; case UserBottomSheetAction.ignore: - if (await askConfirmation()) { - await showFutureLoadingDialog( - context: context, - future: () => Matrix.of(widget.outerContext) - .client - .ignoreUser(user?.id ?? widget.profile!.userId), - ); - } + context.go('/rooms/settings/security/ignorelist'); } } diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index fbbb4978e..bc7e929ae 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -112,6 +112,7 @@ class ChatSettingsPopupMenuState extends State { title: L10n.of(context)!.areYouSure, okLabel: L10n.of(context)!.ok, cancelLabel: L10n.of(context)!.cancel, + message: L10n.of(context)!.archiveRoomDescription, ); if (confirmed == OkCancelResult.ok) { final success = await showFutureLoadingDialog( From 20f9a19397387a56a8d88a55950501d5e64010c1 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 11:01:15 +0200 Subject: [PATCH 09/93] refactor: Remove unused config params --- lib/config/app_config.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index f078fdf08..034b66c15 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -38,9 +38,6 @@ abstract class AppConfig { host: 'github.com', path: '/krille-chan/fluffychat/issues/new', ); - static const bool enableSentry = true; - static const String sentryDns = - 'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143'; static bool renderHtml = true; static bool hideRedactedEvents = false; static bool hideUnknownEvents = true; From deda870e99074dd7b68fc9847ec6f1984d1c5b33 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 11:30:02 +0200 Subject: [PATCH 10/93] fix: noFCM warning dialog --- assets/l10n/intl_en.arb | 6 ++++-- lib/utils/background_push.dart | 2 +- lib/widgets/matrix.dart | 13 +++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 088f7802f..7b30bc1cd 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -1290,7 +1290,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using https://microg.org/ or https://unifiedpush.org/.", + "noGoogleServicesWarning": "Firebase Cloud Messaging doesn't appear to be available on your device. To still receive push notifications, we recommend installing MicroG or Unified Push.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2541,5 +2541,7 @@ "banUserDescription": "The user will be banned from the chat and will not be able to enter the chat again until they are unbanned.", "unbanUserDescription": "The user will be able to enter the chat again if they try.", "kickUserDescription": "The user is kicked out of the chat but not banned. In public chats, the user can rejoin at any time.", - "makeAdminDescription": "Once you make this user admin, you may not be able to undo this as they will then have the same permissions as you." + "makeAdminDescription": "Once you make this user admin, you may not be able to undo this as they will then have the same permissions as you.", + "pushNotificationsNotAvailable": "Push notifications not available", + "learnMore": "Learn more" } diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 1f04d4d69..8853cf623 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -271,7 +271,7 @@ class BackgroundPush { if (matrix == null) { return; } - if (await store.getItemBool(SettingKeys.showNoGoogle, true) == true) { + if (await store.getItemBool(SettingKeys.showNoGoogle, false) == true) { return; } await loadLocale(); diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 2c26de59a..90352db03 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -376,14 +376,19 @@ class MatrixState extends State with WidgetsBindingObserver { final result = await showOkCancelAlertDialog( barrierDismissible: true, context: context, - title: L10n.of(context)!.oopsSomethingWentWrong, + title: L10n.of(context)!.pushNotificationsNotAvailable, message: errorMsg, - okLabel: - link == null ? L10n.of(context)!.ok : L10n.of(context)!.help, + fullyCapitalizedForMaterial: false, + okLabel: link == null + ? L10n.of(context)!.ok + : L10n.of(context)!.learnMore, cancelLabel: L10n.of(context)!.doNotShowAgain, ); if (result == OkCancelResult.ok && link != null) { - launchUrlString(link.toString()); + launchUrlString( + link.toString(), + mode: LaunchMode.externalApplication, + ); } if (result == OkCancelResult.cancel) { await store.setItemBool(SettingKeys.showNoGoogle, true); From 94fc250751df774d8dc91331cd7ba17c3665e8c6 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 11:35:31 +0200 Subject: [PATCH 11/93] refactor: Remove unused config --- lib/config/app_config.dart | 1 - lib/config/setting_keys.dart | 2 -- lib/widgets/matrix.dart | 6 ------ 3 files changed, 9 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 034b66c15..75fa52a1b 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -42,7 +42,6 @@ abstract class AppConfig { static bool hideRedactedEvents = false; static bool hideUnknownEvents = true; static bool hideUnimportantStateEvents = true; - static bool showDirectChatsInSpaces = true; static bool separateChatTypes = false; static bool autoplayImages = true; static bool sendTypingNotifications = true; diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index 14bc6da3c..e7ef76596 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -5,8 +5,6 @@ abstract class SettingKeys { static const String hideUnknownEvents = 'chat.fluffy.hideUnknownEvents'; static const String hideUnimportantStateEvents = 'chat.fluffy.hideUnimportantStateEvents'; - static const String showDirectChatsInSpaces = - 'chat.fluffy.showDirectChatsInSpaces'; static const String separateChatTypes = 'chat.fluffy.separateChatTypes'; static const String sentry = 'sentry'; static const String theme = 'theme'; diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 90352db03..ab328e9b6 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -442,12 +442,6 @@ class MatrixState extends State with WidgetsBindingObserver { store .getItemBool(SettingKeys.hideUnknownEvents, AppConfig.hideUnknownEvents) .then((value) => AppConfig.hideUnknownEvents = value); - store - .getItemBool( - SettingKeys.showDirectChatsInSpaces, - AppConfig.showDirectChatsInSpaces, - ) - .then((value) => AppConfig.showDirectChatsInSpaces = value); store .getItemBool(SettingKeys.separateChatTypes, AppConfig.separateChatTypes) .then((value) => AppConfig.separateChatTypes = value); From a327e8b0e9d990f0ce4fe84c5a6c8707bb99b4f0 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 11:47:31 +0200 Subject: [PATCH 12/93] chore: Limit image file and video picker until we have a background service --- lib/pages/chat/chat.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 23b77c70e..9de368fc8 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -485,7 +485,7 @@ class ChatController extends State { void sendFileAction() async { final result = await AppLock.of(context).pauseWhile( FilePicker.platform.pickFiles( - allowMultiple: true, + allowMultiple: false, withData: true, ), ); @@ -524,12 +524,11 @@ class ChatController extends State { } void sendImageAction() async { - //AppLock.of(context).pauseWhile(); final result = await AppLock.of(context).pauseWhile( FilePicker.platform.pickFiles( type: FileType.image, withData: true, - allowMultiple: true, + allowMultiple: false, ), ); if (result == null || result.files.isEmpty) return; @@ -575,7 +574,10 @@ class ChatController extends State { void openVideoCameraAction() async { // Make sure the textfield is unfocused before opening the camera FocusScope.of(context).requestFocus(FocusNode()); - final file = await ImagePicker().pickVideo(source: ImageSource.camera); + final file = await ImagePicker().pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); if (file == null) return; final bytes = await file.readAsBytes(); await showDialog( From bf1f091f15ae3f8ca8392b75fb5b865fafa42408 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 11:48:57 +0200 Subject: [PATCH 13/93] chore: Revert color changes --- lib/pages/chat/events/message.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index d25785a96..9d8642d68 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -84,7 +84,7 @@ class Message extends StatelessWidget { nextEvent!.senderId == event.senderId && !displayTime; final textColor = ownMessage - ? Theme.of(context).colorScheme.onPrimaryContainer + ? Theme.of(context).colorScheme.onPrimary : Theme.of(context).colorScheme.onBackground; final rowMainAxisAlignment = ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start; @@ -114,7 +114,7 @@ class Message extends StatelessWidget { if (ownMessage) { color = displayEvent.status.isError ? Colors.redAccent - : Theme.of(context).colorScheme.primaryContainer; + : Theme.of(context).colorScheme.primary; } final row = Row( From 69f7429b71d0a768b03de13a7f89e2cbaaf2d6f3 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 11:51:32 +0200 Subject: [PATCH 14/93] chore: Folllow up message bubble color --- lib/pages/chat/events/message.dart | 4 ++-- lib/pages/chat/events/reply_content.dart | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 9d8642d68..d25785a96 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -84,7 +84,7 @@ class Message extends StatelessWidget { nextEvent!.senderId == event.senderId && !displayTime; final textColor = ownMessage - ? Theme.of(context).colorScheme.onPrimary + ? Theme.of(context).colorScheme.onPrimaryContainer : Theme.of(context).colorScheme.onBackground; final rowMainAxisAlignment = ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start; @@ -114,7 +114,7 @@ class Message extends StatelessWidget { if (ownMessage) { color = displayEvent.status.isError ? Colors.redAccent - : Theme.of(context).colorScheme.primary; + : Theme.of(context).colorScheme.primaryContainer; } final row = Row( diff --git a/lib/pages/chat/events/reply_content.dart b/lib/pages/chat/events/reply_content.dart index 6de2e5924..69d244c05 100644 --- a/lib/pages/chat/events/reply_content.dart +++ b/lib/pages/chat/events/reply_content.dart @@ -36,7 +36,7 @@ class ReplyContent extends StatelessWidget { maxLines: 1, style: TextStyle( color: ownMessage - ? Theme.of(context).colorScheme.onPrimary + ? Theme.of(context).colorScheme.onPrimaryContainer : Theme.of(context).colorScheme.onBackground, fontSize: fontSize, ), @@ -49,7 +49,7 @@ class ReplyContent extends StatelessWidget { width: 3, height: fontSize * 2 + 6, color: ownMessage - ? Theme.of(context).colorScheme.onPrimary + ? Theme.of(context).colorScheme.onPrimaryContainer : Theme.of(context).colorScheme.onBackground, ), const SizedBox(width: 6), @@ -68,7 +68,7 @@ class ReplyContent extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.bold, color: ownMessage - ? Theme.of(context).colorScheme.onPrimary + ? Theme.of(context).colorScheme.onPrimaryContainer : Theme.of(context).colorScheme.onBackground, fontSize: fontSize, ), From e2eebe15abe16abf77d8aff0d0e000f80cddc4fa Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 12:36:46 +0200 Subject: [PATCH 15/93] build: Add appid suffix to android debug builds --- android/app/build.gradle | 2 ++ android/app/src/debug/AndroidManifest.xml | 8 -------- android/app/src/profile/AndroidManifest.xml | 8 -------- 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 android/app/src/debug/AndroidManifest.xml delete mode 100644 android/app/src/profile/AndroidManifest.xml diff --git a/android/app/build.gradle b/android/app/build.gradle index 923f82e0a..c652ad3be 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -65,6 +65,8 @@ android { buildTypes { debug { signingConfig signingConfigs.debug + applicationIdSuffix ".debug" + versionNameSuffix "-debug" } release { signingConfig signingConfigs.release diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 4a2a1f5d7..000000000 --- a/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 4a2a1f5d7..000000000 --- a/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - From 4605a92d17c3fb6318b4d9e82e2f894b7a87ec68 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 12:46:22 +0200 Subject: [PATCH 16/93] chore: Adjust bitrate for smaller voice messages --- lib/pages/chat/recording_dialog.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 0629240df..a4b66c88d 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -31,8 +31,7 @@ class RecordingDialogState extends State { final _audioRecorder = AudioRecorder(); final List amplitudeTimeline = []; - static const int bitRate = 64000; - static const int samplingRate = 22050; + static const int bitRate = 16000; Future startRecording() async { try { @@ -61,7 +60,6 @@ class RecordingDialogState extends State { noiseSuppress: true, echoCancel: true, bitRate: bitRate, - sampleRate: samplingRate, ), path: path, ); From eca4825c704e4725ee0944b0bea604b95807e57f Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 13:03:16 +0200 Subject: [PATCH 17/93] build: Update packages and flutter super.key refactoring --- lib/pages/add_story/add_story.dart | 2 +- lib/pages/add_story/add_story_view.dart | 2 +- lib/pages/add_story/invite_story_page.dart | 4 +- lib/pages/archive/archive.dart | 2 +- lib/pages/archive/archive_view.dart | 2 +- lib/pages/bootstrap/bootstrap_dialog.dart | 4 +- lib/pages/chat/add_widget_tile.dart | 2 +- lib/pages/chat/add_widget_tile_view.dart | 3 +- lib/pages/chat/chat.dart | 8 +- lib/pages/chat/chat_app_bar_title.dart | 2 +- lib/pages/chat/chat_emoji_picker.dart | 2 +- lib/pages/chat/chat_event_list.dart | 4 +- lib/pages/chat/chat_input_row.dart | 4 +- lib/pages/chat/chat_view.dart | 2 +- lib/pages/chat/edit_widgets_dialog.dart | 2 +- lib/pages/chat/encryption_button.dart | 2 +- lib/pages/chat/event_info_dialog.dart | 4 +- lib/pages/chat/events/audio_player.dart | 3 +- lib/pages/chat/events/cute_events.dart | 8 +- lib/pages/chat/events/html_message.dart | 4 +- lib/pages/chat/events/image_bubble.dart | 4 +- lib/pages/chat/events/map_bubble.dart | 4 +- lib/pages/chat/events/message.dart | 4 +- lib/pages/chat/events/message_content.dart | 7 +- .../chat/events/message_download_content.dart | 3 +- lib/pages/chat/events/message_reactions.dart | 64 +++++++-------- lib/pages/chat/events/reply_content.dart | 4 +- lib/pages/chat/events/state_message.dart | 2 +- lib/pages/chat/events/sticker.dart | 2 +- .../events/verification_request_content.dart | 4 +- lib/pages/chat/events/video_player.dart | 2 +- lib/pages/chat/input_bar.dart | 4 +- lib/pages/chat/pinned_events.dart | 2 +- lib/pages/chat/reactions_picker.dart | 2 +- lib/pages/chat/recording_dialog.dart | 4 +- lib/pages/chat/reply_display.dart | 2 +- lib/pages/chat/seen_by_row.dart | 17 ++-- lib/pages/chat/send_file_dialog.dart | 4 +- lib/pages/chat/send_location_dialog.dart | 4 +- lib/pages/chat/sticker_picker_dialog.dart | 2 +- lib/pages/chat/tombstone_display.dart | 2 +- lib/pages/chat/typing_indicators.dart | 2 +- lib/pages/chat_details/chat_details.dart | 7 +- lib/pages/chat_details/chat_details_view.dart | 2 +- .../chat_details/participant_list_item.dart | 2 +- .../chat_encryption_settings.dart | 2 +- .../chat_encryption_settings_view.dart | 3 +- lib/pages/chat_list/chat_list.dart | 4 +- lib/pages/chat_list/chat_list_body.dart | 5 +- lib/pages/chat_list/chat_list_header.dart | 2 +- lib/pages/chat_list/chat_list_item.dart | 4 +- lib/pages/chat_list/chat_list_view.dart | 2 +- .../chat_list/client_chooser_button.dart | 79 +++++++++---------- lib/pages/chat_list/navi_rail_item.dart | 4 +- lib/pages/chat_list/search_title.dart | 4 +- lib/pages/chat_list/space_view.dart | 4 +- lib/pages/chat_list/start_chat_fab.dart | 4 +- lib/pages/chat_list/stories_header.dart | 5 +- .../chat_permissions_settings.dart | 2 +- .../chat_permissions_settings_view.dart | 3 +- .../permission_list_tile.dart | 4 +- .../device_settings/device_settings.dart | 2 +- .../device_settings/device_settings_view.dart | 2 +- .../user_device_list_item.dart | 4 +- lib/pages/dialer/dialer.dart | 7 +- lib/pages/dialer/pip/pip_view.dart | 4 +- .../homeserver_picker/homeserver_app_bar.dart | 3 +- .../homeserver_bottom_sheet.dart | 3 +- .../homeserver_picker/homeserver_picker.dart | 2 +- .../homeserver_picker_view.dart | 5 +- lib/pages/image_viewer/image_viewer.dart | 2 +- lib/pages/image_viewer/image_viewer_view.dart | 2 +- .../invitation_selection.dart | 4 +- .../invitation_selection_view.dart | 5 +- .../key_verification_dialog.dart | 4 +- lib/pages/login/login.dart | 2 +- lib/pages/login/login_view.dart | 2 +- lib/pages/new_group/new_group.dart | 2 +- lib/pages/new_group/new_group_view.dart | 2 +- .../new_private_chat/new_private_chat.dart | 2 +- .../new_private_chat_view.dart | 2 +- .../new_private_chat/qr_scanner_modal.dart | 2 +- lib/pages/new_space/new_space.dart | 2 +- lib/pages/new_space/new_space_view.dart | 2 +- lib/pages/settings/settings.dart | 2 +- lib/pages/settings/settings_view.dart | 2 +- lib/pages/settings_3pid/settings_3pid.dart | 2 +- .../settings_3pid/settings_3pid_view.dart | 2 +- lib/pages/settings_chat/settings_chat.dart | 2 +- .../settings_chat/settings_chat_view.dart | 2 +- .../import_archive_dialog.dart | 4 +- .../settings_emotes/settings_emotes.dart | 2 +- .../settings_emotes/settings_emotes_view.dart | 2 +- .../settings_ignore_list.dart | 2 +- .../settings_ignore_list_view.dart | 2 +- .../settings_multiple_emotes.dart | 2 +- .../settings_multiple_emotes_view.dart | 3 +- .../settings_notifications.dart | 2 +- .../settings_notifications_view.dart | 3 +- .../settings_security/settings_security.dart | 2 +- .../settings_security_view.dart | 2 +- .../settings_stories/settings_stories.dart | 2 +- .../settings_stories_view.dart | 2 +- lib/pages/settings_style/settings_style.dart | 2 +- .../settings_style/settings_style_view.dart | 2 +- lib/pages/story/story_page.dart | 2 +- lib/pages/story/story_view.dart | 2 +- .../user_bottom_sheet/user_bottom_sheet.dart | 5 +- .../user_bottom_sheet_view.dart | 2 +- .../flutter_hive_collections_database.dart | 12 +-- lib/widgets/adaptive_flat_button.dart | 4 +- lib/widgets/avatar.dart | 4 +- lib/widgets/chat_settings_popup_menu.dart | 3 +- lib/widgets/connection_status_header.dart | 2 +- lib/widgets/content_banner.dart | 4 +- lib/widgets/fluffy_chat_app.dart | 4 +- lib/widgets/layouts/empty_page.dart | 2 +- lib/widgets/layouts/login_scaffold.dart | 4 +- lib/widgets/layouts/max_width_body.dart | 4 +- lib/widgets/layouts/two_column_layout.dart | 4 +- lib/widgets/log_view.dart | 2 +- lib/widgets/matrix.dart | 4 +- lib/widgets/mxc_image.dart | 4 +- lib/widgets/public_room_bottom_sheet.dart | 4 +- lib/widgets/settings_switch_list_tile.dart | 4 +- lib/widgets/theme_builder.dart | 4 +- lib/widgets/unread_rooms_badge.dart | 4 +- pubspec.lock | 52 ++++++------ pubspec.yaml | 16 ++-- 129 files changed, 294 insertions(+), 324 deletions(-) diff --git a/lib/pages/add_story/add_story.dart b/lib/pages/add_story/add_story.dart index 323d103b4..6f96fdf64 100644 --- a/lib/pages/add_story/add_story.dart +++ b/lib/pages/add_story/add_story.dart @@ -22,7 +22,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import '../../utils/matrix_sdk_extensions/client_stories_extension.dart'; class AddStoryPage extends StatefulWidget { - const AddStoryPage({Key? key}) : super(key: key); + const AddStoryPage({super.key}); @override AddStoryController createState() => AddStoryController(); diff --git a/lib/pages/add_story/add_story_view.dart b/lib/pages/add_story/add_story_view.dart index 12f7dad26..ea828aadf 100644 --- a/lib/pages/add_story/add_story_view.dart +++ b/lib/pages/add_story/add_story_view.dart @@ -8,7 +8,7 @@ import 'add_story.dart'; class AddStoryView extends StatelessWidget { final AddStoryController controller; - const AddStoryView(this.controller, {Key? key}) : super(key: key); + const AddStoryView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/add_story/invite_story_page.dart b/lib/pages/add_story/invite_story_page.dart index c28af4416..c62a0b815 100644 --- a/lib/pages/add_story/invite_story_page.dart +++ b/lib/pages/add_story/invite_story_page.dart @@ -14,8 +14,8 @@ class InviteStoryPage extends StatefulWidget { final Room? storiesRoom; const InviteStoryPage({ required this.storiesRoom, - Key? key, - }) : super(key: key); + super.key, + }); @override InviteStoryPageState createState() => InviteStoryPageState(); diff --git a/lib/pages/archive/archive.dart b/lib/pages/archive/archive.dart index 823fe8362..aba2dace4 100644 --- a/lib/pages/archive/archive.dart +++ b/lib/pages/archive/archive.dart @@ -9,7 +9,7 @@ import 'package:fluffychat/pages/archive/archive_view.dart'; import 'package:fluffychat/widgets/matrix.dart'; class Archive extends StatefulWidget { - const Archive({Key? key}) : super(key: key); + const Archive({super.key}); @override ArchiveController createState() => ArchiveController(); diff --git a/lib/pages/archive/archive_view.dart b/lib/pages/archive/archive_view.dart index d0df690b8..6dde7b720 100644 --- a/lib/pages/archive/archive_view.dart +++ b/lib/pages/archive/archive_view.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/widgets/layouts/max_width_body.dart'; class ArchiveView extends StatelessWidget { final ArchiveController controller; - const ArchiveView(this.controller, {Key? key}) : super(key: key); + const ArchiveView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/bootstrap/bootstrap_dialog.dart b/lib/pages/bootstrap/bootstrap_dialog.dart index 20128b069..8a227cb03 100644 --- a/lib/pages/bootstrap/bootstrap_dialog.dart +++ b/lib/pages/bootstrap/bootstrap_dialog.dart @@ -18,10 +18,10 @@ class BootstrapDialog extends StatefulWidget { final bool wipe; final Client client; const BootstrapDialog({ - Key? key, + super.key, this.wipe = false, required this.client, - }) : super(key: key); + }); Future show(BuildContext context) => showAdaptiveBottomSheet( context: context, diff --git a/lib/pages/chat/add_widget_tile.dart b/lib/pages/chat/add_widget_tile.dart index 89ad8976d..11e3cce5d 100644 --- a/lib/pages/chat/add_widget_tile.dart +++ b/lib/pages/chat/add_widget_tile.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/pages/chat/add_widget_tile_view.dart'; class AddWidgetTile extends StatefulWidget { final Room room; - const AddWidgetTile({Key? key, required this.room}) : super(key: key); + const AddWidgetTile({super.key, required this.room}); @override State createState() => AddWidgetTileState(); diff --git a/lib/pages/chat/add_widget_tile_view.dart b/lib/pages/chat/add_widget_tile_view.dart index 7ce441e84..d7ac53ef2 100644 --- a/lib/pages/chat/add_widget_tile_view.dart +++ b/lib/pages/chat/add_widget_tile_view.dart @@ -8,8 +8,7 @@ import 'package:fluffychat/pages/chat/add_widget_tile.dart'; class AddWidgetTileView extends StatelessWidget { final AddWidgetTileState controller; - const AddWidgetTileView({Key? key, required this.controller}) - : super(key: key); + const AddWidgetTileView({super.key, required this.controller}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 9de368fc8..8bda72667 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -43,9 +43,9 @@ class ChatPage extends StatelessWidget { final String roomId; const ChatPage({ - Key? key, + super.key, required this.roomId, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -95,9 +95,9 @@ class ChatPageWithRoom extends StatefulWidget { final Room room; const ChatPageWithRoom({ - Key? key, + super.key, required this.room, - }) : super(key: key); + }); @override ChatController createState() => ChatController(); diff --git a/lib/pages/chat/chat_app_bar_title.dart b/lib/pages/chat/chat_app_bar_title.dart index ae659fa06..2e755f7de 100644 --- a/lib/pages/chat/chat_app_bar_title.dart +++ b/lib/pages/chat/chat_app_bar_title.dart @@ -9,7 +9,7 @@ import 'package:fluffychat/widgets/avatar.dart'; class ChatAppBarTitle extends StatelessWidget { final ChatController controller; - const ChatAppBarTitle(this.controller, {Key? key}) : super(key: key); + const ChatAppBarTitle(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart index aaad97403..78bdda10c 100644 --- a/lib/pages/chat/chat_emoji_picker.dart +++ b/lib/pages/chat/chat_emoji_picker.dart @@ -8,7 +8,7 @@ import 'chat.dart'; class ChatEmojiPicker extends StatelessWidget { final ChatController controller; - const ChatEmojiPicker(this.controller, {Key? key}) : super(key: key); + const ChatEmojiPicker(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 85cf557a2..2fd094735 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -16,9 +16,9 @@ import 'package:fluffychat/utils/platform_infos.dart'; class ChatEventList extends StatelessWidget { final ChatController controller; const ChatEventList({ - Key? key, + super.key, required this.controller, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 7d2d083a9..c7401d0dd 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -17,7 +17,7 @@ import 'input_bar.dart'; class ChatInputRow extends StatelessWidget { final ChatController controller; - const ChatInputRow(this.controller, {Key? key}) : super(key: key); + const ChatInputRow(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -278,7 +278,7 @@ class ChatInputRow extends StatelessWidget { class _ChatAccountPicker extends StatelessWidget { final ChatController controller; - const _ChatAccountPicker(this.controller, {Key? key}) : super(key: key); + const _ChatAccountPicker(this.controller); void _popupMenuButtonSelected(String mxid, BuildContext context) { final client = Matrix.of(context) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index a92d6ce88..efa0fbde2 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -29,7 +29,7 @@ enum _EventContextAction { info, report } class ChatView extends StatelessWidget { final ChatController controller; - const ChatView(this.controller, {Key? key}) : super(key: key); + const ChatView(this.controller, {super.key}); List _appBarActions(BuildContext context) { if (controller.selectMode) { diff --git a/lib/pages/chat/edit_widgets_dialog.dart b/lib/pages/chat/edit_widgets_dialog.dart index 7d9579a10..2ba163feb 100644 --- a/lib/pages/chat/edit_widgets_dialog.dart +++ b/lib/pages/chat/edit_widgets_dialog.dart @@ -8,7 +8,7 @@ import 'add_widget_tile.dart'; class EditWidgetsDialog extends StatelessWidget { final Room room; - const EditWidgetsDialog({Key? key, required this.room}) : super(key: key); + const EditWidgetsDialog({super.key, required this.room}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/encryption_button.dart b/lib/pages/chat/encryption_button.dart index d5a4481c5..c68a1c976 100644 --- a/lib/pages/chat/encryption_button.dart +++ b/lib/pages/chat/encryption_button.dart @@ -8,7 +8,7 @@ import '../../widgets/matrix.dart'; class EncryptionButton extends StatelessWidget { final Room room; - const EncryptionButton(this.room, {Key? key}) : super(key: key); + const EncryptionButton(this.room, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/event_info_dialog.dart b/lib/pages/chat/event_info_dialog.dart index 5ac29c80c..0846e137e 100644 --- a/lib/pages/chat/event_info_dialog.dart +++ b/lib/pages/chat/event_info_dialog.dart @@ -24,8 +24,8 @@ class EventInfoDialog extends StatelessWidget { const EventInfoDialog({ required this.event, required this.l10n, - Key? key, - }) : super(key: key); + super.key, + }); String get prettyJson { const JsonDecoder decoder = JsonDecoder(); diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart index 1b0586d27..6335b54bf 100644 --- a/lib/pages/chat/events/audio_player.dart +++ b/lib/pages/chat/events/audio_player.dart @@ -20,8 +20,7 @@ class AudioPlayerWidget extends StatefulWidget { static const int wavesCount = 40; - const AudioPlayerWidget(this.event, {this.color = Colors.black, Key? key}) - : super(key: key); + const AudioPlayerWidget(this.event, {this.color = Colors.black, super.key}); @override AudioPlayerState createState() => AudioPlayerState(); diff --git a/lib/pages/chat/events/cute_events.dart b/lib/pages/chat/events/cute_events.dart index 6b5b0f909..6a9424818 100644 --- a/lib/pages/chat/events/cute_events.dart +++ b/lib/pages/chat/events/cute_events.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/config/app_config.dart'; class CuteContent extends StatefulWidget { final Event event; - const CuteContent(this.event, {Key? key}) : super(key: key); + const CuteContent(this.event, {super.key}); @override State createState() => _CuteContentState(); @@ -98,10 +98,10 @@ class CuteEventOverlay extends StatefulWidget { final VoidCallback onAnimationEnd; const CuteEventOverlay({ - Key? key, + super.key, required this.emoji, required this.onAnimationEnd, - }) : super(key: key); + }); @override State createState() => _CuteEventOverlayState(); @@ -179,7 +179,7 @@ class _CuteOverlayContent extends StatelessWidget { static const double size = 64.0; final String emoji; - const _CuteOverlayContent({Key? key, required this.emoji}) : super(key: key); + const _CuteOverlayContent({required this.emoji}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index cd57545a2..8d8993d50 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -20,11 +20,11 @@ class HtmlMessage extends StatelessWidget { final Color textColor; const HtmlMessage({ - Key? key, + super.key, required this.html, required this.room, this.textColor = Colors.black, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/image_bubble.dart b/lib/pages/chat/events/image_bubble.dart index 6974be5f7..8de5f2464 100644 --- a/lib/pages/chat/events/image_bubble.dart +++ b/lib/pages/chat/events/image_bubble.dart @@ -29,8 +29,8 @@ class ImageBubble extends StatelessWidget { this.height = 300, this.animated = false, this.onTap, - Key? key, - }) : super(key: key); + super.key, + }); Widget _buildPlaceholder(BuildContext context) { if (event.messageType == MessageTypes.Sticker) { diff --git a/lib/pages/chat/events/map_bubble.dart b/lib/pages/chat/events/map_bubble.dart index b003bc6ed..c441c7d64 100644 --- a/lib/pages/chat/events/map_bubble.dart +++ b/lib/pages/chat/events/map_bubble.dart @@ -17,8 +17,8 @@ class MapBubble extends StatelessWidget { this.width = 400, this.height = 400, this.radius = 10.0, - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index d25785a96..477af9153 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -41,8 +41,8 @@ class Message extends StatelessWidget { required this.onSwipe, this.selected = false, required this.timeline, - Key? key, - }) : super(key: key); + super.key, + }); /// Indicates wheither the user may use a mouse instead /// of touchscreen. diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 2be0ba826..2177d2de2 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -30,9 +30,9 @@ class MessageContent extends StatelessWidget { const MessageContent( this.event, { this.onInfoTab, - Key? key, + super.key, required this.textColor, - }) : super(key: key); + }); void _verifyOrRequestKey(BuildContext context) async { final l10n = L10n.of(context)!; @@ -311,8 +311,7 @@ class _ButtonContent extends StatelessWidget { required this.textColor, required this.onPressed, required this.fontSize, - Key? key, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/message_download_content.dart b/lib/pages/chat/events/message_download_content.dart index 42dcdff97..767ea8e7b 100644 --- a/lib/pages/chat/events/message_download_content.dart +++ b/lib/pages/chat/events/message_download_content.dart @@ -8,8 +8,7 @@ class MessageDownloadContent extends StatelessWidget { final Event event; final Color textColor; - const MessageDownloadContent(this.event, this.textColor, {Key? key}) - : super(key: key); + const MessageDownloadContent(this.event, this.textColor, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/message_reactions.dart b/lib/pages/chat/events/message_reactions.dart index 110553b9e..58a613968 100644 --- a/lib/pages/chat/events/message_reactions.dart +++ b/lib/pages/chat/events/message_reactions.dart @@ -15,8 +15,7 @@ class MessageReactions extends StatelessWidget { final Event event; final Timeline timeline; - const MessageReactions(this.event, this.timeline, {Key? key}) - : super(key: key); + const MessageReactions(this.event, this.timeline, {super.key}); @override Widget build(BuildContext context) { @@ -50,36 +49,34 @@ class MessageReactions extends StatelessWidget { spacing: 4.0, runSpacing: 4.0, children: [ - ...reactionList - .map( - (r) => _Reaction( - reactionKey: r.key, - count: r.count, - reacted: r.reacted, - onTap: () { - if (r.reacted) { - final evt = allReactionEvents.firstWhereOrNull( - (e) => - e.senderId == e.room.client.userID && - e.content.tryGetMap('m.relates_to')?['key'] == r.key, - ); - if (evt != null) { - showFutureLoadingDialog( - context: context, - future: () => evt.redactEvent(), - ); - } - } else { - event.room.sendReaction(event.eventId, r.key!); - } - }, - onLongPress: () async => await _AdaptableReactorsDialog( - client: client, - reactionEntry: r, - ).show(context), - ), - ) - .toList(), + ...reactionList.map( + (r) => _Reaction( + reactionKey: r.key, + count: r.count, + reacted: r.reacted, + onTap: () { + if (r.reacted) { + final evt = allReactionEvents.firstWhereOrNull( + (e) => + e.senderId == e.room.client.userID && + e.content.tryGetMap('m.relates_to')?['key'] == r.key, + ); + if (evt != null) { + showFutureLoadingDialog( + context: context, + future: () => evt.redactEvent(), + ); + } + } else { + event.room.sendReaction(event.eventId, r.key!); + } + }, + onLongPress: () async => await _AdaptableReactorsDialog( + client: client, + reactionEntry: r, + ).show(context), + ), + ), if (allReactionEvents.any((e) => e.status.isSending)) const SizedBox( width: 28, @@ -190,10 +187,9 @@ class _AdaptableReactorsDialog extends StatelessWidget { final _ReactionEntry? reactionEntry; const _AdaptableReactorsDialog({ - Key? key, this.client, this.reactionEntry, - }) : super(key: key); + }); Future show(BuildContext context) => PlatformInfos.isCupertinoStyle ? showCupertinoDialog( diff --git a/lib/pages/chat/events/reply_content.dart b/lib/pages/chat/events/reply_content.dart index 69d244c05..4c70ae004 100644 --- a/lib/pages/chat/events/reply_content.dart +++ b/lib/pages/chat/events/reply_content.dart @@ -14,9 +14,9 @@ class ReplyContent extends StatelessWidget { const ReplyContent( this.replyEvent, { this.ownMessage = false, - Key? key, + super.key, this.timeline, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/state_message.dart b/lib/pages/chat/events/state_message.dart index d60aa8148..b5e54eba3 100644 --- a/lib/pages/chat/events/state_message.dart +++ b/lib/pages/chat/events/state_message.dart @@ -8,7 +8,7 @@ import '../../../config/app_config.dart'; class StateMessage extends StatelessWidget { final Event event; - const StateMessage(this.event, {Key? key}) : super(key: key); + const StateMessage(this.event, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/sticker.dart b/lib/pages/chat/events/sticker.dart index 041eae7ae..8e52bfe39 100644 --- a/lib/pages/chat/events/sticker.dart +++ b/lib/pages/chat/events/sticker.dart @@ -10,7 +10,7 @@ import 'image_bubble.dart'; class Sticker extends StatefulWidget { final Event event; - const Sticker(this.event, {Key? key}) : super(key: key); + const Sticker(this.event, {super.key}); @override StickerState createState() => StickerState(); diff --git a/lib/pages/chat/events/verification_request_content.dart b/lib/pages/chat/events/verification_request_content.dart index a7271125c..5a0c75f4b 100644 --- a/lib/pages/chat/events/verification_request_content.dart +++ b/lib/pages/chat/events/verification_request_content.dart @@ -12,8 +12,8 @@ class VerificationRequestContent extends StatelessWidget { const VerificationRequestContent({ required this.event, required this.timeline, - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index 3cfd1e703..9f204a59e 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -18,7 +18,7 @@ import '../../../utils/error_reporter.dart'; class EventVideoPlayer extends StatefulWidget { final Event event; - const EventVideoPlayer(this.event, {Key? key}) : super(key: key); + const EventVideoPlayer(this.event, {super.key}); @override EventVideoPlayerState createState() => EventVideoPlayerState(); diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index cdff8e210..c179227a7 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -44,8 +44,8 @@ class InputBar extends StatelessWidget { this.autofocus, this.textInputAction, this.readOnly = false, - Key? key, - }) : super(key: key); + super.key, + }); List> getSuggestions(String text) { if (controller!.selection.baseOffset != diff --git a/lib/pages/chat/pinned_events.dart b/lib/pages/chat/pinned_events.dart index ae81b46e4..e7c90c6e1 100644 --- a/lib/pages/chat/pinned_events.dart +++ b/lib/pages/chat/pinned_events.dart @@ -15,7 +15,7 @@ import 'package:fluffychat/utils/url_launcher.dart'; class PinnedEvents extends StatelessWidget { final ChatController controller; - const PinnedEvents(this.controller, {Key? key}) : super(key: key); + const PinnedEvents(this.controller, {super.key}); Future _displayPinnedEventsDialog( BuildContext context, diff --git a/lib/pages/chat/reactions_picker.dart b/lib/pages/chat/reactions_picker.dart index 4ae5505fd..6434c9970 100644 --- a/lib/pages/chat/reactions_picker.dart +++ b/lib/pages/chat/reactions_picker.dart @@ -11,7 +11,7 @@ import '../../config/themes.dart'; class ReactionsPicker extends StatelessWidget { final ChatController controller; - const ReactionsPicker(this.controller, {Key? key}) : super(key: key); + const ReactionsPicker(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index a4b66c88d..0d18867a4 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -15,8 +15,8 @@ import 'events/audio_player.dart'; class RecordingDialog extends StatefulWidget { static const String recordingFileType = 'm4a'; const RecordingDialog({ - Key? key, - }) : super(key: key); + super.key, + }); @override RecordingDialogState createState() => RecordingDialogState(); diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index 77edee1dc..32bec7c25 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -10,7 +10,7 @@ import 'events/reply_content.dart'; class ReplyDisplay extends StatelessWidget { final ChatController controller; - const ReplyDisplay(this.controller, {Key? key}) : super(key: key); + const ReplyDisplay(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/seen_by_row.dart b/lib/pages/chat/seen_by_row.dart index 47bc4f9ce..faac0db4d 100644 --- a/lib/pages/chat/seen_by_row.dart +++ b/lib/pages/chat/seen_by_row.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class SeenByRow extends StatelessWidget { final ChatController controller; - const SeenByRow(this.controller, {Key? key}) : super(key: key); + const SeenByRow(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -38,14 +38,13 @@ class SeenByRow extends StatelessWidget { ? seenByUsers.sublist(0, maxAvatars) : seenByUsers) .map( - (user) => Avatar( - mxContent: user.avatarUrl, - name: user.calcDisplayname(), - size: 16, - fontSize: 9, - ), - ) - .toList(), + (user) => Avatar( + mxContent: user.avatarUrl, + name: user.calcDisplayname(), + size: 16, + fontSize: 9, + ), + ), if (seenByUsers.length > maxAvatars) SizedBox( width: 16, diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index 11b61db79..57ddf0a28 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -15,8 +15,8 @@ class SendFileDialog extends StatefulWidget { const SendFileDialog({ required this.room, required this.files, - Key? key, - }) : super(key: key); + super.key, + }); @override SendFileDialogState createState() => SendFileDialogState(); diff --git a/lib/pages/chat/send_location_dialog.dart b/lib/pages/chat/send_location_dialog.dart index d29626f56..182362b52 100644 --- a/lib/pages/chat/send_location_dialog.dart +++ b/lib/pages/chat/send_location_dialog.dart @@ -16,8 +16,8 @@ class SendLocationDialog extends StatefulWidget { const SendLocationDialog({ required this.room, - Key? key, - }) : super(key: key); + super.key, + }); @override SendLocationDialogState createState() => SendLocationDialogState(); diff --git a/lib/pages/chat/sticker_picker_dialog.dart b/lib/pages/chat/sticker_picker_dialog.dart index 80cae6b54..16065c916 100644 --- a/lib/pages/chat/sticker_picker_dialog.dart +++ b/lib/pages/chat/sticker_picker_dialog.dart @@ -9,7 +9,7 @@ import 'events/image_bubble.dart'; class StickerPickerDialog extends StatefulWidget { final Room room; - const StickerPickerDialog({required this.room, Key? key}) : super(key: key); + const StickerPickerDialog({required this.room, super.key}); @override StickerPickerDialogState createState() => StickerPickerDialogState(); diff --git a/lib/pages/chat/tombstone_display.dart b/lib/pages/chat/tombstone_display.dart index 50609cfd4..e080a0009 100644 --- a/lib/pages/chat/tombstone_display.dart +++ b/lib/pages/chat/tombstone_display.dart @@ -7,7 +7,7 @@ import 'chat.dart'; class TombstoneDisplay extends StatelessWidget { final ChatController controller; - const TombstoneDisplay(this.controller, {Key? key}) : super(key: key); + const TombstoneDisplay(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat/typing_indicators.dart b/lib/pages/chat/typing_indicators.dart index 3bb3f907e..8101fd76f 100644 --- a/lib/pages/chat/typing_indicators.dart +++ b/lib/pages/chat/typing_indicators.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class TypingIndicators extends StatelessWidget { final ChatController controller; - const TypingIndicators(this.controller, {Key? key}) : super(key: key); + const TypingIndicators(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index b9da5c654..d15eb9d73 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -24,9 +24,9 @@ class ChatDetails extends StatefulWidget { final String roomId; const ChatDetails({ - Key? key, + super.key, required this.roomId, - }) : super(key: key); + }); @override ChatDetailsController createState() => ChatDetailsController(); @@ -109,8 +109,7 @@ class ChatDetailsController extends State { if (adminMode) AlertDialogAction(label: L10n.of(context)!.create, key: 'new'), ...aliases.result! - .map((alias) => AlertDialogAction(key: alias, label: alias)) - .toList(), + .map((alias) => AlertDialogAction(key: alias, label: alias)), ], ); if (select == null) return; diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index b7fd77bc9..1dc923666 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -19,7 +19,7 @@ import '../../utils/url_launcher.dart'; class ChatDetailsView extends StatelessWidget { final ChatDetailsController controller; - const ChatDetailsView(this.controller, {Key? key}) : super(key: key); + const ChatDetailsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat_details/participant_list_item.dart b/lib/pages/chat_details/participant_list_item.dart index 11848cb68..f6986d155 100644 --- a/lib/pages/chat_details/participant_list_item.dart +++ b/lib/pages/chat_details/participant_list_item.dart @@ -10,7 +10,7 @@ import '../user_bottom_sheet/user_bottom_sheet.dart'; class ParticipantListItem extends StatelessWidget { final User user; - const ParticipantListItem(this.user, {Key? key}) : super(key: key); + const ParticipantListItem(this.user, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat_encryption_settings/chat_encryption_settings.dart b/lib/pages/chat_encryption_settings/chat_encryption_settings.dart index b8df84c80..c7aed03a3 100644 --- a/lib/pages/chat_encryption_settings/chat_encryption_settings.dart +++ b/lib/pages/chat_encryption_settings/chat_encryption_settings.dart @@ -12,7 +12,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import '../key_verification/key_verification_dialog.dart'; class ChatEncryptionSettings extends StatefulWidget { - const ChatEncryptionSettings({Key? key}) : super(key: key); + const ChatEncryptionSettings({super.key}); @override ChatEncryptionSettingsController createState() => diff --git a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart index fc78f30ae..f7a19f966 100644 --- a/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart +++ b/lib/pages/chat_encryption_settings/chat_encryption_settings_view.dart @@ -14,8 +14,7 @@ import 'package:fluffychat/widgets/layouts/max_width_body.dart'; class ChatEncryptionSettingsView extends StatelessWidget { final ChatEncryptionSettingsController controller; - const ChatEncryptionSettingsView(this.controller, {Key? key}) - : super(key: key); + const ChatEncryptionSettingsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 79542fd81..9b202c49d 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -61,10 +61,10 @@ class ChatList extends StatefulWidget { final String? activeChat; const ChatList({ - Key? key, + super.key, this.displayNavigationRail = false, required this.activeChat, - }) : super(key: key); + }); @override ChatListController createState() => ChatListController(); diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 69b438e8c..520adaaa3 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -25,7 +25,7 @@ import 'chat_list_header.dart'; class ChatListViewBody extends StatelessWidget { final ChatListController controller; - const ChatListViewBody(this.controller, {Key? key}) : super(key: key); + const ChatListViewBody(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -313,8 +313,7 @@ class _SearchItem extends StatelessWidget { required this.title, this.avatar, required this.onPressed, - Key? key, - }) : super(key: key); + }); @override Widget build(BuildContext context) => InkWell( diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index f9675a8dd..1329c8a83 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -10,7 +10,7 @@ import '../../widgets/matrix.dart'; class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { final ChatListController controller; - const ChatListHeader({Key? key, required this.controller}) : super(key: key); + const ChatListHeader({super.key, required this.controller}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 41a4aec35..2d6a97499 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -30,8 +30,8 @@ class ChatListItem extends StatelessWidget { this.selected = false, this.onTap, this.onLongPress, - Key? key, - }) : super(key: key); + super.key, + }); void clickAction(BuildContext context) async { if (onTap != null) return onTap!(); diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index b60371746..e8b5e958f 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -20,7 +20,7 @@ import 'start_chat_fab.dart'; class ChatListView extends StatelessWidget { final ChatListController controller; - const ChatListView(this.controller, {Key? key}) : super(key: key); + const ChatListView(this.controller, {super.key}); List getNavigationDestinations(BuildContext context) { final badgePosition = BadgePosition.topEnd(top: -12, end: -8); diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 2791a4a0c..f2394bb69 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -15,7 +15,7 @@ import 'chat_list.dart'; class ClientChooserButton extends StatelessWidget { final ChatListController controller; - const ClientChooserButton(this.controller, {Key? key}) : super(key: key); + const ClientChooserButton(this.controller, {super.key}); List> _bundleMenuItems(BuildContext context) { final matrix = Matrix.of(context); @@ -112,48 +112,45 @@ class ClientChooserButton extends StatelessWidget { ], ), ), - ...matrix.accountBundles[bundle]! - .map( - (client) => PopupMenuItem( - value: client, - child: FutureBuilder( - // analyzer does not understand this type cast for error - // handling - // - // ignore: unnecessary_cast - future: (client!.fetchOwnProfile() as Future) - .onError((e, s) => null), - builder: (context, snapshot) => Row( - children: [ - Avatar( - mxContent: snapshot.data?.avatarUrl, - name: snapshot.data?.displayName ?? - client.userID!.localpart, - size: 32, - fontSize: 12, - ), - const SizedBox(width: 12), - Expanded( - child: Text( - snapshot.data?.displayName ?? - client.userID!.localpart!, - overflow: TextOverflow.ellipsis, - ), - ), - const SizedBox(width: 12), - IconButton( - icon: const Icon(Icons.edit_outlined), - onPressed: () => controller.editBundlesForAccount( - client.userID, - bundle, - ), - ), - ], + ...matrix.accountBundles[bundle]!.map( + (client) => PopupMenuItem( + value: client, + child: FutureBuilder( + // analyzer does not understand this type cast for error + // handling + // + // ignore: unnecessary_cast + future: (client!.fetchOwnProfile() as Future) + .onError((e, s) => null), + builder: (context, snapshot) => Row( + children: [ + Avatar( + mxContent: snapshot.data?.avatarUrl, + name: + snapshot.data?.displayName ?? client.userID!.localpart, + size: 32, + fontSize: 12, ), - ), + const SizedBox(width: 12), + Expanded( + child: Text( + snapshot.data?.displayName ?? client.userID!.localpart!, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 12), + IconButton( + icon: const Icon(Icons.edit_outlined), + onPressed: () => controller.editBundlesForAccount( + client.userID, + bundle, + ), + ), + ], ), - ) - .toList(), + ), + ), + ), ], PopupMenuItem( value: SettingsAction.addAccount, diff --git a/lib/pages/chat_list/navi_rail_item.dart b/lib/pages/chat_list/navi_rail_item.dart index 7eef55468..2004f3568 100644 --- a/lib/pages/chat_list/navi_rail_item.dart +++ b/lib/pages/chat_list/navi_rail_item.dart @@ -16,8 +16,8 @@ class NaviRailItem extends StatefulWidget { required this.onTap, required this.icon, this.selectedIcon, - Key? key, - }) : super(key: key); + super.key, + }); @override State createState() => _NaviRailItemState(); diff --git a/lib/pages/chat_list/search_title.dart b/lib/pages/chat_list/search_title.dart index 3ed0e64d4..62bcfb684 100644 --- a/lib/pages/chat_list/search_title.dart +++ b/lib/pages/chat_list/search_title.dart @@ -13,8 +13,8 @@ class SearchTitle extends StatelessWidget { this.trailing, this.onTap, this.color, - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) => Material( diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 8bbb63691..2dc7604ee 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -21,9 +21,9 @@ class SpaceView extends StatefulWidget { final ScrollController scrollController; const SpaceView( this.controller, { - Key? key, + super.key, required this.scrollController, - }) : super(key: key); + }); @override State createState() => _SpaceViewState(); diff --git a/lib/pages/chat_list/start_chat_fab.dart b/lib/pages/chat_list/start_chat_fab.dart index f702ad69d..192161477 100644 --- a/lib/pages/chat_list/start_chat_fab.dart +++ b/lib/pages/chat_list/start_chat_fab.dart @@ -12,11 +12,11 @@ class StartChatFloatingActionButton extends StatelessWidget { final bool roomsIsEmpty; const StartChatFloatingActionButton({ - Key? key, + super.key, required this.activeFilter, required this.scrolledToTop, required this.roomsIsEmpty, - }) : super(key: key); + }); void _onPressed(BuildContext context) { switch (activeFilter) { diff --git a/lib/pages/chat_list/stories_header.dart b/lib/pages/chat_list/stories_header.dart index ab6caabbc..d5325b91f 100644 --- a/lib/pages/chat_list/stories_header.dart +++ b/lib/pages/chat_list/stories_header.dart @@ -23,7 +23,7 @@ enum ContextualRoomAction { class StoriesHeader extends StatelessWidget { final String filter; - const StoriesHeader({required this.filter, Key? key}) : super(key: key); + const StoriesHeader({required this.filter, super.key}); void _addToStoryAction(BuildContext context) => context.go('/rooms/stories/create'); @@ -186,8 +186,7 @@ class _StoryButton extends StatefulWidget { this.hasPosts = true, this.unread = false, this.onLongPressed, - Key? key, - }) : super(key: key); + }); @override State<_StoryButton> createState() => _StoryButtonState(); diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart index 45983574e..a6cdcbef0 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings.dart @@ -13,7 +13,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/permission_slider_dialog.dart'; class ChatPermissionsSettings extends StatefulWidget { - const ChatPermissionsSettings({Key? key}) : super(key: key); + const ChatPermissionsSettings({super.key}); @override ChatPermissionsSettingsController createState() => diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart index fd10b9af0..072e20405 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart @@ -11,8 +11,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class ChatPermissionsSettingsView extends StatelessWidget { final ChatPermissionsSettingsController controller; - const ChatPermissionsSettingsView(this.controller, {Key? key}) - : super(key: key); + const ChatPermissionsSettingsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/chat_permissions_settings/permission_list_tile.dart b/lib/pages/chat_permissions_settings/permission_list_tile.dart index 0ac68f44c..a9d159854 100644 --- a/lib/pages/chat_permissions_settings/permission_list_tile.dart +++ b/lib/pages/chat_permissions_settings/permission_list_tile.dart @@ -10,12 +10,12 @@ class PermissionsListTile extends StatelessWidget { final void Function()? onTap; const PermissionsListTile({ - Key? key, + super.key, required this.permissionKey, required this.permission, this.category, this.onTap, - }) : super(key: key); + }); String getLocalizedPowerLevelString(BuildContext context) { if (category == null) { diff --git a/lib/pages/device_settings/device_settings.dart b/lib/pages/device_settings/device_settings.dart index 25011c5fc..fba065bc2 100644 --- a/lib/pages/device_settings/device_settings.dart +++ b/lib/pages/device_settings/device_settings.dart @@ -13,7 +13,7 @@ import 'package:fluffychat/utils/localized_exception_extension.dart'; import '../../widgets/matrix.dart'; class DevicesSettings extends StatefulWidget { - const DevicesSettings({Key? key}) : super(key: key); + const DevicesSettings({super.key}); @override DevicesSettingsController createState() => DevicesSettingsController(); diff --git a/lib/pages/device_settings/device_settings_view.dart b/lib/pages/device_settings/device_settings_view.dart index b03f0dc7b..4094c0227 100644 --- a/lib/pages/device_settings/device_settings_view.dart +++ b/lib/pages/device_settings/device_settings_view.dart @@ -9,7 +9,7 @@ import 'user_device_list_item.dart'; class DevicesSettingsView extends StatelessWidget { final DevicesSettingsController controller; - const DevicesSettingsView(this.controller, {Key? key}) : super(key: key); + const DevicesSettingsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/device_settings/user_device_list_item.dart b/lib/pages/device_settings/user_device_list_item.dart index 887b3b445..db793ac35 100644 --- a/lib/pages/device_settings/user_device_list_item.dart +++ b/lib/pages/device_settings/user_device_list_item.dart @@ -31,8 +31,8 @@ class UserDeviceListItem extends StatelessWidget { required this.verify, required this.block, required this.unblock, - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/dialer/dialer.dart b/lib/pages/dialer/dialer.dart index 1af65ce9d..a8e5a9202 100644 --- a/lib/pages/dialer/dialer.dart +++ b/lib/pages/dialer/dialer.dart @@ -38,10 +38,9 @@ import 'pip/pip_view.dart'; class _StreamView extends StatelessWidget { const _StreamView( this.wrappedStream, { - Key? key, this.mainView = false, required this.matrixClient, - }) : super(key: key); + }); final WrappedMediaStream wrappedStream; final Client matrixClient; @@ -128,8 +127,8 @@ class Calling extends StatefulWidget { required this.client, required this.callId, this.onClear, - Key? key, - }) : super(key: key); + super.key, + }); @override MyCallingPage createState() => MyCallingPage(); diff --git a/lib/pages/dialer/pip/pip_view.dart b/lib/pages/dialer/pip/pip_view.dart index 74008aaf1..5396c9136 100644 --- a/lib/pages/dialer/pip/pip_view.dart +++ b/lib/pages/dialer/pip/pip_view.dart @@ -15,13 +15,13 @@ class PIPView extends StatefulWidget { ) builder; const PIPView({ - Key? key, + super.key, required this.builder, this.initialCorner = PIPViewCorner.topRight, this.floatingWidth, this.floatingHeight, this.avoidKeyboard = true, - }) : super(key: key); + }); @override PIPViewState createState() => PIPViewState(); diff --git a/lib/pages/homeserver_picker/homeserver_app_bar.dart b/lib/pages/homeserver_picker/homeserver_app_bar.dart index d1fd7149e..e29aeb7e6 100644 --- a/lib/pages/homeserver_picker/homeserver_app_bar.dart +++ b/lib/pages/homeserver_picker/homeserver_app_bar.dart @@ -11,8 +11,7 @@ import 'homeserver_picker.dart'; class HomeserverAppBar extends StatelessWidget { final HomeserverPickerController controller; - const HomeserverAppBar({Key? key, required this.controller}) - : super(key: key); + const HomeserverAppBar({super.key, required this.controller}); @override Widget build(BuildContext context) { diff --git a/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart b/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart index d7cd534bf..0fc6d0112 100644 --- a/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart +++ b/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart @@ -5,8 +5,7 @@ import 'package:url_launcher/url_launcher_string.dart'; class HomeserverBottomSheet extends StatelessWidget { final HomeserverBenchmarkResult homeserver; - const HomeserverBottomSheet({required this.homeserver, Key? key}) - : super(key: key); + const HomeserverBottomSheet({required this.homeserver, super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index a5df23a51..e29cabccb 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -25,7 +25,7 @@ import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; class HomeserverPicker extends StatefulWidget { - const HomeserverPicker({Key? key}) : super(key: key); + const HomeserverPicker({super.key}); @override HomeserverPickerController createState() => HomeserverPickerController(); diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index 3cd9d713e..6d25e9ea4 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -13,7 +13,7 @@ import 'homeserver_picker.dart'; class HomeserverPickerView extends StatelessWidget { final HomeserverPickerController controller; - const HomeserverPickerView(this.controller, {Key? key}) : super(key: key); + const HomeserverPickerView(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -172,8 +172,7 @@ class _LoginButton extends StatelessWidget { required this.icon, required this.label, required this.onPressed, - Key? key, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/image_viewer/image_viewer.dart b/lib/pages/image_viewer/image_viewer.dart index ac4505c40..94ab19dd4 100644 --- a/lib/pages/image_viewer/image_viewer.dart +++ b/lib/pages/image_viewer/image_viewer.dart @@ -11,7 +11,7 @@ import '../../utils/matrix_sdk_extensions/event_extension.dart'; class ImageViewer extends StatefulWidget { final Event event; - const ImageViewer(this.event, {Key? key}) : super(key: key); + const ImageViewer(this.event, {super.key}); @override ImageViewerController createState() => ImageViewerController(); diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index 3bd6166c7..7646c8a60 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -9,7 +9,7 @@ import 'image_viewer.dart'; class ImageViewerView extends StatelessWidget { final ImageViewerController controller; - const ImageViewerView(this.controller, {Key? key}) : super(key: key); + const ImageViewerView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/invitation_selection/invitation_selection.dart b/lib/pages/invitation_selection/invitation_selection.dart index 74d301630..15078735d 100644 --- a/lib/pages/invitation_selection/invitation_selection.dart +++ b/lib/pages/invitation_selection/invitation_selection.dart @@ -15,9 +15,9 @@ import '../../utils/localized_exception_extension.dart'; class InvitationSelection extends StatefulWidget { final String roomId; const InvitationSelection({ - Key? key, + super.key, required this.roomId, - }) : super(key: key); + }); @override InvitationSelectionController createState() => diff --git a/lib/pages/invitation_selection/invitation_selection_view.dart b/lib/pages/invitation_selection/invitation_selection_view.dart index 202e4b378..8a5a55dd4 100644 --- a/lib/pages/invitation_selection/invitation_selection_view.dart +++ b/lib/pages/invitation_selection/invitation_selection_view.dart @@ -11,7 +11,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class InvitationSelectionView extends StatelessWidget { final InvitationSelectionController controller; - const InvitationSelectionView(this.controller, {Key? key}) : super(key: key); + const InvitationSelectionView(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -142,13 +142,12 @@ class _InviteContactListTile extends StatelessWidget { final void Function() onTap; const _InviteContactListTile({ - Key? key, required this.userId, required this.displayname, required this.avatarUrl, required this.isMember, required this.onTap, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/key_verification/key_verification_dialog.dart b/lib/pages/key_verification/key_verification_dialog.dart index bfed12be7..6c303b859 100644 --- a/lib/pages/key_verification/key_verification_dialog.dart +++ b/lib/pages/key_verification/key_verification_dialog.dart @@ -23,9 +23,9 @@ class KeyVerificationDialog extends StatefulWidget { final KeyVerification request; const KeyVerificationDialog({ - Key? key, + super.key, required this.request, - }) : super(key: key); + }); @override KeyVerificationPageState createState() => KeyVerificationPageState(); diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index d4d5d1f26..49f666d10 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -13,7 +13,7 @@ import '../../utils/platform_infos.dart'; import 'login_view.dart'; class Login extends StatefulWidget { - const Login({Key? key}) : super(key: key); + const Login({super.key}); @override LoginController createState() => LoginController(); diff --git a/lib/pages/login/login_view.dart b/lib/pages/login/login_view.dart index 50fa1e383..503ab6ac4 100644 --- a/lib/pages/login/login_view.dart +++ b/lib/pages/login/login_view.dart @@ -9,7 +9,7 @@ import 'login.dart'; class LoginView extends StatelessWidget { final LoginController controller; - const LoginView(this.controller, {Key? key}) : super(key: key); + const LoginView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/new_group/new_group.dart b/lib/pages/new_group/new_group.dart index 70ea22eff..d00a71e8a 100644 --- a/lib/pages/new_group/new_group.dart +++ b/lib/pages/new_group/new_group.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/pages/new_group/new_group_view.dart'; import 'package:fluffychat/widgets/matrix.dart'; class NewGroup extends StatefulWidget { - const NewGroup({Key? key}) : super(key: key); + const NewGroup({super.key}); @override NewGroupController createState() => NewGroupController(); diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart index 7b8dd7b1c..ee947e6fc 100644 --- a/lib/pages/new_group/new_group_view.dart +++ b/lib/pages/new_group/new_group_view.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/widgets/layouts/max_width_body.dart'; class NewGroupView extends StatelessWidget { final NewGroupController controller; - const NewGroupView(this.controller, {Key? key}) : super(key: key); + const NewGroupView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/new_private_chat/new_private_chat.dart b/lib/pages/new_private_chat/new_private_chat.dart index 179d4aab1..f392dca9d 100644 --- a/lib/pages/new_private_chat/new_private_chat.dart +++ b/lib/pages/new_private_chat/new_private_chat.dart @@ -14,7 +14,7 @@ import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/widgets/matrix.dart'; class NewPrivateChat extends StatefulWidget { - const NewPrivateChat({Key? key}) : super(key: key); + const NewPrivateChat({super.key}); @override NewPrivateChatController createState() => NewPrivateChatController(); diff --git a/lib/pages/new_private_chat/new_private_chat_view.dart b/lib/pages/new_private_chat/new_private_chat_view.dart index 4672633b6..f6aecbde0 100644 --- a/lib/pages/new_private_chat/new_private_chat_view.dart +++ b/lib/pages/new_private_chat/new_private_chat_view.dart @@ -14,7 +14,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class NewPrivateChatView extends StatelessWidget { final NewPrivateChatController controller; - const NewPrivateChatView(this.controller, {Key? key}) : super(key: key); + const NewPrivateChatView(this.controller, {super.key}); static const double _qrCodePadding = 8; diff --git a/lib/pages/new_private_chat/qr_scanner_modal.dart b/lib/pages/new_private_chat/qr_scanner_modal.dart index 8c125e460..6155ebb23 100644 --- a/lib/pages/new_private_chat/qr_scanner_modal.dart +++ b/lib/pages/new_private_chat/qr_scanner_modal.dart @@ -9,7 +9,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:fluffychat/utils/url_launcher.dart'; class QrScannerModal extends StatefulWidget { - const QrScannerModal({Key? key}) : super(key: key); + const QrScannerModal({super.key}); @override QrScannerModalState createState() => QrScannerModalState(); diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 9d4a8272a..bd523941a 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -9,7 +9,7 @@ import 'package:fluffychat/pages/new_space/new_space_view.dart'; import 'package:fluffychat/widgets/matrix.dart'; class NewSpace extends StatefulWidget { - const NewSpace({Key? key}) : super(key: key); + const NewSpace({super.key}); @override NewSpaceController createState() => NewSpaceController(); diff --git a/lib/pages/new_space/new_space_view.dart b/lib/pages/new_space/new_space_view.dart index ce4f967c9..02c2571e3 100644 --- a/lib/pages/new_space/new_space_view.dart +++ b/lib/pages/new_space/new_space_view.dart @@ -8,7 +8,7 @@ import 'new_space.dart'; class NewSpaceView extends StatelessWidget { final NewSpaceController controller; - const NewSpaceView(this.controller, {Key? key}) : super(key: key); + const NewSpaceView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index b4b9c2f1f..97bbec66d 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -17,7 +17,7 @@ import '../bootstrap/bootstrap_dialog.dart'; import 'settings_view.dart'; class Settings extends StatefulWidget { - const Settings({Key? key}) : super(key: key); + const Settings({super.key}); @override SettingsController createState() => SettingsController(); diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index c7277e023..05af6fda9 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -15,7 +15,7 @@ import 'settings.dart'; class SettingsView extends StatelessWidget { final SettingsController controller; - const SettingsView(this.controller, {Key? key}) : super(key: key); + const SettingsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_3pid/settings_3pid.dart b/lib/pages/settings_3pid/settings_3pid.dart index ff3256934..d46cac2a9 100644 --- a/lib/pages/settings_3pid/settings_3pid.dart +++ b/lib/pages/settings_3pid/settings_3pid.dart @@ -11,7 +11,7 @@ import 'settings_3pid_view.dart'; class Settings3Pid extends StatefulWidget { static int sendAttempt = 0; - const Settings3Pid({Key? key}) : super(key: key); + const Settings3Pid({super.key}); @override Settings3PidController createState() => Settings3PidController(); diff --git a/lib/pages/settings_3pid/settings_3pid_view.dart b/lib/pages/settings_3pid/settings_3pid_view.dart index 245077d9c..1d1fed36c 100644 --- a/lib/pages/settings_3pid/settings_3pid_view.dart +++ b/lib/pages/settings_3pid/settings_3pid_view.dart @@ -10,7 +10,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class Settings3PidView extends StatelessWidget { final Settings3PidController controller; - const Settings3PidView(this.controller, {Key? key}) : super(key: key); + const Settings3PidView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_chat/settings_chat.dart b/lib/pages/settings_chat/settings_chat.dart index b25941669..1c1035559 100644 --- a/lib/pages/settings_chat/settings_chat.dart +++ b/lib/pages/settings_chat/settings_chat.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'settings_chat_view.dart'; class SettingsChat extends StatefulWidget { - const SettingsChat({Key? key}) : super(key: key); + const SettingsChat({super.key}); @override SettingsChatController createState() => SettingsChatController(); diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 76522bffa..1d0f6686b 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -14,7 +14,7 @@ import 'settings_chat.dart'; class SettingsChatView extends StatelessWidget { final SettingsChatController controller; - const SettingsChatView(this.controller, {Key? key}) : super(key: key); + const SettingsChatView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_emotes/import_archive_dialog.dart b/lib/pages/settings_emotes/import_archive_dialog.dart index faf069120..fd25fc9c6 100644 --- a/lib/pages/settings_emotes/import_archive_dialog.dart +++ b/lib/pages/settings_emotes/import_archive_dialog.dart @@ -204,11 +204,11 @@ class _EmojiImportPreview extends StatefulWidget { final VoidCallback onRemove; const _EmojiImportPreview({ - Key? key, + super.key, required this.entry, required this.onNameChanged, required this.onRemove, - }) : super(key: key); + }); @override State<_EmojiImportPreview> createState() => _EmojiImportPreviewState(); diff --git a/lib/pages/settings_emotes/settings_emotes.dart b/lib/pages/settings_emotes/settings_emotes.dart index 98d5a214d..92f2d7ea6 100644 --- a/lib/pages/settings_emotes/settings_emotes.dart +++ b/lib/pages/settings_emotes/settings_emotes.dart @@ -23,7 +23,7 @@ import 'package:archive/archive.dart' if (dart.library.io) 'package:archive/archive_io.dart'; class EmotesSettings extends StatefulWidget { - const EmotesSettings({Key? key}) : super(key: key); + const EmotesSettings({super.key}); @override EmotesSettingsController createState() => EmotesSettingsController(); diff --git a/lib/pages/settings_emotes/settings_emotes_view.dart b/lib/pages/settings_emotes/settings_emotes_view.dart index 5803ee94a..acb93788e 100644 --- a/lib/pages/settings_emotes/settings_emotes_view.dart +++ b/lib/pages/settings_emotes/settings_emotes_view.dart @@ -15,7 +15,7 @@ enum PopupMenuEmojiActions { import, export } class EmotesSettingsView extends StatelessWidget { final EmotesSettingsController controller; - const EmotesSettingsView(this.controller, {Key? key}) : super(key: key); + const EmotesSettingsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_ignore_list/settings_ignore_list.dart b/lib/pages/settings_ignore_list/settings_ignore_list.dart index cf0713aa9..0b27cee3b 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list.dart @@ -8,7 +8,7 @@ import 'settings_ignore_list_view.dart'; class SettingsIgnoreList extends StatefulWidget { final String? initialUserId; - const SettingsIgnoreList({Key? key, this.initialUserId}) : super(key: key); + const SettingsIgnoreList({super.key, this.initialUserId}); @override SettingsIgnoreListController createState() => SettingsIgnoreListController(); diff --git a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart index 438e530b3..8c7be9c4e 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart @@ -12,7 +12,7 @@ import 'settings_ignore_list.dart'; class SettingsIgnoreListView extends StatelessWidget { final SettingsIgnoreListController controller; - const SettingsIgnoreListView(this.controller, {Key? key}) : super(key: key); + const SettingsIgnoreListView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_multiple_emotes/settings_multiple_emotes.dart b/lib/pages/settings_multiple_emotes/settings_multiple_emotes.dart index b4901832d..276bc08ad 100644 --- a/lib/pages/settings_multiple_emotes/settings_multiple_emotes.dart +++ b/lib/pages/settings_multiple_emotes/settings_multiple_emotes.dart @@ -5,7 +5,7 @@ import 'package:go_router/go_router.dart'; import 'settings_multiple_emotes_view.dart'; class MultipleEmotesSettings extends StatefulWidget { - const MultipleEmotesSettings({Key? key}) : super(key: key); + const MultipleEmotesSettings({super.key}); @override MultipleEmotesSettingsController createState() => diff --git a/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart b/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart index b699e37bf..8d62549c7 100644 --- a/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart +++ b/lib/pages/settings_multiple_emotes/settings_multiple_emotes_view.dart @@ -10,8 +10,7 @@ import 'package:fluffychat/widgets/matrix.dart'; class MultipleEmotesSettingsView extends StatelessWidget { final MultipleEmotesSettingsController controller; - const MultipleEmotesSettingsView(this.controller, {Key? key}) - : super(key: key); + const MultipleEmotesSettingsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart index ff9dcbd35..6ab4740af 100644 --- a/lib/pages/settings_notifications/settings_notifications.dart +++ b/lib/pages/settings_notifications/settings_notifications.dart @@ -54,7 +54,7 @@ class NotificationSettingsItem { } class SettingsNotifications extends StatefulWidget { - const SettingsNotifications({Key? key}) : super(key: key); + const SettingsNotifications({super.key}); @override SettingsNotificationsController createState() => diff --git a/lib/pages/settings_notifications/settings_notifications_view.dart b/lib/pages/settings_notifications/settings_notifications_view.dart index 0cdef98dd..65ddac162 100644 --- a/lib/pages/settings_notifications/settings_notifications_view.dart +++ b/lib/pages/settings_notifications/settings_notifications_view.dart @@ -12,8 +12,7 @@ import 'settings_notifications.dart'; class SettingsNotificationsView extends StatelessWidget { final SettingsNotificationsController controller; - const SettingsNotificationsView(this.controller, {Key? key}) - : super(key: key); + const SettingsNotificationsView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_security/settings_security.dart b/lib/pages/settings_security/settings_security.dart index b3a58ca68..60f746e89 100644 --- a/lib/pages/settings_security/settings_security.dart +++ b/lib/pages/settings_security/settings_security.dart @@ -16,7 +16,7 @@ import '../bootstrap/bootstrap_dialog.dart'; import 'settings_security_view.dart'; class SettingsSecurity extends StatefulWidget { - const SettingsSecurity({Key? key}) : super(key: key); + const SettingsSecurity({super.key}); @override SettingsSecurityController createState() => SettingsSecurityController(); diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index fc241a850..6f73d4de3 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -11,7 +11,7 @@ import 'settings_security.dart'; class SettingsSecurityView extends StatelessWidget { final SettingsSecurityController controller; - const SettingsSecurityView(this.controller, {Key? key}) : super(key: key); + const SettingsSecurityView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_stories/settings_stories.dart b/lib/pages/settings_stories/settings_stories.dart index 802c07d0c..16e9208bb 100644 --- a/lib/pages/settings_stories/settings_stories.dart +++ b/lib/pages/settings_stories/settings_stories.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import '../../utils/matrix_sdk_extensions/client_stories_extension.dart'; class SettingsStories extends StatefulWidget { - const SettingsStories({Key? key}) : super(key: key); + const SettingsStories({super.key}); @override SettingsStoriesController createState() => SettingsStoriesController(); diff --git a/lib/pages/settings_stories/settings_stories_view.dart b/lib/pages/settings_stories/settings_stories_view.dart index 31524eef4..f5d71fab3 100644 --- a/lib/pages/settings_stories/settings_stories_view.dart +++ b/lib/pages/settings_stories/settings_stories_view.dart @@ -8,7 +8,7 @@ import 'package:fluffychat/widgets/avatar.dart'; class SettingsStoriesView extends StatelessWidget { final SettingsStoriesController controller; - const SettingsStoriesView(this.controller, {Key? key}) : super(key: key); + const SettingsStoriesView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index 619dc2c6d..727839892 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -11,7 +11,7 @@ import '../../widgets/matrix.dart'; import 'settings_style_view.dart'; class SettingsStyle extends StatefulWidget { - const SettingsStyle({Key? key}) : super(key: key); + const SettingsStyle({super.key}); @override SettingsStyleController createState() => SettingsStyleController(); diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index f7646fdc2..9104912e7 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -11,7 +11,7 @@ import 'settings_style.dart'; class SettingsStyleView extends StatelessWidget { final SettingsStyleController controller; - const SettingsStyleView(this.controller, {Key? key}) : super(key: key); + const SettingsStyleView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/story/story_page.dart b/lib/pages/story/story_page.dart index 1fafdf0fa..1a0333c2b 100644 --- a/lib/pages/story/story_page.dart +++ b/lib/pages/story/story_page.dart @@ -24,7 +24,7 @@ import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; class StoryPage extends StatefulWidget { - const StoryPage({Key? key}) : super(key: key); + const StoryPage({super.key}); @override StoryPageController createState() => StoryPageController(); diff --git a/lib/pages/story/story_view.dart b/lib/pages/story/story_view.dart index 03e25502b..913becfbf 100644 --- a/lib/pages/story/story_view.dart +++ b/lib/pages/story/story_view.dart @@ -19,7 +19,7 @@ import '../../config/themes.dart'; class StoryView extends StatelessWidget { final StoryPageController controller; - const StoryView(this.controller, {Key? key}) : super(key: key); + const StoryView(this.controller, {super.key}); static const List textShadows = [ Shadow( diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index 7234317a8..9dbbb84d2 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -74,14 +74,13 @@ class UserBottomSheet extends StatefulWidget { final Object? profileSearchError; const UserBottomSheet({ - Key? key, + super.key, this.user, this.profile, required this.outerContext, this.onMention, this.profileSearchError, - }) : assert(user != null || profile != null), - super(key: key); + }) : assert(user != null || profile != null); @override UserBottomSheetController createState() => UserBottomSheetController(); diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart index 1926193d7..544fc92cc 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart @@ -11,7 +11,7 @@ import 'user_bottom_sheet.dart'; class UserBottomSheetView extends StatelessWidget { final UserBottomSheetController controller; - const UserBottomSheetView(this.controller, {Key? key}) : super(key: key); + const UserBottomSheetView(this.controller, {super.key}); @override Widget build(BuildContext context) { diff --git a/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart b/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart index 241ae1a6f..e930f90c0 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart @@ -12,14 +12,10 @@ import 'package:universal_html/html.dart' as html; class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase { FlutterHiveCollectionsDatabase( - String name, - String path, { - HiveCipher? key, - }) : super( - name, - path, - key: key, - ); + super.name, + String super.path, { + super.key, + }); static const String _cipherStorageKey = 'hive_encryption_key'; diff --git a/lib/widgets/adaptive_flat_button.dart b/lib/widgets/adaptive_flat_button.dart index 7faa7eed4..faed7e951 100644 --- a/lib/widgets/adaptive_flat_button.dart +++ b/lib/widgets/adaptive_flat_button.dart @@ -9,11 +9,11 @@ class AdaptiveFlatButton extends StatelessWidget { final void Function()? onPressed; const AdaptiveFlatButton({ - Key? key, + super.key, required this.label, this.textColor, this.onPressed, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index 7d43f8f3c..be1424da8 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -21,8 +21,8 @@ class Avatar extends StatelessWidget { this.onTap, this.client, this.fontSize = 18, - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) { diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index bc7e929ae..54a2fbf08 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -16,8 +16,7 @@ class ChatSettingsPopupMenu extends StatefulWidget { final Room room; final bool displayChatDetails; - const ChatSettingsPopupMenu(this.room, this.displayChatDetails, {Key? key}) - : super(key: key); + const ChatSettingsPopupMenu(this.room, this.displayChatDetails, {super.key}); @override ChatSettingsPopupMenuState createState() => ChatSettingsPopupMenuState(); diff --git a/lib/widgets/connection_status_header.dart b/lib/widgets/connection_status_header.dart index 7d58b000f..2283c41bf 100644 --- a/lib/widgets/connection_status_header.dart +++ b/lib/widgets/connection_status_header.dart @@ -10,7 +10,7 @@ import '../utils/localized_exception_extension.dart'; import 'matrix.dart'; class ConnectionStatusHeader extends StatefulWidget { - const ConnectionStatusHeader({Key? key}) : super(key: key); + const ConnectionStatusHeader({super.key}); @override ConnectionStatusHeaderState createState() => ConnectionStatusHeaderState(); diff --git a/lib/widgets/content_banner.dart b/lib/widgets/content_banner.dart index 72a89a5ad..f9e6a6167 100644 --- a/lib/widgets/content_banner.dart +++ b/lib/widgets/content_banner.dart @@ -21,8 +21,8 @@ class ContentBanner extends StatelessWidget { this.client, this.opacity = 0.75, this.placeholder, - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) { diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index 45852dfcc..708f814fc 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -18,11 +18,11 @@ class FluffyChatApp extends StatelessWidget { final String? pincode; const FluffyChatApp({ - Key? key, + super.key, this.testWidget, required this.clients, this.pincode, - }) : super(key: key); + }); /// getInitialLink may rereturn the value multiple times if this view is /// opened multiple times for example if the user logs out after they logged diff --git a/lib/widgets/layouts/empty_page.dart b/lib/widgets/layouts/empty_page.dart index 9f377f65b..a43f96df2 100644 --- a/lib/widgets/layouts/empty_page.dart +++ b/lib/widgets/layouts/empty_page.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; class EmptyPage extends StatelessWidget { final bool loading; static const double _width = 300; - const EmptyPage({this.loading = false, Key? key}) : super(key: key); + const EmptyPage({this.loading = false, super.key}); @override Widget build(BuildContext context) { final width = min(MediaQuery.of(context).size.width, EmptyPage._width) / 2; diff --git a/lib/widgets/layouts/login_scaffold.dart b/lib/widgets/layouts/login_scaffold.dart index bc9e04c2d..0ad8e93dc 100644 --- a/lib/widgets/layouts/login_scaffold.dart +++ b/lib/widgets/layouts/login_scaffold.dart @@ -13,11 +13,11 @@ class LoginScaffold extends StatelessWidget { final bool enforceMobileMode; const LoginScaffold({ - Key? key, + super.key, required this.body, this.appBar, this.enforceMobileMode = false, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/widgets/layouts/max_width_body.dart b/lib/widgets/layouts/max_width_body.dart index 31f1e7bef..a5d9cafe0 100644 --- a/lib/widgets/layouts/max_width_body.dart +++ b/lib/widgets/layouts/max_width_body.dart @@ -15,8 +15,8 @@ class MaxWidthBody extends StatelessWidget { this.maxWidth = 600, this.withFrame = true, this.withScrolling = true, - Key? key, - }) : super(key: key); + super.key, + }); @override Widget build(BuildContext context) { return SafeArea( diff --git a/lib/widgets/layouts/two_column_layout.dart b/lib/widgets/layouts/two_column_layout.dart index f0fdb9912..a6f4c8bdf 100644 --- a/lib/widgets/layouts/two_column_layout.dart +++ b/lib/widgets/layouts/two_column_layout.dart @@ -6,11 +6,11 @@ class TwoColumnLayout extends StatelessWidget { final bool displayNavigationRail; const TwoColumnLayout({ - Key? key, + super.key, required this.mainView, required this.sideView, required this.displayNavigationRail, - }) : super(key: key); + }); @override Widget build(BuildContext context) { return ScaffoldMessenger( diff --git a/lib/widgets/log_view.dart b/lib/widgets/log_view.dart index 4c58f8b4b..6e5e31882 100644 --- a/lib/widgets/log_view.dart +++ b/lib/widgets/log_view.dart @@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; class LogViewer extends StatefulWidget { - const LogViewer({Key? key}) : super(key: key); + const LogViewer({super.key}); @override LogViewerState createState() => LogViewerState(); diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index ab328e9b6..26ad915e6 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -45,8 +45,8 @@ class Matrix extends StatefulWidget { this.child, required this.clients, this.queryParameters, - Key? key, - }) : super(key: key); + super.key, + }); @override MatrixState createState() => MatrixState(); diff --git a/lib/widgets/mxc_image.dart b/lib/widgets/mxc_image.dart index beb79425a..0aedd1e4f 100644 --- a/lib/widgets/mxc_image.dart +++ b/lib/widgets/mxc_image.dart @@ -38,8 +38,8 @@ class MxcImage extends StatefulWidget { this.animationCurve = FluffyThemes.animationCurve, this.thumbnailMethod = ThumbnailMethod.scale, this.cacheKey, - Key? key, - }) : super(key: key); + super.key, + }); @override State createState() => _MxcImageState(); diff --git a/lib/widgets/public_room_bottom_sheet.dart b/lib/widgets/public_room_bottom_sheet.dart index 87ee50256..cfe21c5e3 100644 --- a/lib/widgets/public_room_bottom_sheet.dart +++ b/lib/widgets/public_room_bottom_sheet.dart @@ -22,8 +22,8 @@ class PublicRoomBottomSheet extends StatelessWidget { required this.outerContext, this.chunk, this.onRoomJoined, - Key? key, - }) : super(key: key) { + super.key, + }) { assert(roomAlias != null || chunk != null); } diff --git a/lib/widgets/settings_switch_list_tile.dart b/lib/widgets/settings_switch_list_tile.dart index a3da2dbb6..268e5f187 100644 --- a/lib/widgets/settings_switch_list_tile.dart +++ b/lib/widgets/settings_switch_list_tile.dart @@ -9,12 +9,12 @@ class SettingsSwitchListTile extends StatefulWidget { final Function(bool)? onChanged; const SettingsSwitchListTile.adaptive({ - Key? key, + super.key, this.defaultValue = false, required this.storeKey, required this.title, this.onChanged, - }) : super(key: key); + }); @override SettingsSwitchListTileState createState() => SettingsSwitchListTileState(); diff --git a/lib/widgets/theme_builder.dart b/lib/widgets/theme_builder.dart index b1ca4aa42..b35a0b449 100644 --- a/lib/widgets/theme_builder.dart +++ b/lib/widgets/theme_builder.dart @@ -19,8 +19,8 @@ class ThemeBuilder extends StatefulWidget { required this.builder, this.themeModeSettingsKey = 'theme_mode', this.primaryColorSettingsKey = 'primary_color', - Key? key, - }) : super(key: key); + super.key, + }); @override State createState() => ThemeController(); diff --git a/lib/widgets/unread_rooms_badge.dart b/lib/widgets/unread_rooms_badge.dart index 2110e4e33..a808c14f3 100644 --- a/lib/widgets/unread_rooms_badge.dart +++ b/lib/widgets/unread_rooms_badge.dart @@ -11,11 +11,11 @@ class UnreadRoomsBadge extends StatelessWidget { final Widget? child; const UnreadRoomsBadge({ - Key? key, + super.key, required this.filter, this.badgePosition, this.child, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/pubspec.lock b/pubspec.lock index 225d998a7..6198009e3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: "direct main" description: name: adaptive_dialog - sha256: "3b8abc7d1ba0834061759ee0be8e623eff5bffbcd1e6df5608a11983cfad6b2b" + sha256: "2861f072027046912caaaec9a688e50e10b712ab11ea18d266ef83c391e5f4d6" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.0+2" analyzer: dependency: transitive description: @@ -373,10 +373,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "21145c9c268d54b1f771d8380c195d2d6f655e0567dc1ca2f9c134c02c819e0a" + sha256: "903dd4ba13eae7cef64acc480e91bf54c3ddd23b5b90b639c170f3911e489620" url: "https://pub.dev" source: hosted - version: "5.3.3" + version: "6.0.0" file_selector_linux: dependency: transitive description: @@ -543,18 +543,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "3.0.0" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications - sha256: "3cc40fe8c50ab8383f3e053a499f00f975636622ecdc8e20a77418ece3b1e975" + sha256: "6d11ea777496061e583623aaf31923f93a9409ef8fcaeeefdd6cd78bf4fe5bb3" url: "https://pub.dev" source: hosted - version: "15.1.0+1" + version: "16.1.0" flutter_local_notifications_linux: dependency: transitive description: @@ -636,10 +636,10 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: "98352186ee7ad3639ccc77ad7924b773ff6883076ab952437d20f18a61f0a7c5" + sha256: ffdbb60130e4665d2af814a0267c481bcf522c41ae2e43caf69fa0146876d685 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "9.0.0" flutter_secure_storage_linux: dependency: transitive description: @@ -676,10 +676,10 @@ packages: dependency: transitive description: name: flutter_secure_storage_windows - sha256: fc2910ec9b28d60598216c29ea763b3a96c401f0ce1d13cdf69ccb0e5c93c3ee + sha256: "5809c66f9dd3b4b93b0a6e2e8561539405322ee767ac2f64d084e2ab5429d108" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.0" flutter_svg: dependency: transitive description: @@ -705,18 +705,18 @@ packages: dependency: "direct main" description: name: flutter_web_auth_2 - sha256: "70e4df72940183b8e269c4163f78dd5bf9102ba3329bfe00c0f2373f30fb32d0" + sha256: "75613aa4d8e43df3de0fc3d93df36ae5b4ba2e94070384c5a9baeda99f5a235f" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "3.0.3" flutter_web_auth_2_platform_interface: dependency: transitive description: name: flutter_web_auth_2_platform_interface - sha256: f6fa7059ff3428c19cd756c02fef8eb0147131c7e64591f9060c90b5ab84f094 + sha256: "9124824cbd21e12680bf58190e27b77f251c897e80ec81cd557ec1fde9aecabf" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "3.0.0" flutter_web_plugins: dependency: transitive description: flutter @@ -803,10 +803,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: "2aa884667eeda3a1c461f31e72af1f77984ab0f29450d8fb12ec1f7bc53eea14" + sha256: e156bc1b2088eb5ece9351bccd48c3e1719a4858eacbd44e59162e98a68205d1 url: "https://pub.dev" source: hosted - version: "10.1.0" + version: "12.0.1" highlighter: dependency: transitive description: @@ -1033,10 +1033,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" list_counter: dependency: transitive description: @@ -1281,18 +1281,18 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" + sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8" url: "https://pub.dev" source: hosted - version: "10.4.3" + version: "11.0.1" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221" + sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e url: "https://pub.dev" source: hosted - version: "10.3.3" + version: "11.1.0" permission_handler_apple: dependency: transitive description: @@ -1305,10 +1305,10 @@ packages: dependency: transitive description: name: permission_handler_platform_interface - sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" url: "https://pub.dev" source: hosted - version: "3.11.3" + version: "3.12.0" permission_handler_windows: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 48c764378..e564bd015 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - adaptive_dialog: ^1.9.0-x-macos-beta.1 + adaptive_dialog: ^1.9.0+2 animations: ^2.0.7 archive: ^3.3.9 badges: ^3.1.1 @@ -26,7 +26,7 @@ dependencies: emoji_proposal: ^0.0.1 emojis: ^0.9.9 #fcm_shared_isolate: ^0.1.0 - file_picker: ^5.3.0 + file_picker: ^6.0.0 flutter: sdk: flutter flutter_app_badger: ^1.5.0 @@ -37,7 +37,7 @@ dependencies: flutter_html: ^3.0.0-beta.2 flutter_html_table: ^3.0.0-beta.2 flutter_linkify: ^6.0.0 - flutter_local_notifications: ^15.1.0+1 + flutter_local_notifications: ^16.1.0 flutter_localizations: sdk: flutter flutter_map: ^4.0.0 @@ -45,13 +45,13 @@ dependencies: flutter_olm: ^1.2.0 flutter_openssl_crypto: ^0.1.0 flutter_ringtone_player: ^3.1.1 - flutter_secure_storage: ^8.0.0 + flutter_secure_storage: ^9.0.0 flutter_typeahead: ^4.3.2 - flutter_web_auth_2: ^2.1.1 + flutter_web_auth_2: ^3.0.3 flutter_webrtc: ^0.9.37 future_loading_dialog: ^0.2.3 geolocator: ^7.6.2 - go_router: ^10.0.0 + go_router: ^12.0.1 hive: ^2.2.3 hive_flutter: ^1.1.0 http: ^0.13.4 @@ -67,7 +67,7 @@ dependencies: package_info_plus: ^4.0.0 pasteboard: ^0.2.0 path_provider: ^2.0.9 - permission_handler: ^10.0.0 + permission_handler: ^11.0.1 provider: ^6.0.2 punycode: ^1.0.0 qr_code_scanner: ^1.0.0 @@ -92,7 +92,7 @@ dependencies: dev_dependencies: dart_code_metrics: ^5.7.5 - flutter_lints: ^2.0.1 + flutter_lints: ^3.0.0 flutter_native_splash: ^2.0.3+1 flutter_test: sdk: flutter From 072c4db616f1c585aa4bbc63485a237f8700c79e Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 16:36:03 +0200 Subject: [PATCH 18/93] build: Update google services patch --- scripts/enable-android-google-services.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/enable-android-google-services.patch b/scripts/enable-android-google-services.patch index ab4ec8a6a..2abc2acab 100644 --- a/scripts/enable-android-google-services.patch +++ b/scripts/enable-android-google-services.patch @@ -154,6 +154,6 @@ index 6999d0b8..b2c9144f 100644 emojis: ^0.9.9 - #fcm_shared_isolate: ^0.1.0 + fcm_shared_isolate: ^0.1.0 - file_picker: ^5.3.0 + file_picker: ^6.0.0 flutter: sdk: flutter From f028b12185adcc8d9a17d08989bb7a7f88d74e7e Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 17:32:37 +0200 Subject: [PATCH 19/93] refactor: Update FutureLoadingDialog --- lib/pages/chat/events/message_reactions.dart | 34 ++++++------------- .../key_verification_dialog.dart | 20 ++--------- .../event_extension.dart | 3 +- pubspec.lock | 6 ++-- pubspec.yaml | 3 +- 5 files changed, 19 insertions(+), 47 deletions(-) diff --git a/lib/pages/chat/events/message_reactions.dart b/lib/pages/chat/events/message_reactions.dart index 58a613968..5b0ec29ff 100644 --- a/lib/pages/chat/events/message_reactions.dart +++ b/lib/pages/chat/events/message_reactions.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart' show IterableExtension; @@ -6,7 +5,6 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; @@ -191,19 +189,12 @@ class _AdaptableReactorsDialog extends StatelessWidget { this.reactionEntry, }); - Future show(BuildContext context) => PlatformInfos.isCupertinoStyle - ? showCupertinoDialog( - context: context, - builder: (context) => this, - barrierDismissible: true, - useRootNavigator: false, - ) - : showDialog( - context: context, - builder: (context) => this, - barrierDismissible: true, - useRootNavigator: false, - ); + Future show(BuildContext context) => showAdaptiveDialog( + context: context, + builder: (context) => this, + barrierDismissible: true, + useRootNavigator: false, + ); @override Widget build(BuildContext context) { @@ -228,14 +219,9 @@ class _AdaptableReactorsDialog extends StatelessWidget { final title = Center(child: Text(reactionEntry!.key!)); - return PlatformInfos.isCupertinoStyle - ? CupertinoAlertDialog( - title: title, - content: body, - ) - : AlertDialog( - title: title, - content: body, - ); + return AlertDialog.adaptive( + title: title, + content: body, + ); } } diff --git a/lib/pages/key_verification/key_verification_dialog.dart b/lib/pages/key_verification/key_verification_dialog.dart index 6c303b859..417825960 100644 --- a/lib/pages/key_verification/key_verification_dialog.dart +++ b/lib/pages/key_verification/key_verification_dialog.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:ui'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -14,7 +13,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/widgets/avatar.dart'; class KeyVerificationDialog extends StatefulWidget { - Future show(BuildContext context) => showDialog( + Future show(BuildContext context) => showAdaptiveDialog( context: context, builder: (context) => this, barrierDismissible: false, @@ -342,23 +341,8 @@ class KeyVerificationPageState extends State { ); break; } - if ({TargetPlatform.iOS, TargetPlatform.macOS} - .contains(Theme.of(context).platform)) { - return CupertinoAlertDialog( - title: title, - content: SizedBox( - height: 256, - width: 256, - child: ListView( - padding: const EdgeInsets.only(top: 16), - children: [body], - ), - ), - actions: buttons, - ); - } - return AlertDialog( + return AlertDialog.adaptive( title: title, content: SizedBox( height: 256, diff --git a/lib/utils/matrix_sdk_extensions/event_extension.dart b/lib/utils/matrix_sdk_extensions/event_extension.dart index dbd4a4aec..ddd5abe80 100644 --- a/lib/utils/matrix_sdk_extensions/event_extension.dart +++ b/lib/utils/matrix_sdk_extensions/event_extension.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:async/async.dart' as async; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; @@ -10,7 +11,7 @@ import 'package:fluffychat/utils/size_string.dart'; import 'matrix_file_extension.dart'; extension LocalizedBody on Event { - Future> _getFile(BuildContext context) => + Future> _getFile(BuildContext context) => showFutureLoadingDialog( context: context, future: downloadAndDecryptAttachment, diff --git a/pubspec.lock b/pubspec.lock index 6198009e3..969c4c156 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -66,7 +66,7 @@ packages: source: hosted version: "2.4.2" async: - dependency: transitive + dependency: "direct main" description: name: async sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" @@ -739,10 +739,10 @@ packages: dependency: "direct main" description: name: future_loading_dialog - sha256: "6227dddb32ad5c7d233a54668f862acb4beb5a5e0dde072de372347cc0799e63" + sha256: "2718b1a308db452da32ab9bca9ad496ff92b683e217add9e92cf50520f90537e" url: "https://pub.dev" source: hosted - version: "0.2.4" + version: "0.3.0" geolocator: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index e564bd015..57e059856 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: adaptive_dialog: ^1.9.0+2 animations: ^2.0.7 archive: ^3.3.9 + async: ^2.11.0 badges: ^3.1.1 blurhash_dart: ^1.1.0 callkeep: ^0.3.2 @@ -49,7 +50,7 @@ dependencies: flutter_typeahead: ^4.3.2 flutter_web_auth_2: ^3.0.3 flutter_webrtc: ^0.9.37 - future_loading_dialog: ^0.2.3 + future_loading_dialog: ^0.3.0 geolocator: ^7.6.2 go_router: ^12.0.1 hive: ^2.2.3 From b2d3b32ba80aae3bfd3d9831cd243bed3a4de837 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 20:40:14 +0200 Subject: [PATCH 20/93] feat: Add experimental todo list for rooms --- assets/l10n/intl_en.arb | 8 +- lib/config/routes.dart | 12 + lib/pages/tasks/model/matrix_todo_list.dart | 59 +++++ lib/pages/tasks/tasks.dart | 234 +++++++++++++++++++ lib/pages/tasks/tasks_view.dart | 199 ++++++++++++++++ lib/utils/localized_exception_extension.dart | 4 + lib/widgets/chat_settings_popup_menu.dart | 13 ++ 7 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 lib/pages/tasks/model/matrix_todo_list.dart create mode 100644 lib/pages/tasks/tasks.dart create mode 100644 lib/pages/tasks/tasks_view.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 7b30bc1cd..60ffc39b8 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2543,5 +2543,11 @@ "kickUserDescription": "The user is kicked out of the chat but not banned. In public chats, the user can rejoin at any time.", "makeAdminDescription": "Once you make this user admin, you may not be able to undo this as they will then have the same permissions as you.", "pushNotificationsNotAvailable": "Push notifications not available", - "learnMore": "Learn more" + "learnMore": "Learn more", + "todoLists": "(Beta) Todolists", + "newTodo": "New todo", + "noTodosYet": "No todos have been added to this chat yet. Create your first todo and start cooperating with others. 📝", + "editTodo": "Edit todo", + "pleaseAddATitle": "Please add a title", + "todoListChangedError": "Oops... The todo list has been changed while you edited it." } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 2386fb3c9..9d6b837e0 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -31,6 +31,7 @@ import 'package:fluffychat/pages/settings_security/settings_security.dart'; import 'package:fluffychat/pages/settings_stories/settings_stories.dart'; import 'package:fluffychat/pages/settings_style/settings_style.dart'; import 'package:fluffychat/pages/story/story_page.dart'; +import 'package:fluffychat/pages/tasks/tasks.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart'; import 'package:fluffychat/widgets/layouts/two_column_layout.dart'; import 'package:fluffychat/widgets/log_view.dart'; @@ -391,6 +392,17 @@ abstract class AppRoutes { ], redirect: loggedOutRedirect, ), + GoRoute( + path: 'tasks', + pageBuilder: (context, state) => defaultPageBuilder( + context, + TasksPage( + room: Matrix.of(context) + .client + .getRoomById(state.pathParameters['roomid']!)!, + ), + ), + ), ], ), ], diff --git a/lib/pages/tasks/model/matrix_todo_list.dart b/lib/pages/tasks/model/matrix_todo_list.dart new file mode 100644 index 000000000..fdcb88970 --- /dev/null +++ b/lib/pages/tasks/model/matrix_todo_list.dart @@ -0,0 +1,59 @@ +import 'package:matrix/matrix.dart'; + +extension MatrixTodoExtension on Room { + static const String stateKey = 'im.fluffychat.matrix_todos'; + static const String contentKey = 'todos'; + + List? get matrixTodos => getState(stateKey) + ?.content + .tryGetList(contentKey) + ?.map((json) => MatrixTodo.fromJson(json)) + .toList(); + + Future updateMatrixTodos(List matrixTodos) => + client.setRoomStateWithKey( + id, + stateKey, + '', + {contentKey: matrixTodos.map((todo) => todo.toJson()).toList()}, + ); +} + +class MatrixTodo { + String title; + String? description; + DateTime? dueDate; + bool done; + List? subTasks; + + MatrixTodo({ + required this.title, + this.description, + this.dueDate, + this.done = false, + this.subTasks, + }); + + factory MatrixTodo.fromJson(Map json) => MatrixTodo( + title: json['title'] as String, + description: json['description'] as String?, + dueDate: json['due_date'] == null + ? null + : DateTime.fromMillisecondsSinceEpoch(json['due_date'] as int), + done: json['done'] as bool, + subTasks: json['sub_tasks'] == null + ? null + : (json['sub_tasks'] as List) + .map((json) => MatrixTodo.fromJson(json)) + .toList(), + ); + + Map toJson() => { + 'title': title, + if (description != null) 'description': description, + if (dueDate != null) 'due_date': dueDate?.millisecondsSinceEpoch, + 'done': done, + if (subTasks != null) + 'sub_tasks': subTasks?.map((t) => t.toJson()).toList(), + }; +} diff --git a/lib/pages/tasks/tasks.dart b/lib/pages/tasks/tasks.dart new file mode 100644 index 000000000..6874a9508 --- /dev/null +++ b/lib/pages/tasks/tasks.dart @@ -0,0 +1,234 @@ +import 'package:flutter/material.dart'; + +import 'package:adaptive_dialog/adaptive_dialog.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/pages/tasks/tasks_view.dart'; +import 'package:fluffychat/utils/localized_exception_extension.dart'; +import 'model/matrix_todo_list.dart'; + +class TasksPage extends StatefulWidget { + final Room room; + const TasksPage({required this.room, super.key}); + + @override + State createState() => TasksController(); +} + +class TasksController extends State { + bool isLoading = false; + DateTime? newTaskDateTime; + String? newTaskDescription; + + final FocusNode focusNode = FocusNode(); + final TextEditingController textEditingController = TextEditingController(); + + List? _tmpTodos; + + List get todos => _tmpTodos ?? widget.room.matrixTodos ?? []; + + Stream get onUpdate => widget.room.client.onSync.stream.where( + (syncUpdate) => + syncUpdate.rooms?.join?[widget.room.id]?.state + ?.any((event) => event.type == MatrixTodoExtension.stateKey) ?? + false, + ); + + void setNewTaskDateTime() async { + final now = DateTime.now(); + final date = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: now.subtract(const Duration(days: 365 * 100)), + lastDate: now.add(const Duration(days: 365 * 100)), + ); + if (date == null) return; + setState(() { + newTaskDateTime = date; + }); + } + + void setNewTaskDescription() async { + final text = await showTextInputDialog( + context: context, + title: L10n.of(context)!.addDescription, + textFields: [ + DialogTextField( + hintText: L10n.of(context)!.addDescription, + maxLength: 512, + minLines: 4, + maxLines: 8, + ), + ], + ); + if (text == null || text.single.isEmpty) return; + setState(() { + newTaskDescription = text.single; + }); + } + + void addTodo([_]) { + if (textEditingController.text.isEmpty) return; + updateTodos( + update: (todos) => [ + ...todos, + MatrixTodo( + title: textEditingController.text, + dueDate: newTaskDateTime, + description: newTaskDescription, + ), + ], + onSuccess: () { + newTaskDateTime = null; + newTaskDescription = null; + textEditingController.clear(); + focusNode.requestFocus(); + }, + ); + } + + void toggleDone(int i) => updateTodos( + update: (todos) { + todos[i].done = !todos[i].done; + return todos; + }, + ); + + void cleanUp() => updateTodos( + update: (todos) => todos..removeWhere((t) => t.done), + ); + + void onReorder(int oldindex, int newindex) => updateTodos( + update: (todos) { + if (newindex > oldindex) { + newindex -= 1; + } + final todo = todos.removeAt(oldindex); + todos.insert(newindex, todo); + + return todos; + }, + tmpTodo: true, + ); + + void updateTodos({ + required List Function(List) update, + void Function()? onSuccess, + bool tmpTodo = false, + }) async { + setState(() { + isLoading = true; + }); + try { + final newTodos = update(todos); + if (tmpTodo) { + setState(() { + _tmpTodos = newTodos; + }); + onUpdate.first.then((_) { + _tmpTodos = null; + }); + } + await widget.room.updateMatrixTodos(newTodos); + onSuccess?.call(); + } on MatrixException catch (e) { + if (e.error != MatrixError.M_LIMIT_EXCEEDED) rethrow; + Logs().w('Rate limit! Try again in ${e.raw['retry_after_ms']}ms'); + await Future.delayed( + Duration(milliseconds: e.raw['retry_after_ms'] as int), + ); + updateTodos(update: update, onSuccess: onSuccess); + } catch (e, s) { + Logs().w('Unable to toggle done', e, s); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + duration: const Duration(seconds: 20), + content: Row( + children: [ + Icon( + Icons.signal_wifi_connected_no_internet_4_outlined, + color: Theme.of(context).colorScheme.background, + ), + const SizedBox(width: 16), + Text(e.toLocalizedString(context)), + ], + ), + action: e is TodoListChangedException + ? null + : SnackBarAction( + label: 'Try again', + onPressed: () { + updateTodos(update: update, onSuccess: onSuccess); + }, + ), + ), + ); + } finally { + setState(() { + isLoading = false; + }); + } + } + + void editTodo(int i, MatrixTodo todo) async { + final texts = await showTextInputDialog( + context: context, + title: L10n.of(context)!.editTodo, + textFields: [ + DialogTextField( + hintText: L10n.of(context)!.newTodo, + initialText: todo.title, + maxLength: 64, + validator: (text) { + if (text == null) return L10n.of(context)!.pleaseAddATitle; + return null; + }, + ), + DialogTextField( + hintText: L10n.of(context)!.addDescription, + maxLength: 512, + minLines: 4, + maxLines: 8, + initialText: todo.description, + ), + ], + ); + if (texts == null) return; + updateTodos( + update: (todos) { + if (todos[i].toJson().toString() != todo.toJson().toString()) { + throw TodoListChangedException(); + } + todos[i].title = texts[0]; + todos[i].description = texts[1].isEmpty ? null : texts[1]; + return todos; + }, + ); + } + + void editTodoDueDate(int i, MatrixTodo todo) async { + final now = DateTime.now(); + final date = await showDatePicker( + context: context, + initialDate: todo.dueDate ?? DateTime.now(), + firstDate: now.subtract(const Duration(days: 365 * 100)), + lastDate: now.add(const Duration(days: 365 * 100)), + ); + if (date == null) return; + updateTodos( + update: (todos) { + if (todos[i].toJson().toString() != todo.toJson().toString()) { + throw TodoListChangedException(); + } + todos[i].dueDate = date; + return todos; + }, + ); + } + + @override + Widget build(BuildContext context) => TasksView(this); +} + +class TodoListChangedException implements Exception {} diff --git a/lib/pages/tasks/tasks_view.dart b/lib/pages/tasks/tasks_view.dart new file mode 100644 index 000000000..d60ac291f --- /dev/null +++ b/lib/pages/tasks/tasks_view.dart @@ -0,0 +1,199 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:intl/intl.dart'; + +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pages/tasks/tasks.dart'; + +class TasksView extends StatelessWidget { + final TasksController controller; + const TasksView(this.controller, {super.key}); + + @override + Widget build(BuildContext context) { + final tag = Localizations.maybeLocaleOf(context)?.toLanguageTag(); + return Scaffold( + appBar: AppBar( + title: Text(L10n.of(context)!.todoLists), + actions: [ + AnimatedCrossFade( + duration: FluffyThemes.animationDuration, + firstChild: const SizedBox( + width: 32, + height: 32, + ), + secondChild: const Padding( + padding: EdgeInsets.all(8.0), + child: SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ), + ), + crossFadeState: controller.isLoading + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + ), + IconButton( + icon: const Icon(Icons.cleaning_services_outlined), + onPressed: controller.cleanUp, + ), + ], + ), + body: Column( + children: [ + Expanded( + child: Opacity( + opacity: controller.isLoading ? 0.66 : 1, + child: StreamBuilder( + stream: controller.widget.room.onUpdate.stream, + builder: (context, snapshot) { + final list = controller.todos; + if (list.isEmpty) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.task_alt, + size: 80, + color: Theme.of(context).colorScheme.secondary, + ), + const SizedBox(height: 16), + SizedBox( + width: 256, + child: Text( + L10n.of(context)!.noTodosYet, + textAlign: TextAlign.center, + ), + ), + ], + ); + } + return ReorderableListView.builder( + onReorder: controller.onReorder, + itemCount: list.length, + itemBuilder: (context, i) { + final todo = list[i]; + final description = todo.description; + final dueDate = todo.dueDate; + return ListTile( + key: Key(todo.toJson().toString()), + leading: Icon( + todo.done + ? Icons.check_circle + : Icons.circle_outlined, + ), + title: Text( + todo.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + decoration: + todo.done ? TextDecoration.lineThrough : null, + ), + ), + subtitle: description == null && dueDate == null + ? null + : Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (description != null) + Text( + description, + maxLines: 2, + ), + if (dueDate != null) + SizedBox( + height: 24, + child: OutlinedButton.icon( + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 6, + ), + ), + icon: const Icon( + Icons.calendar_month, + size: 16, + ), + label: Text( + DateFormat.yMMMd(tag).format(dueDate), + style: const TextStyle(fontSize: 12), + ), + onPressed: () => + controller.editTodoDueDate( + i, + todo, + ), + ), + ), + ], + ), + onTap: controller.isLoading + ? null + : () => controller.toggleDone(i), + trailing: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: IconButton( + icon: const Icon(Icons.edit_outlined, size: 16), + onPressed: () => controller.editTodo(i, todo), + ), + ), + ); + }, + ); + }, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(12.0), + child: TextField( + focusNode: controller.focusNode, + readOnly: controller.isLoading, + controller: controller.textEditingController, + onSubmitted: controller.addTodo, + maxLength: 64, + decoration: InputDecoration( + counterStyle: const TextStyle(height: double.minPositive), + counterText: '', + hintText: L10n.of(context)!.newTodo, + prefixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon( + controller.newTaskDateTime == null + ? Icons.calendar_month_outlined + : Icons.calendar_month, + color: controller.newTaskDateTime == null + ? null + : Theme.of(context).primaryColor, + ), + onPressed: controller.setNewTaskDateTime, + ), + IconButton( + icon: Icon( + Icons.text_fields, + color: controller.newTaskDescription == null + ? null + : Theme.of(context).primaryColor, + ), + onPressed: controller.setNewTaskDescription, + ), + ], + ), + suffixIcon: IconButton( + icon: const Icon(Icons.add_outlined), + onPressed: controller.isLoading ? null : controller.addTodo, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/utils/localized_exception_extension.dart b/lib/utils/localized_exception_extension.dart index b3d6093c3..d665c7f10 100644 --- a/lib/utils/localized_exception_extension.dart +++ b/lib/utils/localized_exception_extension.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/pages/tasks/tasks.dart'; import 'uia_request_manager.dart'; extension LocalizedExceptionExtension on Object { @@ -19,6 +20,9 @@ extension LocalizedExceptionExtension on Object { return (this as MatrixException).errorMessage; } } + if (this is TodoListChangedException) { + return L10n.of(context)!.todoListChangedError; + } if (this is FileTooBigMatrixException) { return L10n.of(context)!.fileIsTooBigForServer; } diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index 54a2fbf08..0ccc55b67 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -63,6 +63,16 @@ class ChatSettingsPopupMenuState extends State { ], ), ), + PopupMenuItem( + value: 'todos', + child: Row( + children: [ + const Icon(Icons.task_alt_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.todoLists), + ], + ), + ), PopupMenuItem( value: 'leave', child: Row( @@ -137,6 +147,9 @@ class ChatSettingsPopupMenuState extends State { widget.room.setPushRuleState(PushRuleState.notify), ); break; + case 'todos': + context.go('/rooms/${widget.room.id}/tasks'); + break; case 'details': _showChatDetails(); break; From d0dbaa5e7251a755956d03e84d0313253e77c7ea Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 28 Oct 2023 20:54:23 +0200 Subject: [PATCH 21/93] build: Update android compileSdkVersion to 34 --- android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index c652ad3be..8eb605e94 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 33 + compileSdkVersion 34 sourceSets { main.java.srcDirs += 'src/main/kotlin' From 5d387145c8f95607bccf9050ebdd889258725c35 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 07:55:00 +0100 Subject: [PATCH 22/93] chore: Follow up todo list feature --- lib/pages/tasks/tasks.dart | 61 ++++-- lib/pages/tasks/tasks_view.dart | 363 +++++++++++++++++--------------- 2 files changed, 233 insertions(+), 191 deletions(-) diff --git a/lib/pages/tasks/tasks.dart b/lib/pages/tasks/tasks.dart index 6874a9508..7743b99e5 100644 --- a/lib/pages/tasks/tasks.dart +++ b/lib/pages/tasks/tasks.dart @@ -99,18 +99,19 @@ class TasksController extends State { update: (todos) => todos..removeWhere((t) => t.done), ); - void onReorder(int oldindex, int newindex) => updateTodos( - update: (todos) { - if (newindex > oldindex) { - newindex -= 1; - } - final todo = todos.removeAt(oldindex); - todos.insert(newindex, todo); - - return todos; - }, - tmpTodo: true, - ); + void onReorder(int oldindex, int newindex) { + if (newindex > oldindex) { + newindex -= 1; + } + updateTodos( + update: (todos) { + final todo = todos.removeAt(oldindex); + todos.insert(newindex, todo); + return todos; + }, + tmpTodo: true, + ); + } void updateTodos({ required List Function(List) update, @@ -122,6 +123,7 @@ class TasksController extends State { }); try { final newTodos = update(todos); + assert(todos != newTodos); if (tmpTodo) { setState(() { _tmpTodos = newTodos; @@ -130,17 +132,23 @@ class TasksController extends State { _tmpTodos = null; }); } - await widget.room.updateMatrixTodos(newTodos); + await widget.room + .updateMatrixTodos(newTodos) + .timeout(const Duration(seconds: 30)); onSuccess?.call(); } on MatrixException catch (e) { - if (e.error != MatrixError.M_LIMIT_EXCEEDED) rethrow; - Logs().w('Rate limit! Try again in ${e.raw['retry_after_ms']}ms'); - await Future.delayed( - Duration(milliseconds: e.raw['retry_after_ms'] as int), - ); + final retryAfterMs = e.retryAfterMs; + if (retryAfterMs == null) rethrow; + Logs().w('Rate limit! Try again in $retryAfterMs ms'); + await Future.delayed(Duration(milliseconds: retryAfterMs)); updateTodos(update: update, onSuccess: onSuccess); } catch (e, s) { - Logs().w('Unable to toggle done', e, s); + Logs().w('Unable to update todo list', e, s); + if (_tmpTodos != null) { + setState(() { + _tmpTodos = null; + }); + } ScaffoldMessenger.of(context).showSnackBar( SnackBar( duration: const Duration(seconds: 20), @@ -151,7 +159,13 @@ class TasksController extends State { color: Theme.of(context).colorScheme.background, ), const SizedBox(width: 16), - Text(e.toLocalizedString(context)), + Expanded( + child: Text( + e.toLocalizedString(context), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), ], ), action: e is TodoListChangedException @@ -207,6 +221,13 @@ class TasksController extends State { ); } + void deleteTodo(int i) => updateTodos( + update: (list) { + list.removeAt(i); + return list; + }, + ); + void editTodoDueDate(int i, MatrixTodo todo) async { final now = DateTime.now(); final date = await showDatePicker( diff --git a/lib/pages/tasks/tasks_view.dart b/lib/pages/tasks/tasks_view.dart index d60ac291f..2651572c0 100644 --- a/lib/pages/tasks/tasks_view.dart +++ b/lib/pages/tasks/tasks_view.dart @@ -13,187 +13,208 @@ class TasksView extends StatelessWidget { @override Widget build(BuildContext context) { final tag = Localizations.maybeLocaleOf(context)?.toLanguageTag(); - return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context)!.todoLists), - actions: [ - AnimatedCrossFade( - duration: FluffyThemes.animationDuration, - firstChild: const SizedBox( - width: 32, - height: 32, - ), - secondChild: const Padding( - padding: EdgeInsets.all(8.0), - child: SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator.adaptive(strokeWidth: 2), + return StreamBuilder( + stream: controller.widget.room.onUpdate.stream, + builder: (context, snapshot) { + final list = controller.todos; + return Scaffold( + appBar: AppBar( + title: Text(L10n.of(context)!.todoLists), + actions: [ + AnimatedCrossFade( + duration: FluffyThemes.animationDuration, + firstChild: const SizedBox( + width: 32, + height: 32, + ), + secondChild: const Padding( + padding: EdgeInsets.all(8.0), + child: SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ), + ), + crossFadeState: controller.isLoading + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, ), - ), - crossFadeState: controller.isLoading - ? CrossFadeState.showSecond - : CrossFadeState.showFirst, + if (list.any((todo) => todo.done)) + IconButton( + icon: const Icon(Icons.cleaning_services_outlined), + onPressed: controller.cleanUp, + ), + ], ), - IconButton( - icon: const Icon(Icons.cleaning_services_outlined), - onPressed: controller.cleanUp, - ), - ], - ), - body: Column( - children: [ - Expanded( - child: Opacity( - opacity: controller.isLoading ? 0.66 : 1, - child: StreamBuilder( - stream: controller.widget.room.onUpdate.stream, - builder: (context, snapshot) { - final list = controller.todos; - if (list.isEmpty) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon( - Icons.task_alt, - size: 80, - color: Theme.of(context).colorScheme.secondary, - ), - const SizedBox(height: 16), - SizedBox( - width: 256, - child: Text( - L10n.of(context)!.noTodosYet, - textAlign: TextAlign.center, - ), - ), - ], - ); - } - return ReorderableListView.builder( - onReorder: controller.onReorder, - itemCount: list.length, - itemBuilder: (context, i) { - final todo = list[i]; - final description = todo.description; - final dueDate = todo.dueDate; - return ListTile( - key: Key(todo.toJson().toString()), - leading: Icon( - todo.done - ? Icons.check_circle - : Icons.circle_outlined, - ), - title: Text( - todo.title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - decoration: - todo.done ? TextDecoration.lineThrough : null, - ), - ), - subtitle: description == null && dueDate == null - ? null - : Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (description != null) - Text( - description, - maxLines: 2, - ), - if (dueDate != null) - SizedBox( - height: 24, - child: OutlinedButton.icon( - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric( - horizontal: 6, + body: Column( + children: [ + Expanded( + child: Opacity( + opacity: controller.isLoading ? 0.66 : 1, + child: list.isEmpty + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.task_alt, + size: 80, + color: Theme.of(context).colorScheme.secondary, + ), + const SizedBox(height: 16), + SizedBox( + width: 256, + child: Text( + L10n.of(context)!.noTodosYet, + textAlign: TextAlign.center, + ), + ), + ], + ) + : ReorderableListView.builder( + onReorder: controller.onReorder, + itemCount: list.length, + itemBuilder: (context, i) { + final todo = list[i]; + final description = todo.description; + final dueDate = todo.dueDate; + return ListTile( + key: Key(todo.toJson().toString()), + leading: Icon( + todo.done + ? Icons.check_circle + : Icons.circle_outlined, + ), + title: Text( + todo.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + decoration: todo.done + ? TextDecoration.lineThrough + : null, + ), + ), + subtitle: description == null && dueDate == null + ? null + : Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + if (description != null) + Text( + description, + maxLines: 2, ), - ), - icon: const Icon( - Icons.calendar_month, - size: 16, - ), - label: Text( - DateFormat.yMMMd(tag).format(dueDate), - style: const TextStyle(fontSize: 12), - ), - onPressed: () => - controller.editTodoDueDate( - i, - todo, - ), - ), + if (dueDate != null) + SizedBox( + height: 24, + child: OutlinedButton.icon( + style: OutlinedButton.styleFrom( + padding: + const EdgeInsets.symmetric( + horizontal: 6, + ), + ), + icon: const Icon( + Icons.calendar_month, + size: 16, + ), + label: Text( + DateFormat.yMMMd(tag) + .format(dueDate), + style: const TextStyle( + fontSize: 12, + ), + ), + onPressed: () => + controller.editTodoDueDate( + i, + todo, + ), + ), + ), + ], ), + onTap: controller.isLoading + ? null + : () => controller.toggleDone(i), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon( + Icons.edit_outlined, + size: 16, + ), + onPressed: () => + controller.editTodo(i, todo), + ), + IconButton( + icon: const Icon( + Icons.delete_outlined, + size: 16, + ), + onPressed: () => controller.deleteTodo(i), + ), + const SizedBox(width: 8), ], ), - onTap: controller.isLoading - ? null - : () => controller.toggleDone(i), - trailing: Padding( - padding: const EdgeInsets.only(right: 8.0), - child: IconButton( - icon: const Icon(Icons.edit_outlined, size: 16), - onPressed: () => controller.editTodo(i, todo), - ), + ); + }, ), - ); - }, - ); - }, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(12.0), - child: TextField( - focusNode: controller.focusNode, - readOnly: controller.isLoading, - controller: controller.textEditingController, - onSubmitted: controller.addTodo, - maxLength: 64, - decoration: InputDecoration( - counterStyle: const TextStyle(height: double.minPositive), - counterText: '', - hintText: L10n.of(context)!.newTodo, - prefixIcon: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: Icon( - controller.newTaskDateTime == null - ? Icons.calendar_month_outlined - : Icons.calendar_month, - color: controller.newTaskDateTime == null - ? null - : Theme.of(context).primaryColor, - ), - onPressed: controller.setNewTaskDateTime, - ), - IconButton( - icon: Icon( - Icons.text_fields, - color: controller.newTaskDescription == null - ? null - : Theme.of(context).primaryColor, - ), - onPressed: controller.setNewTaskDescription, - ), - ], - ), - suffixIcon: IconButton( - icon: const Icon(Icons.add_outlined), - onPressed: controller.isLoading ? null : controller.addTodo, ), ), - ), + Padding( + padding: const EdgeInsets.all(12.0), + child: TextField( + focusNode: controller.focusNode, + readOnly: controller.isLoading, + controller: controller.textEditingController, + onSubmitted: controller.addTodo, + maxLength: 64, + decoration: InputDecoration( + counterStyle: const TextStyle(height: double.minPositive), + counterText: '', + hintText: L10n.of(context)!.newTodo, + prefixIcon: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon( + controller.newTaskDateTime == null + ? Icons.calendar_month_outlined + : Icons.calendar_month, + color: controller.newTaskDateTime == null + ? null + : Theme.of(context).primaryColor, + ), + onPressed: controller.setNewTaskDateTime, + ), + IconButton( + icon: Icon( + Icons.text_fields, + color: controller.newTaskDescription == null + ? null + : Theme.of(context).primaryColor, + ), + onPressed: controller.setNewTaskDescription, + ), + ], + ), + suffixIcon: IconButton( + icon: const Icon(Icons.add_outlined), + onPressed: + controller.isLoading ? null : controller.addTodo, + ), + ), + ), + ), + ], ), - ], - ), + ); + }, ); } } From cc62240a5920c35aec36160951f24e5531b7e67f Mon Sep 17 00:00:00 2001 From: Tomasz W Date: Thu, 26 Oct 2023 18:27:47 +0000 Subject: [PATCH 23/93] Translated using Weblate (Polish) Currently translated at 99.8% (580 of 581 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/pl/ --- assets/l10n/intl_pl.arb | 139 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 6 deletions(-) diff --git a/assets/l10n/intl_pl.arb b/assets/l10n/intl_pl.arb index c66066775..6a9f22c74 100644 --- a/assets/l10n/intl_pl.arb +++ b/assets/l10n/intl_pl.arb @@ -844,7 +844,7 @@ "type": "text", "placeholders": {} }, - "inviteText": "{username} zaprosił/-a cię do FluffyChat. \n1. Zainstaluj FluffyChat: https://fluffychat.im \n2. Zarejestuj się lub zaloguj \n3. Otwórz link zaproszenia: {link}", + "inviteText": "{username} zaprosił/-a cię do FluffyChat. \n1. Odwiedź fluffychat.im i zainstaluj aplikację\n2. Zarejestuj się lub zaloguj \n3. Otwórz link zaproszenia:\n{link}", "@inviteText": { "type": "text", "placeholders": { @@ -1490,7 +1490,7 @@ "type": "text", "placeholders": {} }, - "wallpaper": "Tapeta", + "wallpaper": "Tapeta:", "@wallpaper": { "type": "text", "placeholders": {} @@ -1827,7 +1827,7 @@ "type": "text", "placeholders": {} }, - "redactMessage": "Przekaż wiadomość", + "redactMessage": "Utajnij wiadomość", "@redactMessage": { "type": "text", "placeholders": {} @@ -1992,7 +1992,7 @@ "@whoCanSeeMyStoriesDesc": {}, "shareYourInviteLink": "Udostępnij swój link zaproszenia", "@shareYourInviteLink": {}, - "separateChatTypes": "Oddzielenie czatów bezpośrednich i grup", + "separateChatTypes": "Oddzielenie czatów bezpośrednich i grupowych", "@separateChatTypes": { "type": "text", "placeholders": {} @@ -2202,7 +2202,7 @@ "mxid": {} } }, - "commandHint_markasdm": "Oznacz jako pokój wiadomości bezpośrednich", + "commandHint_markasdm": "Oznacz jako pokój wiadomości bezpośrednich dla podanego Matrix ID", "@commandHint_markasdm": {}, "confirmMatrixId": "Potwierdź swój identyfikator Matrix w celu usunięcia konta.", "@confirmMatrixId": {}, @@ -2483,5 +2483,132 @@ "removeFromBundle": "Usuń z tej paczki", "@removeFromBundle": {}, "openLinkInBrowser": "Otwórz link w przeglądarce", - "@openLinkInBrowser": {} + "@openLinkInBrowser": {}, + "allRooms": "Wszystkie czaty grupowe", + "@allRooms": { + "type": "text", + "placeholders": {} + }, + "reportErrorDescription": "O nie. Coś poszło nie tak. Spróbuj ponownie później. Jeśli chcesz, możesz zgłosić błąd programistom.", + "@reportErrorDescription": {}, + "setColorTheme": "Ustal styl kolorów:", + "@setColorTheme": {}, + "requests": "Żądania", + "@requests": {}, + "tryAgain": "Spróbuj ponownie", + "@tryAgain": {}, + "messagesStyle": "Wiadomości:", + "@messagesStyle": {}, + "chatDescription": "Opis czatu", + "@chatDescription": {}, + "invalidServerName": "Nieprawidłowa nazwa serwera", + "@invalidServerName": {}, + "chatPermissions": "Uprawnienia czatu", + "@chatPermissions": {}, + "signInWithPassword": "Zaloguj się z hasłem", + "@signInWithPassword": {}, + "setChatDescription": "Ustaw opis czatu", + "@setChatDescription": {}, + "importFromZipFile": "Zaimportuj z pliku .zip", + "@importFromZipFile": {}, + "discover": "Odkrywanie", + "@discover": { + "type": "text", + "placeholders": {} + }, + "redactedBy": "Utajnione przez {username}", + "@redactedBy": { + "type": "text", + "placeholders": { + "username": {} + } + }, + "signInWith": "Zaloguj się z {provider}", + "@signInWith": { + "type": "text", + "placeholders": { + "provider": {} + } + }, + "optionalRedactReason": "(Opcjonalnie) Powód utajnienia tej wiadomości...", + "@optionalRedactReason": {}, + "exportEmotePack": "Eksportuj pakiet Emotikon jako .zip", + "@exportEmotePack": {}, + "savedEmotePack": "Zapisano pakiet emotikon do {path}!", + "@savedEmotePack": { + "type": "text", + "placeholders": { + "path": {} + } + }, + "inviteContactToGroupQuestion": "Czy chcesz zaprosić {contact} do czatu „{groupName}”?", + "@inviteContactToGroupQuestion": {}, + "redactedByBecause": "Utajnione przez {username} z powodu: \"{reason}\"", + "@redactedByBecause": { + "type": "text", + "placeholders": { + "username": {}, + "reason": {} + } + }, + "importZipFile": "Zaimportuj plik .zip", + "@importZipFile": {}, + "anyoneCanKnock": "Każdy może zapukać", + "@anyoneCanKnock": {}, + "redactMessageDescription": "Wiadomość zostanie utajniona u wszystkich uczestników tej rozmowy. Nie można tego cofnąć.", + "@redactMessageDescription": {}, + "invalidInput": "Nieprawidłowe dane!", + "@invalidInput": {}, + "report": "raport", + "@report": {}, + "addChatDescription": "Dodaj opis tego czatu", + "@addChatDescription": {}, + "directChat": "Rozmowa bezpośrednia", + "@directChat": {}, + "noOneCanJoin": "Nikt nie może dołączyć", + "@noOneCanJoin": {}, + "wrongPinEntered": "Wprowadzono nieprawidłowy kod PIN! Spróbuj ponownie za {seconds} sekund...", + "@wrongPinEntered": { + "type": "text", + "placeholders": { + "seconds": {} + } + }, + "sendTypingNotifications": "Wysyłaj powiadomienie o pisaniu", + "@sendTypingNotifications": {}, + "inviteGroupChat": "📨 Zaproszenie do rozmowy grupowej", + "@inviteGroupChat": {}, + "invitePrivateChat": "📨 Zaproszenie do rozmowy prywatnej", + "@invitePrivateChat": {}, + "importEmojis": "Zaimportuj Emoji", + "@importEmojis": {}, + "noChatDescriptionYet": "Nie utworzono jeszcze opisu czatu.", + "@noChatDescriptionYet": {}, + "notAnImage": "To nie jest plik obrazu.", + "@notAnImage": {}, + "chatDescriptionHasBeenChanged": "Zmieniono opis czatu", + "@chatDescriptionHasBeenChanged": {}, + "profileNotFound": "Nie można odnaleźć użytkownika na serwerze. Być może wystąpił problem z połączeniem lub użytkownik nie istnieje.", + "@profileNotFound": {}, + "shareInviteLink": "Udostępnij link zaproszenia", + "@shareInviteLink": {}, + "emoteKeyboardNoRecents": "Tutaj pojawiają się ostatnio używane emotikony...", + "@emoteKeyboardNoRecents": { + "type": "text", + "placeholders": {} + }, + "setTheme": "Ustaw wygląd:", + "@setTheme": {}, + "replace": "Zastąp", + "@replace": {}, + "pleaseTryAgainLaterOrChooseDifferentServer": "Spróbuj ponownie później lub wybierz inny serwer.", + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "createGroup": "Utwórz grupę", + "@createGroup": {}, + "importNow": "Zaimportuj", + "@importNow": {}, + "invite": "Zaproszenie", + "@invite": {}, + "continueWith": "Kontynuuj z:", + "@continueWith": {} } From 9336de770e94ad2d208ad2200c15938a3b891397 Mon Sep 17 00:00:00 2001 From: Amy/Atius Date: Fri, 27 Oct 2023 22:23:42 +0000 Subject: [PATCH 24/93] Translated using Weblate (Thai) Currently translated at 9.8% (57 of 581 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/th/ --- assets/l10n/intl_th.arb | 251 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_th.arb b/assets/l10n/intl_th.arb index 9e26dfeeb..93d784b63 100644 --- a/assets/l10n/intl_th.arb +++ b/assets/l10n/intl_th.arb @@ -1 +1,250 @@ -{} \ No newline at end of file +{ + "hugContent": "{senderName} กอดคุณ", + "@hugContent": { + "type": "text", + "placeholders": { + "senderName": {} + } + }, + "commandHint_cuddle": "ส่งเคล้าเคลียให้", + "@commandHint_cuddle": {}, + "admin": "แอดมิน", + "@admin": { + "type": "text", + "placeholders": {} + }, + "supposedMxid": "อันนี้ควรเป็น {mxid}", + "@supposedMxid": { + "type": "text", + "placeholders": { + "mxid": {} + } + }, + "askSSSSSign": "เพื่อให้สามารถลงนามบุคคลอื่นได้ โปรดป้อนรหัสผ่านร้านค้าที่ปลอดภัยหรือรหัสกู้คืนของคุณ", + "@askSSSSSign": { + "type": "text", + "placeholders": {} + }, + "remove": "ลบออก", + "@remove": { + "type": "text", + "placeholders": {} + }, + "areGuestsAllowedToJoin": "ผู้ใช้ทั่วไปได้รับอนุญาตให้เข้าร่วมหรือไม่", + "@areGuestsAllowedToJoin": { + "type": "text", + "placeholders": {} + }, + "pleaseEnterValidEmail": "กรุณาใส่อีเมล์ที่ถูกต้อง", + "@pleaseEnterValidEmail": {}, + "sendOnEnter": "ส่งเมื่อกด enter", + "@sendOnEnter": {}, + "answeredTheCall": "{senderName} รับสายแล้ว", + "@answeredTheCall": { + "type": "text", + "placeholders": { + "senderName": {} + } + }, + "pleaseChooseAtLeastChars": "กรุณาใส่รหัสอย่างน้อย {min} ตัว", + "@pleaseChooseAtLeastChars": { + "type": "text", + "placeholders": { + "min": {} + } + }, + "alias": "นามแฝง", + "@alias": { + "type": "text", + "placeholders": {} + }, + "all": "ทั้งหมด", + "@all": { + "type": "text", + "placeholders": {} + }, + "badServerLoginTypesException": "โฮมเซิร์ฟเวอร์รองรับประเภทการเข้าสู่ระบบ:\n{serverVersions}\nแต่แอปนี้รองรับเฉพาะ:\n{supportedVersions}", + "@badServerLoginTypesException": { + "type": "text", + "placeholders": { + "serverVersions": {}, + "supportedVersions": {} + } + }, + "updateNow": "เริ่มการอัปเดตในเบื้องหลัง", + "@updateNow": {}, + "edit": "แก้ไข", + "@edit": { + "type": "text", + "placeholders": {} + }, + "copy": "คัดลอก", + "@copy": { + "type": "text", + "placeholders": {} + }, + "importFromZipFile": "นำเข้าจากไฟล์ .zip", + "@importFromZipFile": {}, + "autoplayImages": "เล่นสติ๊กเกอร์และอิโมจิแบบเคลื่อนไหวโดยอัตโนมัติ", + "@autoplayImages": { + "type": "text", + "placeholder": {} + }, + "updateAvailable": "มีการอัปเดต FluffyChat แล้ว", + "@updateAvailable": {}, + "help": "ช่วยเหลือ", + "@help": { + "type": "text", + "placeholders": {} + }, + "chatDetails": "รายละเอียดแชท", + "@chatDetails": { + "type": "text", + "placeholders": {} + }, + "repeatPassword": "ใส่รหัสผ่านอีกรอบ", + "@repeatPassword": {}, + "delete": "ลบออก", + "@delete": { + "type": "text", + "placeholders": {} + }, + "acceptedTheInvitation": "👍 {username} ได้รับการชวนแล้ว", + "@acceptedTheInvitation": { + "type": "text", + "placeholders": { + "username": {} + } + }, + "send": "ส่ง", + "@send": { + "type": "text", + "placeholders": {} + }, + "exportEmotePack": "ส่งอิโมจิแพ็คออกเป็นไฟล์ .zip", + "@exportEmotePack": {}, + "account": "บัญชี", + "@account": { + "type": "text", + "placeholders": {} + }, + "savedEmotePack": "บันท฿กแพ็คอิโมจิไว้ที่ {path}!", + "@savedEmotePack": { + "type": "text", + "placeholders": { + "path": {} + } + }, + "chat": "แชท", + "@chat": { + "type": "text", + "placeholders": {} + }, + "areYouSure": "คุณแน่ใจไหม?", + "@areYouSure": { + "type": "text", + "placeholders": {} + }, + "allChats": "แชททั้งหมด", + "@allChats": { + "type": "text", + "placeholders": {} + }, + "passwordsDoNotMatch": "รหัสผ่านของคุณไม่ตรงกัน", + "@passwordsDoNotMatch": {}, + "addToSpace": "เพิ่มไปที่ space", + "@addToSpace": {}, + "importZipFile": "นำเข้าไฟล์ .zip", + "@importZipFile": {}, + "about": "เกี่ยวกับ", + "@about": { + "type": "text", + "placeholders": {} + }, + "activatedEndToEndEncryption": "🔐 {username} เปิดใช้งาน end to end encryption", + "@activatedEndToEndEncryption": { + "type": "text", + "placeholders": { + "username": {} + } + }, + "fluffychat": "FluffyChat", + "@fluffychat": { + "type": "text", + "placeholders": {} + }, + "googlyEyesContent": "{senderName} ส่งตากวนๆให้คุณ", + "@googlyEyesContent": { + "type": "text", + "placeholders": { + "senderName": {} + } + }, + "addChatDescription": "เพิ่มคำอธิบายการแชท", + "@addChatDescription": {}, + "appLock": "ล็อคแอป", + "@appLock": { + "type": "text", + "placeholders": {} + }, + "sendTypingNotifications": "ส่งการแจ้งเตือนการพิมพ์", + "@sendTypingNotifications": {}, + "importEmojis": "นำเข้าอ๊โมจิ", + "@importEmojis": {}, + "confirmMatrixId": "กรุณายืนยัน Matrix ID ของคุณเพื่อลบบัญชีของคุณ", + "@confirmMatrixId": {}, + "notAnImage": "ไม่ใช่ไฟล์รูปภาพ", + "@notAnImage": {}, + "areYouSureYouWantToLogout": "คุณแน่ใจว่าคุณต้องการที่จะออกจากระบบ?", + "@areYouSureYouWantToLogout": { + "type": "text", + "placeholders": {} + }, + "cuddleContent": "{senderName} เคล้าเคลียคุณ", + "@cuddleContent": { + "type": "text", + "placeholders": { + "senderName": {} + } + }, + "askVerificationRequest": "ยอมรับคำขอยืนยันนี้จาก {username} หรือไม่", + "@askVerificationRequest": { + "type": "text", + "placeholders": { + "username": {} + } + }, + "addEmail": "เพิ่มอีเมล", + "@addEmail": { + "type": "text", + "placeholders": {} + }, + "commandHint_hug": "ส่งกอดให้", + "@commandHint_hug": {}, + "replace": "แทนที่", + "@replace": {}, + "archive": "คลังเก็บ", + "@archive": { + "type": "text", + "placeholders": {} + }, + "accept": "ยอมรับ", + "@accept": { + "type": "text", + "placeholders": {} + }, + "commandHint_googly": "ส่งสายตากวนๆ มาให้หน่อย", + "@commandHint_googly": {}, + "pin": "ปักหมุด", + "@pin": { + "type": "text", + "placeholders": {} + }, + "importNow": "นำเข้าเลย", + "@importNow": {}, + "anyoneCanJoin": "ใครๆ ก็สามารถเข้าร่วมได้", + "@anyoneCanJoin": { + "type": "text", + "placeholders": {} + } +} From 35fddb56920e51401bf883c3866fd5f4b610faf4 Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Sat, 28 Oct 2023 18:27:42 +0000 Subject: [PATCH 25/93] Translated using Weblate (Arabic) Currently translated at 100.0% (583 of 583 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ar/ --- assets/l10n/intl_ar.arb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_ar.arb b/assets/l10n/intl_ar.arb index 0fe4b02d1..c8a5cf503 100644 --- a/assets/l10n/intl_ar.arb +++ b/assets/l10n/intl_ar.arb @@ -2619,5 +2619,13 @@ "placeholders": { "seconds": {} } - } + }, + "hasKnocked": "لقد طرق {user}", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "pleaseEnterANumber": "الرجاء إدخال رقم أكبر من 0", + "@pleaseEnterANumber": {} } From 1d3b1227b1bef152686a0391d502c92f61adcb57 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 29 Oct 2023 06:56:26 +0000 Subject: [PATCH 26/93] Translated using Weblate (German) Currently translated at 100.0% (583 of 583 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 077f0efd3..9545c00d0 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -2607,5 +2607,25 @@ "invitePrivateChat": "📨 Einladungen zum privaten Chat", "@invitePrivateChat": {}, "invalidInput": "Ungültige Eingabe!", - "@invalidInput": {} + "@invalidInput": {}, + "hasKnocked": "{user} hat angeklopft", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "wrongPinEntered": "Falsche PIN eingegeben! Bitte in {seconds} Sekunden erneut versuchen ...", + "@wrongPinEntered": { + "type": "text", + "placeholders": { + "seconds": {} + } + }, + "pleaseEnterANumber": "Bitte eine Zahl größer 0 eingeben", + "@pleaseEnterANumber": {}, + "emoteKeyboardNoRecents": "Kürzlich verwendete Emotes werden hier angezeigt ...", + "@emoteKeyboardNoRecents": { + "type": "text", + "placeholders": {} + } } From a9762be81360c9b7189810f667ba26dc092b08cc Mon Sep 17 00:00:00 2001 From: Milo Ivir Date: Sat, 28 Oct 2023 13:52:18 +0000 Subject: [PATCH 27/93] Translated using Weblate (Croatian) Currently translated at 100.0% (583 of 583 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/hr/ --- assets/l10n/intl_hr.arb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_hr.arb b/assets/l10n/intl_hr.arb index cb93cd2d7..b64f3ec47 100644 --- a/assets/l10n/intl_hr.arb +++ b/assets/l10n/intl_hr.arb @@ -2309,7 +2309,7 @@ "user": {} } }, - "youUnbannedUser": "Ponovo si uključio/la si korisnika {user}", + "youUnbannedUser": "Ponovo si uključio/la korisnika {user}", "@youUnbannedUser": { "placeholders": { "user": {} @@ -2617,5 +2617,13 @@ "placeholders": { "seconds": {} } - } + }, + "hasKnocked": "{user} je pokucao/la", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "pleaseEnterANumber": "Upiši broj veći od 0", + "@pleaseEnterANumber": {} } From 9c4ac4892ff93e92b6d34c0c4039c1a7125f78a9 Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 29 Oct 2023 03:04:26 +0000 Subject: [PATCH 28/93] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (583 of 583 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ --- assets/l10n/intl_zh.arb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index 586a5c9ff..db4a885d7 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -2617,5 +2617,13 @@ "placeholders": { "seconds": {} } - } + }, + "hasKnocked": "{user} 请求了加入聊天室的邀请", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "pleaseEnterANumber": "请输入大于 0 的数", + "@pleaseEnterANumber": {} } From 72a2dcbb220bc3d8361ff726d921dd33fbf90618 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 08:02:53 +0100 Subject: [PATCH 29/93] build: Update automerge --- .github/workflows/auto_merge.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/auto_merge.yaml b/.github/workflows/auto_merge.yaml index 2dab06c40..cbc5a21e2 100644 --- a/.github/workflows/auto_merge.yaml +++ b/.github/workflows/auto_merge.yaml @@ -3,14 +3,12 @@ name: Auto merge on: pull_request_target jobs: - auto-approve: + automerge: runs-on: ubuntu-latest permissions: pull-requests: write if: github.actor == 'dependabot[bot]' || github.actor == 'weblate' steps: - - uses: hmarr/auto-approve-action@v3 - - name: automerge - uses: "pascalgn/automerge-action@v0.15.6" - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + - uses: reitermarkus/automerge@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 10d9409be5e40dce1c24c835d98cfb58ddca576b Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 08:10:26 +0100 Subject: [PATCH 30/93] build: Update automerge script --- .github/workflows/auto_merge.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto_merge.yaml b/.github/workflows/auto_merge.yaml index cbc5a21e2..7a9e2d5bd 100644 --- a/.github/workflows/auto_merge.yaml +++ b/.github/workflows/auto_merge.yaml @@ -11,4 +11,5 @@ jobs: steps: - uses: reitermarkus/automerge@v2 with: - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + token: ${{ secrets.GITHUB_TOKEN }} + merge-method: rebase \ No newline at end of file From 702e120e83178678b23216a81022673bfa8cd8b1 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 08:12:21 +0100 Subject: [PATCH 31/93] build: Remove automerge script --- .github/workflows/auto_merge.yaml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .github/workflows/auto_merge.yaml diff --git a/.github/workflows/auto_merge.yaml b/.github/workflows/auto_merge.yaml deleted file mode 100644 index 7a9e2d5bd..000000000 --- a/.github/workflows/auto_merge.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: Auto merge - -on: pull_request_target - -jobs: - automerge: - runs-on: ubuntu-latest - permissions: - pull-requests: write - if: github.actor == 'dependabot[bot]' || github.actor == 'weblate' - steps: - - uses: reitermarkus/automerge@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - merge-method: rebase \ No newline at end of file From 07672a9412a9baed4c5ba1b4019b00d66355097e Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 29 Oct 2023 07:06:24 +0000 Subject: [PATCH 32/93] Translated using Weblate (German) Currently translated at 100.0% (598 of 598 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 9545c00d0..9bcc973c4 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -1159,7 +1159,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Es sieht so aus, als hättest du keine Google-Dienste auf deinem Gerät. Das ist eine gute Entscheidung für deine Privatsphäre! Um Push-Benachrichtigungen in FluffyChat zu erhalten, empfehlen wir die Verwendung von microG https://microg.org/ oder Unified Push https://unifiedpush.org/.", + "noGoogleServicesWarning": "Firebase Cloud Messaging scheint auf Ihrem Gerät nicht verfügbar zu sein. Um trotzdem Push-Benachrichtigungen zu erhalten, empfehlen wir die Installation von MicroG oder Unified Push.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2627,5 +2627,35 @@ "@emoteKeyboardNoRecents": { "type": "text", "placeholders": {} - } + }, + "banUserDescription": "Der Benutzer wird aus dem Chat gebannt und kann den Chat erst wieder betreten, wenn die Verbannung aufgehoben wird.", + "@banUserDescription": {}, + "removeDevicesDescription": "Du wirst von diesem Gerät abgemeldet und kannst dann dort keine Nachrichten mehr empfangen.", + "@removeDevicesDescription": {}, + "unbanUserDescription": "Der Benutzer kann den Chat dann wieder betreten, wenn er es versucht.", + "@unbanUserDescription": {}, + "todoLists": "(Beta) Aufgabenlisten", + "@todoLists": {}, + "editTodo": "Aufgabe bearbeiten", + "@editTodo": {}, + "pushNotificationsNotAvailable": "Push-Benachrichtigungen nicht verfügbar", + "@pushNotificationsNotAvailable": {}, + "pleaseAddATitle": "Bitte einen Titel hinzufügen", + "@pleaseAddATitle": {}, + "makeAdminDescription": "Sobald du diesen Benutzer zum Administrator gemacht hast, kannst du das möglicherweise nicht mehr rückgängig machen, da er dann über dieselben Berechtigungen wie du verfügt.", + "@makeAdminDescription": {}, + "noTodosYet": "Zu diesem Chat wurden noch keine Aufgaben hinzugefügt. Erstellen die erste Aufgabe und fange an, mit anderen zusammenzuarbeiten. 📝", + "@noTodosYet": {}, + "archiveRoomDescription": "Der Chat wird in das Archiv verschoben. Andere Benutzer können sehen, dass du den Chat verlassen hast.", + "@archiveRoomDescription": {}, + "newTodo": "Neue Aufgabe", + "@newTodo": {}, + "learnMore": "Erfahre mehr", + "@learnMore": {}, + "todoListChangedError": "Hoppla ... Die Aufgabenliste wurde geändert, während Sie sie bearbeitet haben.", + "@todoListChangedError": {}, + "roomUpgradeDescription": "Der Chat wird dann mit der neuen Raumversion neu erstellt. Alle Teilnehmer werden benachrichtigt, dass sie zum neuen Chat wechseln müssen. Mehr über Raumversionen erfährst du unter https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "kickUserDescription": "Der Benutzer wird aus dem Chat geworfen, aber nicht gebannt. In öffentlichen Chats kann der Benutzer jederzeit wieder beitreten.", + "@kickUserDescription": {} } From 946b6530b97ded6048df3402ae7626854e830262 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 08:53:00 +0100 Subject: [PATCH 33/93] chore: Follow up reorder handle for todos --- lib/pages/tasks/tasks_view.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pages/tasks/tasks_view.dart b/lib/pages/tasks/tasks_view.dart index 2651572c0..5bfe9169c 100644 --- a/lib/pages/tasks/tasks_view.dart +++ b/lib/pages/tasks/tasks_view.dart @@ -74,6 +74,7 @@ class TasksView extends StatelessWidget { : ReorderableListView.builder( onReorder: controller.onReorder, itemCount: list.length, + buildDefaultDragHandles: false, itemBuilder: (context, i) { final todo = list[i]; final description = todo.description; @@ -158,7 +159,11 @@ class TasksView extends StatelessWidget { ), onPressed: () => controller.deleteTodo(i), ), - const SizedBox(width: 8), + ReorderableDragStartListener( + index: i, + child: + const Icon(Icons.drag_handle_outlined), + ), ], ), ); From 85ccf9ed42c50ad6310d26ee5a37d6ca7d4ff4de Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 08:56:15 +0100 Subject: [PATCH 34/93] build: Update flutter --- .github/workflows/versions.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/versions.env b/.github/workflows/versions.env index 30e7fdfb8..7ad54f1fe 100644 --- a/.github/workflows/versions.env +++ b/.github/workflows/versions.env @@ -1,2 +1,2 @@ -FLUTTER_VERSION=3.13.7 +FLUTTER_VERSION=3.13.9 JAVA_VERSION=17 \ No newline at end of file From a5b5b1ae1215f533f8beb0edf255811df6370d24 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 09:03:47 +0100 Subject: [PATCH 35/93] build: Download canvaskit on build for flutter web --- .github/workflows/main_deploy.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main_deploy.yaml b/.github/workflows/main_deploy.yaml index f8603f30e..a29d49a92 100644 --- a/.github/workflows/main_deploy.yaml +++ b/.github/workflows/main_deploy.yaml @@ -25,7 +25,7 @@ jobs: - name: Prepare web run: ./scripts/prepare-web.sh - name: Build Release Web - run: flutter build web --release --verbose --source-maps --base-href "/web/" + run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps --base-href "/web/" - name: Build Website run: | cd docs && npx tailwindcss -o ./tailwind.css --minify && cd .. diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e8b4af2bb..5f950ea01 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -25,7 +25,7 @@ jobs: - name: Prepare web run: ./scripts/prepare-web.sh - name: Build Release Web - run: flutter build web --release --source-maps + run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps - name: Create archive run: tar -czf fluffychat-web.tar.gz build/web/ - name: Upload Web Build From 3ef9854c1d455bd31094b57dff5cc77af2566d07 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 09:08:57 +0100 Subject: [PATCH 36/93] fix: render tg-forward as blockquote style --- lib/pages/chat/events/html_message.dart | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index 8d8993d50..acf8f3f71 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -63,6 +63,16 @@ class HtmlMessage extends StatelessWidget { final linkColor = textColor.withAlpha(150); + final blockquoteStyle = Style( + border: Border( + left: BorderSide( + width: 3, + color: textColor, + ), + ), + padding: HtmlPaddings.only(left: 6, bottom: 0), + ); + // there is no need to pre-validate the html, as we validate it while rendering return Html( data: linkifiedRenderHtml, @@ -99,15 +109,8 @@ class HtmlMessage extends StatelessWidget { fontSize: FontSize(fontSize), lineHeight: LineHeight.number(1.5), ), - 'blockquote': Style( - border: Border( - left: BorderSide( - width: 3, - color: textColor, - ), - ), - padding: HtmlPaddings.only(left: 6, bottom: 0), - ), + 'blockquote': blockquoteStyle, + 'tg-forward': blockquoteStyle, 'hr': Style( border: Border.all(color: textColor, width: 0.5), ), @@ -191,6 +194,8 @@ class HtmlMessage extends StatelessWidget { 'ruby', 'rp', 'rt', + // Workaround for https://github.com/krille-chan/fluffychat/issues/507 + 'tg-forward', }; } From d1ce1e3ba721a3d66949d8765a1134dbfc85f771 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 09:32:06 +0100 Subject: [PATCH 37/93] refactor: Make file dialog adaptive and adjust design --- lib/pages/chat/chat.dart | 14 +++++----- lib/pages/chat/send_file_dialog.dart | 33 +++++++++++++----------- lib/pages/chat/send_location_dialog.dart | 19 +------------- 3 files changed, 26 insertions(+), 40 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 8bda72667..bfdd10a20 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -149,7 +149,7 @@ class ChatController extends State { ); } - await showDialog( + await showAdaptiveDialog( context: context, useRootNavigator: false, builder: (c) => SendFileDialog( @@ -490,7 +490,7 @@ class ChatController extends State { ), ); if (result == null || result.files.isEmpty) return; - await showDialog( + await showAdaptiveDialog( context: context, useRootNavigator: false, builder: (c) => SendFileDialog( @@ -508,7 +508,7 @@ class ChatController extends State { } void sendImageFromClipBoard(Uint8List? image) async { - await showDialog( + await showAdaptiveDialog( context: context, useRootNavigator: false, builder: (c) => SendFileDialog( @@ -533,7 +533,7 @@ class ChatController extends State { ); if (result == null || result.files.isEmpty) return; - await showDialog( + await showAdaptiveDialog( context: context, useRootNavigator: false, builder: (c) => SendFileDialog( @@ -556,7 +556,7 @@ class ChatController extends State { final file = await ImagePicker().pickImage(source: ImageSource.camera); if (file == null) return; final bytes = await file.readAsBytes(); - await showDialog( + await showAdaptiveDialog( context: context, useRootNavigator: false, builder: (c) => SendFileDialog( @@ -580,7 +580,7 @@ class ChatController extends State { ); if (file == null) return; final bytes = await file.readAsBytes(); - await showDialog( + await showAdaptiveDialog( context: context, useRootNavigator: false, builder: (c) => SendFileDialog( @@ -688,7 +688,7 @@ class ChatController extends State { } void sendLocationAction() async { - await showDialog( + await showAdaptiveDialog( context: context, useRootNavigator: false, builder: (c) => SendLocationDialog(room: room), diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index 57ddf0a28..28c6bf538 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/size_string.dart'; import '../../utils/resize_image.dart'; @@ -84,29 +85,31 @@ class SendFileDialogState extends State { mainAxisSize: MainAxisSize.min, children: [ Flexible( - child: Image.memory( - widget.files.first.bytes, - fit: BoxFit.contain, + child: Material( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + elevation: + Theme.of(context).appBarTheme.scrolledUnderElevation ?? 4, + shadowColor: Theme.of(context).appBarTheme.shadowColor, + child: Image.memory( + widget.files.first.bytes, + fit: BoxFit.contain, + height: 256, + ), ), ), - Row( - children: [ - Checkbox( - value: origImage, - onChanged: (v) => setState(() => origImage = v ?? false), - ), - InkWell( - onTap: () => setState(() => origImage = !origImage), - child: Text('${L10n.of(context)!.sendOriginal} ($sizeString)'), - ), - ], + SwitchListTile.adaptive( + value: origImage, + contentPadding: EdgeInsets.zero, + onChanged: (v) => setState(() => origImage = v), + title: Text(L10n.of(context)!.sendOriginal), + subtitle: Text(sizeString), ), ], ); } else { contentWidget = Text('$fileName ($sizeString)'); } - return AlertDialog( + return AlertDialog.adaptive( title: Text(sendStr), content: contentWidget, actions: [ diff --git a/lib/pages/chat/send_location_dialog.dart b/lib/pages/chat/send_location_dialog.dart index 182362b52..b2a99004e 100644 --- a/lib/pages/chat/send_location_dialog.dart +++ b/lib/pages/chat/send_location_dialog.dart @@ -9,7 +9,6 @@ import 'package:geolocator/geolocator.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/pages/chat/events/map_bubble.dart'; -import 'package:fluffychat/utils/platform_infos.dart'; class SendLocationDialog extends StatefulWidget { final Room room; @@ -111,23 +110,7 @@ class SendLocationDialogState extends State { ], ); } - if (PlatformInfos.isCupertinoStyle) { - return CupertinoAlertDialog( - title: Text(L10n.of(context)!.shareLocation), - content: contentWidget, - actions: [ - CupertinoDialogAction( - onPressed: Navigator.of(context, rootNavigator: false).pop, - child: Text(L10n.of(context)!.cancel), - ), - CupertinoDialogAction( - onPressed: isSending ? null : sendAction, - child: Text(L10n.of(context)!.send), - ), - ], - ); - } - return AlertDialog( + return AlertDialog.adaptive( title: Text(L10n.of(context)!.shareLocation), content: contentWidget, actions: [ From e7104916798142cc1498200c8ffa29b1bfe17dc3 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 09:33:41 +0100 Subject: [PATCH 38/93] chore: File dialog follow up --- lib/pages/chat/chat.dart | 12 ------------ lib/pages/chat/send_file_dialog.dart | 1 + 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index bfdd10a20..62935d752 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -151,7 +151,6 @@ class ChatController extends State { await showAdaptiveDialog( context: context, - useRootNavigator: false, builder: (c) => SendFileDialog( files: matrixFiles, room: room, @@ -452,7 +451,6 @@ class ChatController extends State { final l10n = L10n.of(context)!; final dialogResult = await showOkCancelAlertDialog( context: context, - useRootNavigator: false, title: l10n.commandInvalid, message: l10n.commandMissing(commandMatch[0]!), okLabel: l10n.sendAsText, @@ -492,7 +490,6 @@ class ChatController extends State { if (result == null || result.files.isEmpty) return; await showAdaptiveDialog( context: context, - useRootNavigator: false, builder: (c) => SendFileDialog( files: result.files .map( @@ -510,7 +507,6 @@ class ChatController extends State { void sendImageFromClipBoard(Uint8List? image) async { await showAdaptiveDialog( context: context, - useRootNavigator: false, builder: (c) => SendFileDialog( files: [ MatrixFile( @@ -535,7 +531,6 @@ class ChatController extends State { await showAdaptiveDialog( context: context, - useRootNavigator: false, builder: (c) => SendFileDialog( files: result.files .map( @@ -558,7 +553,6 @@ class ChatController extends State { final bytes = await file.readAsBytes(); await showAdaptiveDialog( context: context, - useRootNavigator: false, builder: (c) => SendFileDialog( files: [ MatrixImageFile( @@ -582,7 +576,6 @@ class ChatController extends State { final bytes = await file.readAsBytes(); await showAdaptiveDialog( context: context, - useRootNavigator: false, builder: (c) => SendFileDialog( files: [ MatrixVideoFile( @@ -631,7 +624,6 @@ class ChatController extends State { if (await AudioRecorder().hasPermission() == false) return; final result = await showDialog( context: context, - useRootNavigator: false, barrierDismissible: false, builder: (c) => const RecordingDialog(), ); @@ -690,7 +682,6 @@ class ChatController extends State { void sendLocationAction() async { await showAdaptiveDialog( context: context, - useRootNavigator: false, builder: (c) => SendLocationDialog(room: room), ); } @@ -745,7 +736,6 @@ class ChatController extends State { ); if (score == null) return; final reason = await showTextInputDialog( - useRootNavigator: false, context: context, title: L10n.of(context)!.whyDoYouWantToReportThis, okLabel: L10n.of(context)!.ok, @@ -1055,7 +1045,6 @@ class ChatController extends State { void goToNewRoomAction() async { if (OkCancelResult.ok != await showOkCancelAlertDialog( - useRootNavigator: false, context: context, title: L10n.of(context)!.goToTheNewRoom, message: room @@ -1287,7 +1276,6 @@ class ChatController extends State { context: context, title: L10n.of(context)!.unavailable, okLabel: L10n.of(context)!.next, - useRootNavigator: false, ); } } diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index 28c6bf538..105e7f50b 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -90,6 +90,7 @@ class SendFileDialogState extends State { elevation: Theme.of(context).appBarTheme.scrolledUnderElevation ?? 4, shadowColor: Theme.of(context).appBarTheme.shadowColor, + clipBehavior: Clip.hardEdge, child: Image.memory( widget.files.first.bytes, fit: BoxFit.contain, From 9d29f9dcd272f1b7841d9d27e2242868748267b1 Mon Sep 17 00:00:00 2001 From: Krille Date: Sun, 29 Oct 2023 09:45:11 +0100 Subject: [PATCH 39/93] build: Add changelog for v1.15.0 --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69dd95a8c..c9b5e780e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,41 @@ +## v1.15.0 +- feat: Add experimental todo list for rooms (krille-chan) +- feat: better scroll to last read message handling (krille-chan) +- build: Add appid suffix to android debug builds (krille-chan) +- build: Download canvaskit on build for flutter web (krille-chan) +- build: Update to Flutter 3.13.9 (krille-chan) +- chore: Add descriptions in the areYouSure dialogs for better UX (krille-chan) +- chore: Adjust bitrate for smaller voice messages (krille-chan) +- chore: Change way how to seek in audioplayer (Krille) +- chore: Limit image file and video picker until we have a background service (krille-chan) +- chore: Minor design fixes (Krille) +- design: Make incoming messages color more light (krille-chan) +- design: Make key verification an adaptive dialog (krille-chan) +- design: Make own chat bubble primary color for better contrast (krille-chan) +- fix: Create chat dialog crashes sometimes and power level textfield does not validate input (krille-chan) +- fix: Remove uncompatible dependencies connectivity_plus and wakelock (Krille) +- fix: Use correct localization for redactedBy (krille-chan) +- fix: noFCM warning dialog (krille-chan) +- fix: render tg-forward as blockquote style (krille-chan) +- refactor: Change audio codec to opus where supported to have better compatibility with Element (Krille) +- refactor: Make file dialog adaptive and adjust design (krille-chan) +- refactor: Preload notification sound on web (Krille) +- refactor: Remove unused config (krille-chan) +- refactor: Remove unused config params (krille-chan) +- refactor: Update FutureLoadingDialog (krille-chan) +- refactor: use locally hosted canvaskit instead of calling google (root) +- Translated using Weblate (Arabic) (Rex_sa) +- Translated using Weblate (Basque) (xabirequejo) +- Translated using Weblate (Chinese (Simplified)) (Eric) +- Translated using Weblate (Croatian) (Milo Ivir) +- Translated using Weblate (German) (Christian) +- Translated using Weblate (German) (Ettore Atalan) +- Translated using Weblate (Hungarian) (H Tamás) +- Translated using Weblate (Polish) (Tomasz W) +- Translated using Weblate (Russian) (v1s7) +- Translated using Weblate (Slovak) (Jozef Gaal) +- Translated using Weblate (Thai) (Amy/Atius) + ## v1.14.5 - Hotfix iOS crashes on start - Hotfix cannot reset applock diff --git a/pubspec.yaml b/pubspec.yaml index 57e059856..bd47c2ac9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: fluffychat description: Chat with your friends. publish_to: none # On version bump also increase the build number for F-Droid -version: 1.14.5+3520 +version: 1.15.0+3521 environment: sdk: ">=3.0.0 <4.0.0" From b42a565a9a4ba93b7d915ddabed8f1452fe22aa6 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 10:59:07 +0100 Subject: [PATCH 40/93] fix: Archive does not update its state --- lib/pages/archive/archive.dart | 23 ++++++++++++++++++----- lib/pages/archive/archive_view.dart | 9 ++++----- lib/pages/chat/chat.dart | 9 --------- lib/pages/chat/chat_view.dart | 17 ++--------------- lib/pages/chat_list/chat_list_item.dart | 8 ++++++++ 5 files changed, 32 insertions(+), 34 deletions(-) diff --git a/lib/pages/archive/archive.dart b/lib/pages/archive/archive.dart index aba2dace4..a3c35c347 100644 --- a/lib/pages/archive/archive.dart +++ b/lib/pages/archive/archive.dart @@ -16,17 +16,29 @@ class Archive extends StatefulWidget { } class ArchiveController extends State { - List? archive; + List archive = []; Future> getArchive(BuildContext context) async { - final archive = this.archive; - if (archive != null) return archive; - return this.archive = await Matrix.of(context).client.loadArchive(); + if (archive.isNotEmpty) return archive; + return archive = await Matrix.of(context).client.loadArchive(); + } + + void forgetRoomAction(int i) async { + await showFutureLoadingDialog( + context: context, + future: () async { + Logs().v('Forget room ${archive.last.getLocalizedDisplayname()}'); + await archive[i].forget(); + archive.removeAt(i); + }, + ); + setState(() {}); } void forgetAllAction() async { final archive = this.archive; - if (archive == null) return; + final client = Matrix.of(context).client; + if (archive.isEmpty) return; if (await showOkCancelAlertDialog( useRootNavigator: false, context: context, @@ -48,6 +60,7 @@ class ArchiveController extends State { } }, ); + client.clearArchivesFromCache(); setState(() {}); } diff --git a/lib/pages/archive/archive_view.dart b/lib/pages/archive/archive_view.dart index 6dde7b720..fc5c3e250 100644 --- a/lib/pages/archive/archive_view.dart +++ b/lib/pages/archive/archive_view.dart @@ -14,7 +14,6 @@ class ArchiveView extends StatelessWidget { @override Widget build(BuildContext context) { - var archive = controller.archive; return FutureBuilder>( future: controller.getArchive(context), builder: (BuildContext context, snapshot) => Scaffold( @@ -50,16 +49,16 @@ class ArchiveView extends StatelessWidget { child: CircularProgressIndicator.adaptive(strokeWidth: 2), ); } else { - archive = snapshot.data; - if (archive == null || archive!.isEmpty) { + if (controller.archive.isEmpty) { return const Center( child: Icon(Icons.archive_outlined, size: 80), ); } return ListView.builder( - itemCount: archive!.length, + itemCount: controller.archive.length, itemBuilder: (BuildContext context, int i) => ChatListItem( - archive![i], + controller.archive[i], + onForget: () => controller.forgetRoomAction(i), ), ); } diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 62935d752..59f3063c0 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -949,15 +949,6 @@ class ChatController extends State { return sendEmojiAction(emoji.emoji); } - void forgetRoom() async { - final result = await showFutureLoadingDialog( - context: context, - future: room.forget, - ); - if (result.error != null) return; - context.go('/rooms/archive'); - } - void typeEmoji(Emoji? emoji) { if (emoji == null) return; final text = sendController.text; diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index efa0fbde2..9e8ec9f09 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -108,21 +108,7 @@ class ChatView extends StatelessWidget { ], ), ]; - } else if (controller.isArchived) { - return [ - Padding( - padding: const EdgeInsets.all(8.0), - child: TextButton.icon( - onPressed: controller.forgetRoom, - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.error, - ), - icon: const Icon(Icons.delete_forever_outlined), - label: Text(L10n.of(context)!.delete), - ), - ), - ]; - } else { + } else if (!controller.room.isArchived) { return [ if (Matrix.of(context).voipPlugin != null && controller.room.isDirectChat) @@ -135,6 +121,7 @@ class ChatView extends StatelessWidget { ChatSettingsPopupMenu(controller.room, true), ]; } + return []; } @override diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 2d6a97499..b37c8d203 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -23,6 +23,7 @@ class ChatListItem extends StatelessWidget { final bool selected; final void Function()? onTap; final void Function()? onLongPress; + final void Function()? onForget; const ChatListItem( this.room, { @@ -30,6 +31,7 @@ class ChatListItem extends StatelessWidget { this.selected = false, this.onTap, this.onLongPress, + this.onForget, super.key, }); @@ -362,6 +364,12 @@ class ChatListItem extends StatelessWidget { ], ), onTap: () => clickAction(context), + trailing: onForget == null + ? null + : IconButton( + icon: const Icon(Icons.delete_outlined), + onPressed: onForget, + ), ), ), ); From e904f303f6c4c9ddd12ebef377b3f93a510432ec Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 11:46:37 +0100 Subject: [PATCH 41/93] docs: Advertise ntfy in nofcm dialog --- assets/l10n/intl_de.arb | 2 +- assets/l10n/intl_en.arb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 9bcc973c4..44daefa5f 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -1159,7 +1159,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Firebase Cloud Messaging scheint auf Ihrem Gerät nicht verfügbar zu sein. Um trotzdem Push-Benachrichtigungen zu erhalten, empfehlen wir die Installation von MicroG oder Unified Push.", + "noGoogleServicesWarning": "Firebase Cloud Messaging scheint auf deinem Gerät nicht verfügbar zu sein. Um trotzdem Push-Benachrichtigungen zu erhalten, empfehlen wir die Installation von ntfy. Mit ntfy oder einem anderen Unified Push Anbieter kannst du Push-Benachrichtigungen datensicher empfangen. Du kannst ntfy im PlayStore oder bei F-Droid herunterladen.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 60ffc39b8..c7ffd1529 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -1290,7 +1290,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Firebase Cloud Messaging doesn't appear to be available on your device. To still receive push notifications, we recommend installing MicroG or Unified Push.", + "noGoogleServicesWarning": "Firebase Cloud Messaging doesn't appear to be available on your device. To still receive push notifications, we recommend installing ntfy. With ntfy or another Unified Push provider you can receive push notifications in a data secure way. You can download ntfy from the PlayStore or from F-Droid.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} From d24776704281cb2faf0876cb5dcd91b968ffae2c Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 11:47:50 +0100 Subject: [PATCH 42/93] build: Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b5e780e..97c360d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - fix: Use correct localization for redactedBy (krille-chan) - fix: noFCM warning dialog (krille-chan) - fix: render tg-forward as blockquote style (krille-chan) +- fix: Archive does not update its state - refactor: Change audio codec to opus where supported to have better compatibility with Element (Krille) - refactor: Make file dialog adaptive and adjust design (krille-chan) - refactor: Preload notification sound on web (Krille) From 0dbaf09cc7168b18a4554a6eea8abce638e19da7 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 12:17:55 +0100 Subject: [PATCH 43/93] chore: Revert using opus codec in audiorecorder as iOS seems not to be able to play it --- lib/pages/chat/recording_dialog.dart | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 0d18867a4..f65e9b952 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -46,16 +46,8 @@ class RecordingDialogState extends State { } await WakelockPlus.enable(); - // We try to pick Opus where supported, since that is a codec optimized - // for speech as well as what the voice messages MSC uses. - final audioCodec = - (await _audioRecorder.isEncoderSupported(AudioEncoder.opus)) - ? AudioEncoder.opus - : AudioEncoder.aacLc; - await _audioRecorder.start( - RecordConfig( - encoder: audioCodec, + const RecordConfig( autoGain: true, noiseSuppress: true, echoCancel: true, From fd22b4330efc3bf7ed73c3ca353f77a4ead8e256 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 13:04:45 +0100 Subject: [PATCH 44/93] chore: Downgrade record --- lib/pages/chat/chat.dart | 2 +- lib/pages/chat/recording_dialog.dart | 9 +--- macos/Flutter/GeneratedPluginRegistrant.swift | 4 +- pubspec.lock | 44 ++++++++----------- pubspec.yaml | 2 +- 5 files changed, 24 insertions(+), 37 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 59f3063c0..b2b2c12fd 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -621,7 +621,7 @@ class ChatController extends State { } } - if (await AudioRecorder().hasPermission() == false) return; + if (await Record().hasPermission() == false) return; final result = await showDialog( context: context, barrierDismissible: false, diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index f65e9b952..0b5acec42 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -28,7 +28,7 @@ class RecordingDialogState extends State { bool error = false; String? _recordedPath; - final _audioRecorder = AudioRecorder(); + final _audioRecorder = Record(); final List amplitudeTimeline = []; static const int bitRate = 16000; @@ -47,12 +47,7 @@ class RecordingDialogState extends State { await WakelockPlus.enable(); await _audioRecorder.start( - const RecordConfig( - autoGain: true, - noiseSuppress: true, - echoCancel: true, - bitRate: bitRate, - ), + bitRate: bitRate, path: path, ); setState(() => _duration = Duration.zero); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8f5de20fb..e47880af4 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -24,7 +24,7 @@ import macos_window_utils import package_info_plus import pasteboard import path_provider_foundation -import record_darwin +import record_macos import share_plus import shared_preferences_foundation import sqflite @@ -53,7 +53,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin")) + RecordMacosPlugin.register(with: registry.registrar(forPlugin: "RecordMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index 969c4c156..fef69521a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1473,58 +1473,50 @@ packages: dependency: "direct main" description: name: record - sha256: be9b710f42edf94f939dda1a1688e82a68dcd391be0a836c01e639a249f133d3 + sha256: f703397f5a60d9b2b655b3acc94ba079b2d9a67dc0725bdb90ef2fee2441ebf7 url: "https://pub.dev" source: hosted - version: "5.0.1" - record_android: - dependency: transitive - description: - name: record_android - sha256: f2d72c8e86d275967b9e9d1ced58c775958b94d3e2dac3d5416df46d23924ade - url: "https://pub.dev" - source: hosted - version: "1.0.2" - record_darwin: - dependency: transitive - description: - name: record_darwin - sha256: "78dba641ae271e555035ee68b637f7605ba9f8c60ccfd5c03b835e0b77ea201f" - url: "https://pub.dev" - source: hosted - version: "1.0.0" + version: "4.4.4" record_linux: dependency: transitive description: name: record_linux - sha256: "7d0e70cd51635128fe9d37d89bafd6011d7cbba9af8dc323079ae60f23546aef" + sha256: "348db92c4ec1b67b1b85d791381c8c99d7c6908de141e7c9edc20dad399b15ce" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.4.1" + record_macos: + dependency: transitive + description: + name: record_macos + sha256: d1d0199d1395f05e218207e8cacd03eb9dc9e256ddfe2cfcbbb90e8edea06057 + url: "https://pub.dev" + source: hosted + version: "0.2.2" record_platform_interface: dependency: transitive description: name: record_platform_interface - sha256: "3a4b56e94ecd2a0b2b43eb1fa6f94c5b8484334f5d38ef43959c4bf97fb374cf" + sha256: "7a2d4ce7ac3752505157e416e4e0d666a54b1d5d8601701b7e7e5e30bec181b4" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "0.5.0" record_web: dependency: transitive description: name: record_web - sha256: f1c0427dcf5af68dcfe06439d04771d65f73bd040eb0dcd98fbe4c02263a7d35 + sha256: "219ffb4ca59b4338117857db56d3ffadbde3169bcaf1136f5f4d4656f4a2372d" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.5.0" record_windows: dependency: transitive description: name: record_windows - sha256: "326bfbe6f5232dd773ad6b848cd94f78148f02557abff1dd4627d056b688dbdb" + sha256: "42d545155a26b20d74f5107648dbb3382dbbc84dc3f1adc767040359e57a1345" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.7.1" remove_emoji: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index bd47c2ac9..f9afb0968 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,7 +74,7 @@ dependencies: qr_code_scanner: ^1.0.0 qr_flutter: ^4.0.0 receive_sharing_intent: ^1.4.5 - record: ^5.0.1 + record: ^4.4.4 scroll_to_index: ^3.0.1 share_plus: ^7.0.0 shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401 From cf574d0b68f5d05344a7632557909081af4622da Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 13:06:20 +0100 Subject: [PATCH 45/93] chore: Follow up todos --- assets/l10n/intl_en.arb | 3 ++- lib/pages/tasks/tasks_view.dart | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index c7ffd1529..0c625e747 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2549,5 +2549,6 @@ "noTodosYet": "No todos have been added to this chat yet. Create your first todo and start cooperating with others. 📝", "editTodo": "Edit todo", "pleaseAddATitle": "Please add a title", - "todoListChangedError": "Oops... The todo list has been changed while you edited it." + "todoListChangedError": "Oops... The todo list has been changed while you edited it.", + "todosUnencrypted": "Please notice that todos are visible by everyone in the chat and are not end to end encrypted." } diff --git a/lib/pages/tasks/tasks_view.dart b/lib/pages/tasks/tasks_view.dart index 5bfe9169c..8046b44d1 100644 --- a/lib/pages/tasks/tasks_view.dart +++ b/lib/pages/tasks/tasks_view.dart @@ -69,6 +69,15 @@ class TasksView extends StatelessWidget { textAlign: TextAlign.center, ), ), + const SizedBox(height: 16), + SizedBox( + width: 256, + child: Text( + L10n.of(context)!.todosUnencrypted, + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.orange), + ), + ), ], ) : ReorderableListView.builder( From bedd4e9ade4e84f7d6ae699bb6d47c89e0179c50 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 13:56:36 +0100 Subject: [PATCH 46/93] chore: More revert recorder --- lib/pages/chat/recording_dialog.dart | 9 +++++---- macos/Flutter/GeneratedPluginRegistrant.swift | 2 +- pubspec.lock | 8 ++++---- pubspec.yaml | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 0b5acec42..300d5d9b0 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -31,12 +31,13 @@ class RecordingDialogState extends State { final _audioRecorder = Record(); final List amplitudeTimeline = []; - static const int bitRate = 16000; + static const int bitRate = 64000; + static const int samplingRate = 22050; Future startRecording() async { try { final tempDir = await getTemporaryDirectory(); - final path = _recordedPath = + _recordedPath = '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}'; final result = await _audioRecorder.hasPermission(); @@ -45,10 +46,10 @@ class RecordingDialogState extends State { return; } await WakelockPlus.enable(); - await _audioRecorder.start( + path: _recordedPath, bitRate: bitRate, - path: path, + samplingRate: samplingRate, ); setState(() => _duration = Duration.zero); _recorderSubscription?.cancel(); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e47880af4..8ccdbb831 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -50,7 +50,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin")) MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) - FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) RecordMacosPlugin.register(with: registry.registrar(forPlugin: "RecordMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index fef69521a..818c6e09d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1193,10 +1193,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" + sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.0" package_info_plus_platform_interface: dependency: transitive description: @@ -2046,10 +2046,10 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413 + sha256: f45a6c03aa3f8322e0a9d7f4a0482721c8789cb41d555407367650b8f9c26018 url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.3" wakelock_plus_platform_interface: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f9afb0968..9c01ea5b1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -88,7 +88,7 @@ dependencies: vibration: ^1.7.4-nullsafety.0 video_compress: ^3.1.1 video_player: ^2.2.18 - wakelock_plus: ^1.1.1 + wakelock_plus: ^1.1.3 webrtc_interface: ^1.0.13 dev_dependencies: From 3a9c6e877306cda0d3a47595f509deb7e534c3cc Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 29 Oct 2023 14:01:29 +0100 Subject: [PATCH 47/93] chore: Use correct audiorecorder file extension --- lib/pages/chat/chat.dart | 2 +- lib/pages/chat/recording_dialog.dart | 30 +++++++++---- macos/Flutter/GeneratedPluginRegistrant.swift | 4 +- pubspec.lock | 44 +++++++++++-------- pubspec.yaml | 2 +- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index b2b2c12fd..59f3063c0 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -621,7 +621,7 @@ class ChatController extends State { } } - if (await Record().hasPermission() == false) return; + if (await AudioRecorder().hasPermission() == false) return; final result = await showDialog( context: context, barrierDismissible: false, diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 300d5d9b0..8c575f06b 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -13,7 +13,6 @@ import 'package:fluffychat/utils/platform_infos.dart'; import 'events/audio_player.dart'; class RecordingDialog extends StatefulWidget { - static const String recordingFileType = 'm4a'; const RecordingDialog({ super.key, }); @@ -28,17 +27,24 @@ class RecordingDialogState extends State { bool error = false; String? _recordedPath; - final _audioRecorder = Record(); + final _audioRecorder = AudioRecorder(); final List amplitudeTimeline = []; - static const int bitRate = 64000; - static const int samplingRate = 22050; + static const int bitRate = 16000; Future startRecording() async { try { + // We try to pick Opus where supported, since that is a codec optimized + // for speech as well as what the voice messages MSC uses. + final audioCodec = + (await _audioRecorder.isEncoderSupported(AudioEncoder.opus)) + ? AudioEncoder.opus + : AudioEncoder.aacLc; + // see https://pub.dev/documentation/record/latest/record/AudioEncoder.html + final fileExtension = audioCodec == AudioEncoder.opus ? "opus" : "m4a"; final tempDir = await getTemporaryDirectory(); - _recordedPath = - '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}'; + final path = _recordedPath = + '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.$fileExtension'; final result = await _audioRecorder.hasPermission(); if (result != true) { @@ -46,10 +52,16 @@ class RecordingDialogState extends State { return; } await WakelockPlus.enable(); + await _audioRecorder.start( - path: _recordedPath, - bitRate: bitRate, - samplingRate: samplingRate, + RecordConfig( + encoder: audioCodec, + autoGain: true, + noiseSuppress: true, + echoCancel: true, + bitRate: bitRate, + ), + path: path, ); setState(() => _duration = Duration.zero); _recorderSubscription?.cancel(); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8ccdbb831..8c747268b 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -24,7 +24,7 @@ import macos_window_utils import package_info_plus import pasteboard import path_provider_foundation -import record_macos +import record_darwin import share_plus import shared_preferences_foundation import sqflite @@ -53,7 +53,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - RecordMacosPlugin.register(with: registry.registrar(forPlugin: "RecordMacosPlugin")) + RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index 818c6e09d..b1f16f19e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1473,50 +1473,58 @@ packages: dependency: "direct main" description: name: record - sha256: f703397f5a60d9b2b655b3acc94ba079b2d9a67dc0725bdb90ef2fee2441ebf7 + sha256: be9b710f42edf94f939dda1a1688e82a68dcd391be0a836c01e639a249f133d3 url: "https://pub.dev" source: hosted - version: "4.4.4" + version: "5.0.1" + record_android: + dependency: transitive + description: + name: record_android + sha256: f2d72c8e86d275967b9e9d1ced58c775958b94d3e2dac3d5416df46d23924ade + url: "https://pub.dev" + source: hosted + version: "1.0.2" + record_darwin: + dependency: transitive + description: + name: record_darwin + sha256: "78dba641ae271e555035ee68b637f7605ba9f8c60ccfd5c03b835e0b77ea201f" + url: "https://pub.dev" + source: hosted + version: "1.0.0" record_linux: dependency: transitive description: name: record_linux - sha256: "348db92c4ec1b67b1b85d791381c8c99d7c6908de141e7c9edc20dad399b15ce" + sha256: "7d0e70cd51635128fe9d37d89bafd6011d7cbba9af8dc323079ae60f23546aef" url: "https://pub.dev" source: hosted - version: "0.4.1" - record_macos: - dependency: transitive - description: - name: record_macos - sha256: d1d0199d1395f05e218207e8cacd03eb9dc9e256ddfe2cfcbbb90e8edea06057 - url: "https://pub.dev" - source: hosted - version: "0.2.2" + version: "0.7.1" record_platform_interface: dependency: transitive description: name: record_platform_interface - sha256: "7a2d4ce7ac3752505157e416e4e0d666a54b1d5d8601701b7e7e5e30bec181b4" + sha256: "3a4b56e94ecd2a0b2b43eb1fa6f94c5b8484334f5d38ef43959c4bf97fb374cf" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "1.0.2" record_web: dependency: transitive description: name: record_web - sha256: "219ffb4ca59b4338117857db56d3ffadbde3169bcaf1136f5f4d4656f4a2372d" + sha256: f1c0427dcf5af68dcfe06439d04771d65f73bd040eb0dcd98fbe4c02263a7d35 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "1.0.0" record_windows: dependency: transitive description: name: record_windows - sha256: "42d545155a26b20d74f5107648dbb3382dbbc84dc3f1adc767040359e57a1345" + sha256: "326bfbe6f5232dd773ad6b848cd94f78148f02557abff1dd4627d056b688dbdb" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "1.0.0" remove_emoji: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9c01ea5b1..405fdfd51 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,7 +74,7 @@ dependencies: qr_code_scanner: ^1.0.0 qr_flutter: ^4.0.0 receive_sharing_intent: ^1.4.5 - record: ^4.4.4 + record: ^5.0.1 scroll_to_index: ^3.0.1 share_plus: ^7.0.0 shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401 From b96bc4565d1f97609b21036ec752f9b7a7a1de6f Mon Sep 17 00:00:00 2001 From: Krille Date: Sun, 29 Oct 2023 17:48:18 +0100 Subject: [PATCH 48/93] fix: Audiomessages on iOS --- ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme | 2 +- lib/pages/chat/recording_dialog.dart | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6e1..b52b2e698 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ { final _audioRecorder = AudioRecorder(); final List amplitudeTimeline = []; - static const int bitRate = 16000; + static const int bitRate = + 64000; // Lower makes the audio messages unplayable on iOS for some reason Future startRecording() async { try { @@ -46,6 +48,7 @@ class RecordingDialogState extends State { final path = _recordedPath = '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.$fileExtension'; + Logs().v('Store audio file at', path); final result = await _audioRecorder.hasPermission(); if (result != true) { setState(() => error = true); From 6718104731dd745ea56ed84576c9d592176c3950 Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 30 Oct 2023 08:24:15 +0100 Subject: [PATCH 49/93] chore: Disable opus on android --- lib/pages/chat/recording_dialog.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 8bf9908c4..881727af7 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -38,12 +38,10 @@ class RecordingDialogState extends State { try { // We try to pick Opus where supported, since that is a codec optimized // for speech as well as what the voice messages MSC uses. - final audioCodec = - (await _audioRecorder.isEncoderSupported(AudioEncoder.opus)) - ? AudioEncoder.opus - : AudioEncoder.aacLc; + // Notice: Opus seems not to work on iOS. + const audioCodec = AudioEncoder.aacLc; // see https://pub.dev/documentation/record/latest/record/AudioEncoder.html - final fileExtension = audioCodec == AudioEncoder.opus ? "opus" : "m4a"; + const fileExtension = "m4a"; final tempDir = await getTemporaryDirectory(); final path = _recordedPath = '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.$fileExtension'; @@ -57,7 +55,7 @@ class RecordingDialogState extends State { await WakelockPlus.enable(); await _audioRecorder.start( - RecordConfig( + const RecordConfig( encoder: audioCodec, autoGain: true, noiseSuppress: true, From a9eb76983b2bd776880470df02007089a35fb981 Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 30 Oct 2023 08:57:27 +0100 Subject: [PATCH 50/93] chore: Remove custom bitrate --- lib/pages/chat/recording_dialog.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 881727af7..2546cdbde 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -31,9 +31,6 @@ class RecordingDialogState extends State { final _audioRecorder = AudioRecorder(); final List amplitudeTimeline = []; - static const int bitRate = - 64000; // Lower makes the audio messages unplayable on iOS for some reason - Future startRecording() async { try { // We try to pick Opus where supported, since that is a codec optimized @@ -60,7 +57,6 @@ class RecordingDialogState extends State { autoGain: true, noiseSuppress: true, echoCancel: true, - bitRate: bitRate, ), path: path, ); From 0919d75b04055930a2e698917a0b64cd211ae950 Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 30 Oct 2023 16:32:21 +0100 Subject: [PATCH 51/93] chore: Downgrade record back to 4.4.4 --- lib/pages/chat/chat.dart | 2 +- lib/pages/chat/recording_dialog.dart | 29 +++++------- macos/Flutter/GeneratedPluginRegistrant.swift | 4 +- pubspec.lock | 44 ++++++++----------- pubspec.yaml | 2 +- 5 files changed, 32 insertions(+), 49 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 59f3063c0..b2b2c12fd 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -621,7 +621,7 @@ class ChatController extends State { } } - if (await AudioRecorder().hasPermission() == false) return; + if (await Record().hasPermission() == false) return; final result = await showDialog( context: context, barrierDismissible: false, diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 2546cdbde..300d5d9b0 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -4,7 +4,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:record/record.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @@ -14,6 +13,7 @@ import 'package:fluffychat/utils/platform_infos.dart'; import 'events/audio_player.dart'; class RecordingDialog extends StatefulWidget { + static const String recordingFileType = 'm4a'; const RecordingDialog({ super.key, }); @@ -28,37 +28,28 @@ class RecordingDialogState extends State { bool error = false; String? _recordedPath; - final _audioRecorder = AudioRecorder(); + final _audioRecorder = Record(); final List amplitudeTimeline = []; + static const int bitRate = 64000; + static const int samplingRate = 22050; + Future startRecording() async { try { - // We try to pick Opus where supported, since that is a codec optimized - // for speech as well as what the voice messages MSC uses. - // Notice: Opus seems not to work on iOS. - const audioCodec = AudioEncoder.aacLc; - // see https://pub.dev/documentation/record/latest/record/AudioEncoder.html - const fileExtension = "m4a"; final tempDir = await getTemporaryDirectory(); - final path = _recordedPath = - '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.$fileExtension'; + _recordedPath = + '${tempDir.path}/recording${DateTime.now().microsecondsSinceEpoch}.${RecordingDialog.recordingFileType}'; - Logs().v('Store audio file at', path); final result = await _audioRecorder.hasPermission(); if (result != true) { setState(() => error = true); return; } await WakelockPlus.enable(); - await _audioRecorder.start( - const RecordConfig( - encoder: audioCodec, - autoGain: true, - noiseSuppress: true, - echoCancel: true, - ), - path: path, + path: _recordedPath, + bitRate: bitRate, + samplingRate: samplingRate, ); setState(() => _duration = Duration.zero); _recorderSubscription?.cancel(); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8c747268b..8ccdbb831 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -24,7 +24,7 @@ import macos_window_utils import package_info_plus import pasteboard import path_provider_foundation -import record_darwin +import record_macos import share_plus import shared_preferences_foundation import sqflite @@ -53,7 +53,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin")) + RecordMacosPlugin.register(with: registry.registrar(forPlugin: "RecordMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index b1f16f19e..818c6e09d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1473,58 +1473,50 @@ packages: dependency: "direct main" description: name: record - sha256: be9b710f42edf94f939dda1a1688e82a68dcd391be0a836c01e639a249f133d3 + sha256: f703397f5a60d9b2b655b3acc94ba079b2d9a67dc0725bdb90ef2fee2441ebf7 url: "https://pub.dev" source: hosted - version: "5.0.1" - record_android: - dependency: transitive - description: - name: record_android - sha256: f2d72c8e86d275967b9e9d1ced58c775958b94d3e2dac3d5416df46d23924ade - url: "https://pub.dev" - source: hosted - version: "1.0.2" - record_darwin: - dependency: transitive - description: - name: record_darwin - sha256: "78dba641ae271e555035ee68b637f7605ba9f8c60ccfd5c03b835e0b77ea201f" - url: "https://pub.dev" - source: hosted - version: "1.0.0" + version: "4.4.4" record_linux: dependency: transitive description: name: record_linux - sha256: "7d0e70cd51635128fe9d37d89bafd6011d7cbba9af8dc323079ae60f23546aef" + sha256: "348db92c4ec1b67b1b85d791381c8c99d7c6908de141e7c9edc20dad399b15ce" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.4.1" + record_macos: + dependency: transitive + description: + name: record_macos + sha256: d1d0199d1395f05e218207e8cacd03eb9dc9e256ddfe2cfcbbb90e8edea06057 + url: "https://pub.dev" + source: hosted + version: "0.2.2" record_platform_interface: dependency: transitive description: name: record_platform_interface - sha256: "3a4b56e94ecd2a0b2b43eb1fa6f94c5b8484334f5d38ef43959c4bf97fb374cf" + sha256: "7a2d4ce7ac3752505157e416e4e0d666a54b1d5d8601701b7e7e5e30bec181b4" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "0.5.0" record_web: dependency: transitive description: name: record_web - sha256: f1c0427dcf5af68dcfe06439d04771d65f73bd040eb0dcd98fbe4c02263a7d35 + sha256: "219ffb4ca59b4338117857db56d3ffadbde3169bcaf1136f5f4d4656f4a2372d" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.5.0" record_windows: dependency: transitive description: name: record_windows - sha256: "326bfbe6f5232dd773ad6b848cd94f78148f02557abff1dd4627d056b688dbdb" + sha256: "42d545155a26b20d74f5107648dbb3382dbbc84dc3f1adc767040359e57a1345" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "0.7.1" remove_emoji: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 405fdfd51..b0bc68a81 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,7 +74,7 @@ dependencies: qr_code_scanner: ^1.0.0 qr_flutter: ^4.0.0 receive_sharing_intent: ^1.4.5 - record: ^5.0.1 + record: ^4.4.4 # Upgrade to 5 currently breaks playing on iOS scroll_to_index: ^3.0.1 share_plus: ^7.0.0 shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401 From d9386f0257281d271742af4b2db7c6aad0eee60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 30 Oct 2023 15:19:37 +0000 Subject: [PATCH 52/93] Translated using Weblate (Estonian) Currently translated at 100.0% (598 of 598 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ --- assets/l10n/intl_et.arb | 42 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_et.arb b/assets/l10n/intl_et.arb index 4655b1373..3dc383a42 100644 --- a/assets/l10n/intl_et.arb +++ b/assets/l10n/intl_et.arb @@ -1164,7 +1164,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Tundub, et sinu nutiseadmes pole Google teenuseid. Sinu privaatsuse mõttes on see kindlasti hea otsus! Kui sa soovid FluffyChat'is näha tõuketeavitusi, siis soovitame, et selle jaoks kasutad https://microg.org või https://unifiedpush.org liidestust.", + "noGoogleServicesWarning": "Tundub, et sinu nutiseadmes pole Firebase Cloud Messaging teenuseid. Sinu privaatsuse mõttes on see kindlasti hea otsus! Kui sa soovid FluffyChat'is näha tõuketeavitusi, siis soovitame, et selle jaoks kasutad MicroG või Unified Push liidestust.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2617,5 +2617,43 @@ "placeholders": { "seconds": {} } - } + }, + "banUserDescription": "Sellele kasutajale on nüüd selles jututoas seatud suhtluskeeld ning ta ei saa vestluses osaleda seni, kuni suhtluskeeld pole eemaldatud.", + "@banUserDescription": {}, + "removeDevicesDescription": "Sind logitakse sellest seadmest välja ja sa enam ei saa sõnumeid.", + "@removeDevicesDescription": {}, + "unbanUserDescription": "Uuesti proovimisel saab see kasutaja nüüd vestlusega liituda.", + "@unbanUserDescription": {}, + "todoLists": "Tegemiste loendid (beetaversioon)", + "@todoLists": {}, + "editTodo": "Muuda tegevust", + "@editTodo": {}, + "pushNotificationsNotAvailable": "Tõuketeavitused pole saadaval", + "@pushNotificationsNotAvailable": {}, + "pleaseAddATitle": "Palun lisa pealkiri", + "@pleaseAddATitle": {}, + "makeAdminDescription": "Kui annad sellele kasutajale peakasutaja õigused, siis kuna tal on sinuga samad õigused, sa ei saa seda toimingut enam tagasi pöörata.", + "@makeAdminDescription": {}, + "noTodosYet": "Siia vestlusesse pole veel lisatud ühtegi tegemiste loendit. Tee esimene ja jätka selle täitmist üheskoos teistega. 📝", + "@noTodosYet": {}, + "archiveRoomDescription": "Selle vestluse tõstame nüüd arhiivi. Muud osalejad näevad, et sa oled vestlusest lahkunud.", + "@archiveRoomDescription": {}, + "hasKnocked": "{user} on jututoa uksele koputanud", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "newTodo": "Uus tegevus", + "@newTodo": {}, + "learnMore": "Loe lisaks", + "@learnMore": {}, + "todoListChangedError": "Hopsti... Seda tegevuste loendit on keegi muutnud just samal ajal, kui sina olid seda muutmas.", + "@todoListChangedError": {}, + "roomUpgradeDescription": "See vestlus luuakse nüüd uuesti jututoa uue versioonina. Kõik senised osalejad saavad teate, et nad peavad liituma uue vestlusega. Jututubade versioonide kohta leiad teavet https://spec.matrix.org/latest/rooms/ lehelt", + "@roomUpgradeDescription": {}, + "pleaseEnterANumber": "Palun sisesta 0'st suurem number", + "@pleaseEnterANumber": {}, + "kickUserDescription": "See kasutaja on nüüd jutuoast välja müksatud, kuid talle pole seatud suhtluskeeldu. Avaliku jututoa puhul saab ta alati uuesti liituda.", + "@kickUserDescription": {} } From ccc58bdba8d44417b8380e211ea73ec15bdc8be1 Mon Sep 17 00:00:00 2001 From: xabirequejo Date: Sun, 29 Oct 2023 13:50:02 +0000 Subject: [PATCH 53/93] Translated using Weblate (Basque) Currently translated at 100.0% (598 of 598 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ --- assets/l10n/intl_eu.arb | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/assets/l10n/intl_eu.arb b/assets/l10n/intl_eu.arb index e418d2b22..228b6b268 100644 --- a/assets/l10n/intl_eu.arb +++ b/assets/l10n/intl_eu.arb @@ -84,7 +84,7 @@ "username": {} } }, - "banFromChat": "Kanporatu txatetik", + "banFromChat": "Ezarri txatera batzeko debekua", "@banFromChat": { "type": "text", "placeholders": {} @@ -746,7 +746,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Dirudienez ez daukazu Googleren zerbitzurik zure mugikorrean. Primerako erabakia zure pribatutasunerako! FluffyChaten jakinarazpenak jasotzeko https://microg.org/ edo https://unifiedpush.org/ erabiltzea gomendatzen dugu.", + "noGoogleServicesWarning": "Dirudienez Firebase Cloud Messaging ez dago erabilgarri zure mugikorrean. Jakinarazpenak jasotzeko MicroG edo Unified Push instalatzea gomendatzen dugu.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2166,7 +2166,7 @@ "type": "text", "placeholders": {} }, - "unverified": "Egiaztatu gabe", + "unverified": "Egiaztatu gabe(a)", "@unverified": {}, "verified": "Egiaztatuta", "@verified": { @@ -2569,7 +2569,7 @@ "reason": {} } }, - "anyoneCanKnock": "Edonork egin dezake batzeko eskaera", + "anyoneCanKnock": "Edonork eska dezake batzeko baimena", "@anyoneCanKnock": {}, "redactMessageDescription": "Mezua elkarrizketa honetako partaide guztientzat botako da atzera. Ezin da desegin.", "@redactMessageDescription": {}, @@ -2605,10 +2605,48 @@ "@createGroup": {}, "invite": "Gonbidatu", "@invite": {}, - "invalidInput": "Sarrerak ez du balio!", + "invalidInput": "Sartu duzunak ez du balio!", "@invalidInput": {}, "inviteGroupChat": "📨 Gonbidatu taldeko txatera", "@inviteGroupChat": {}, "invitePrivateChat": "📨 Gonbidatu txat pribatura", - "@invitePrivateChat": {} + "@invitePrivateChat": {}, + "banUserDescription": "Erabiltzailea txatetik kanporatu eta berriro sartzeko debekua ezarriko zaio; ezingo da berriro sartu debekua kendu arte.", + "@banUserDescription": {}, + "removeDevicesDescription": "Gailu honetako saioa amaituko da eta ezingo duzu mezurik jaso aurrerantzean.", + "@removeDevicesDescription": {}, + "unbanUserDescription": "Erabiltzailea txatera berriro sartu ahal izango da berak nahi izanez gero.", + "@unbanUserDescription": {}, + "todoLists": "(Beta) Zeregin-zerrendak", + "@todoLists": {}, + "editTodo": "Editatu zeregina", + "@editTodo": {}, + "pushNotificationsNotAvailable": "Push jakinarazpenak ez daude erabilgarri", + "@pushNotificationsNotAvailable": {}, + "pleaseAddATitle": "Gehitu izenburua", + "@pleaseAddATitle": {}, + "makeAdminDescription": "Behin erabiltzaile hau administratzaile eginda, litekeena da desegin ezin izatea zuk dituzun baimenak izango dituelako.", + "@makeAdminDescription": {}, + "noTodosYet": "Oraindik ez da zereginik gehitu txat honetara. Sortu lehen zeregina eta hasi elkarlanean besteekin. 📝", + "@noTodosYet": {}, + "archiveRoomDescription": "Txata artxibategira mugituko da. Beste erabiltzaileek txatetik alde egin duzula ikusi ahal izango dute.", + "@archiveRoomDescription": {}, + "hasKnocked": "{user}(e)k baimena eskatu du", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "newTodo": "Zeregin berria", + "@newTodo": {}, + "learnMore": "Gehiago irakurri", + "@learnMore": {}, + "todoListChangedError": "Hara… zeregin-zerrenda aldatu da editatzen ari zinen bitartean.", + "@todoListChangedError": {}, + "roomUpgradeDescription": "Gela bertsio berri gisa birsortuko da txata. Partehartzaile guztiei jakinaraziko zaie txat berrira aldatu behar direla. Gehiago irakur dezakezu gela bertsioei buruz ondorengo estekan: https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "pleaseEnterANumber": "Sartu 0 baino zenbaki handiago bat", + "@pleaseEnterANumber": {}, + "kickUserDescription": "Erabiltzailea txatetik kanporatu da baina ez zaio debekua ezarri. Txat publikoen kasuan, edozein momentutan batu daiteke berriro.", + "@kickUserDescription": {} } From 0431d5f7daab112bca36f5cdb039ee11333a27cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Tue, 31 Oct 2023 05:25:33 +0000 Subject: [PATCH 54/93] Translated using Weblate (Galician) Currently translated at 100.0% (598 of 598 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/gl/ --- assets/l10n/intl_gl.arb | 51 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_gl.arb b/assets/l10n/intl_gl.arb index 6dec950cf..79329c83e 100644 --- a/assets/l10n/intl_gl.arb +++ b/assets/l10n/intl_gl.arb @@ -1164,7 +1164,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Semella que non tes os servizos de google no teu dispositivo. Ben feito! a túa privacidade agradécecho! Para recibir notificacións push en FluffyChat recomendamos usar https://microg.org/ ou https://unifiedpush.org/.", + "noGoogleServicesWarning": "Semella que non tes Firebase Cloud Messaging dispoñible no teu dispositivo. Para recibir notificacións push recomendamos que instales MicroG ou Unified Push.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2610,5 +2610,52 @@ "@emoteKeyboardNoRecents": { "type": "text", "placeholders": {} - } + }, + "banUserDescription": "Vaise vetar a usuaria na conversa e non poderá entrar outra vez ata que se retire o veto.", + "@banUserDescription": {}, + "removeDevicesDescription": "Vas pechar a sesión neste dispositivo e xa non poderás recibir mensaxes nel.", + "@removeDevicesDescription": {}, + "unbanUserDescription": "A usuaria vai poder entrar outra vez na conversa se quere.", + "@unbanUserDescription": {}, + "todoLists": "(Beta) Lista de tarefas", + "@todoLists": {}, + "editTodo": "Editar tarefa", + "@editTodo": {}, + "pushNotificationsNotAvailable": "Non están dispoñibles as notificacións push", + "@pushNotificationsNotAvailable": {}, + "pleaseAddATitle": "Engade un título", + "@pleaseAddATitle": {}, + "makeAdminDescription": "Cando convirtas a esta usuaria en admin non poderás desfacer a acción xa que terá os mesmos permisos ca ti.", + "@makeAdminDescription": {}, + "noTodosYet": "Non se engadiron tarefas aínda a este chat. Crea primeiro a túa lista de tarefas e comeza a colaborar con outras. 📝", + "@noTodosYet": {}, + "archiveRoomDescription": "Vaise mover o chat ao arquivo. Outras usuarias poderán ver que saíches da conversa.", + "@archiveRoomDescription": {}, + "invalidInput": "Contido non válido!", + "@invalidInput": {}, + "hasKnocked": "déronlle unha labazada a {user}", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "wrongPinEntered": "PIN incorrecto! Inténtao outra vez en {seconds} segundos...", + "@wrongPinEntered": { + "type": "text", + "placeholders": { + "seconds": {} + } + }, + "newTodo": "Nova tarefa", + "@newTodo": {}, + "learnMore": "Saber máis", + "@learnMore": {}, + "todoListChangedError": "Ooi... A lista cambiou mentras ti a editabas.", + "@todoListChangedError": {}, + "roomUpgradeDescription": "Vaise recrear o chat coa nova versión da sala. Todas as participantes recibirán unha notificación para que cambien ao novo chat. Podes ler máis información acerca das versións das salas en https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "pleaseEnterANumber": "Escribe un número maior de cero", + "@pleaseEnterANumber": {}, + "kickUserDescription": "A usuaria foi expulsada pero non vetada. En conversas públicas a usuaria pode volver cando queira.", + "@kickUserDescription": {} } From 8b139ed4a6b6d3e0a5ded2af79a9a7a56acc02bb Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 30 Oct 2023 00:51:51 +0000 Subject: [PATCH 55/93] Translated using Weblate (Chinese (Simplified)) Currently translated at 97.4% (583 of 598 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ --- assets/l10n/intl_zh.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index db4a885d7..8f41bd13e 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -1123,7 +1123,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "看起来你手机上没有谷歌服务框架。这对保护你的隐私而言是个好决定!要接收 FluffyChat 的推送通知,推荐你使用 https://microg.org/ 或 https://unifiedpush.org/。", + "noGoogleServicesWarning": "看起来你手机上没有 Firebase Cloud Messaging。如果仍希望接收 FluffyChat 的推送通知,推荐安装 MicroG 或 Unified Push。", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} From 6b374506c7ada528981bc63f287d99518a99d297 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 09:25:02 +0100 Subject: [PATCH 56/93] docs: Add template for test protocol --- .github/ISSUE_TEMPLATE/test_report.md | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/test_report.md diff --git a/.github/ISSUE_TEMPLATE/test_report.md b/.github/ISSUE_TEMPLATE/test_report.md new file mode 100644 index 000000000..685ab7e1f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/test_report.md @@ -0,0 +1,37 @@ +--- +name: Test +about: A detailed protocol for testing all features +title: 'Test Report' +--- + +1. App receives push notifications over Firebase Cloud Messaging when it is in background/terminated: + - [ ] Android + - [ ] iOS +2. - [ ] Android receives push notifications over Unified Push when it is in background/terminated +3. Notifications for rooms, which are not in foreground, are working: + - [ ] Web + - [ ] Linux +4. - [ ] QR Code scanner can still scan links to start a new chat +5. Recording and playing voice messages works: + - [ ] Android + - [ ] iOS + - [ ] Web (play only) +6. Sending and downloading files/images works: + - [ ] Android + - [ ] iOS + - [ ] Web + - [ ] Linux +7. Sharing texts/files/images from other apps to FluffyChat works: + - [ ] Android + - [ ] iOS +8. Login with single sign on works: + - [ ] Android + - [ ] iOS + - [ ] Web + - [ ] Linux +9. Test if the app lock works as intended and appears on opening/resuming the app: + - [ ] Android + - [ ] iOS +10. Drag&Drop to send a file into a chat still works: + - [ ] Web + - [ ] Linux From be099c814dee8d2d50ae9c77926c76c33e5a8b6a Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 09:26:31 +0100 Subject: [PATCH 57/93] docs: Add emojis to issue titles --- .github/ISSUE_TEMPLATE/config.yml | 4 ++-- .github/ISSUE_TEMPLATE/test_report.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c91153caa..7de4cb5ea 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - - name: FluffyChat Community + - name: 👬 FluffyChat Community url: https://matrix.to/#/#fluffychat:matrix.org about: Please ask and answer questions here. - - name: Report security vulnerabilities + - name: 🛡 Report security vulnerabilities url: https://matrix.to/#/@krille:janian.de about: Please report security vulnerabilities here. diff --git a/.github/ISSUE_TEMPLATE/test_report.md b/.github/ISSUE_TEMPLATE/test_report.md index 685ab7e1f..eac8730cc 100644 --- a/.github/ISSUE_TEMPLATE/test_report.md +++ b/.github/ISSUE_TEMPLATE/test_report.md @@ -1,5 +1,5 @@ --- -name: Test +name: 📝 Test about: A detailed protocol for testing all features title: 'Test Report' --- From c7a020a71bd9546461633da76232b593dce0540e Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 09:27:03 +0100 Subject: [PATCH 58/93] docs: Deduplicate report security vulnerability --- .github/ISSUE_TEMPLATE/config.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7de4cb5ea..ef50c0447 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,4 @@ blank_issues_enabled: false contact_links: - name: 👬 FluffyChat Community url: https://matrix.to/#/#fluffychat:matrix.org - about: Please ask and answer questions here. - - name: 🛡 Report security vulnerabilities - url: https://matrix.to/#/@krille:janian.de - about: Please report security vulnerabilities here. + about: Please ask and answer questions here. \ No newline at end of file From 31f844da4ddb4991e3df7c06d0d7cb0310dc142f Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 09:53:59 +0100 Subject: [PATCH 59/93] fix: Scan qr code breaks app --- lib/pages/new_private_chat/new_private_chat.dart | 4 +++- lib/pages/new_private_chat/qr_scanner_modal.dart | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pages/new_private_chat/new_private_chat.dart b/lib/pages/new_private_chat/new_private_chat.dart index f392dca9d..70bf7517c 100644 --- a/lib/pages/new_private_chat/new_private_chat.dart +++ b/lib/pages/new_private_chat/new_private_chat.dart @@ -75,7 +75,9 @@ class NewPrivateChatController extends State { } await showAdaptiveBottomSheet( context: context, - builder: (_) => const QrScannerModal(), + builder: (_) => QrScannerModal( + onScan: (link) => UrlLauncher(context, link).openMatrixToUrl(), + ), ); } diff --git a/lib/pages/new_private_chat/qr_scanner_modal.dart b/lib/pages/new_private_chat/qr_scanner_modal.dart index 6155ebb23..b509941e5 100644 --- a/lib/pages/new_private_chat/qr_scanner_modal.dart +++ b/lib/pages/new_private_chat/qr_scanner_modal.dart @@ -6,10 +6,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; -import 'package:fluffychat/utils/url_launcher.dart'; - class QrScannerModal extends StatefulWidget { - const QrScannerModal({super.key}); + final void Function(String) onScan; + const QrScannerModal({required this.onScan, super.key}); @override QrScannerModalState createState() => QrScannerModalState(); @@ -69,7 +68,8 @@ class QrScannerModalState extends State { sub = controller.scannedDataStream.listen((scanData) { sub.cancel(); Navigator.of(context).pop(); - UrlLauncher(context, scanData.code).openMatrixToUrl(); + final data = scanData.code; + if (data != null) widget.onScan(data); }); } From dd758e16cf92d60d1cb84ca4aab41a70dc616840 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 11:45:21 +0100 Subject: [PATCH 60/93] chore: Follow up sendfiledialog fixes --- lib/pages/chat/send_file_dialog.dart | 31 ++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index 105e7f50b..e5f070fd7 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -98,12 +99,30 @@ class SendFileDialogState extends State { ), ), ), - SwitchListTile.adaptive( - value: origImage, - contentPadding: EdgeInsets.zero, - onChanged: (v) => setState(() => origImage = v), - title: Text(L10n.of(context)!.sendOriginal), - subtitle: Text(sizeString), + const SizedBox(height: 16), + // Workaround for SwitchListTile.adaptive crashes in CupertinoDialog + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CupertinoSwitch( + value: origImage, + onChanged: (v) => setState(() => origImage = v), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + L10n.of(context)!.sendOriginal, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Text(sizeString), + ], + ), + ), + ], ), ], ); From 0156e3ff4f94cbc8bca5890ba0cbd1e5f73de25f Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 11:50:59 +0100 Subject: [PATCH 61/93] docs: Update test report template --- .github/ISSUE_TEMPLATE/test_report.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/test_report.md b/.github/ISSUE_TEMPLATE/test_report.md index eac8730cc..4ae34f303 100644 --- a/.github/ISSUE_TEMPLATE/test_report.md +++ b/.github/ISSUE_TEMPLATE/test_report.md @@ -7,11 +7,14 @@ title: 'Test Report' 1. App receives push notifications over Firebase Cloud Messaging when it is in background/terminated: - [ ] Android - [ ] iOS -2. - [ ] Android receives push notifications over Unified Push when it is in background/terminated +2. App receives push notifications over Unified Push when it is in background/terminated: + - [ ] Android 3. Notifications for rooms, which are not in foreground, are working: - [ ] Web - [ ] Linux -4. - [ ] QR Code scanner can still scan links to start a new chat +4. QR Code scanner can still scan links to start a new chat: + - [ ] Android + - [ ] iOS 5. Recording and playing voice messages works: - [ ] Android - [ ] iOS From 81943f062c8d95a29c2f7963b92c1789316f59e0 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 12:03:27 +0100 Subject: [PATCH 62/93] chore: Add better error widget --- lib/main.dart | 2 ++ lib/widgets/error_widget.dart | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 lib/widgets/error_widget.dart diff --git a/lib/main.dart b/lib/main.dart index 1adfa077c..da84c7f44 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/widgets/error_widget.dart'; import 'config/setting_keys.dart'; import 'utils/background_push.dart'; import 'widgets/fluffy_chat_app.dart'; @@ -63,6 +64,7 @@ Future startGui(List clients) async { await firstClient?.roomsLoading; await firstClient?.accountDataLoading; + ErrorWidget.builder = (details) => FluffyChatErrorWidget(details); runApp(FluffyChatApp(clients: clients, pincode: pin)); } diff --git a/lib/widgets/error_widget.dart b/lib/widgets/error_widget.dart new file mode 100644 index 000000000..8606c7608 --- /dev/null +++ b/lib/widgets/error_widget.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/utils/error_reporter.dart'; + +class FluffyChatErrorWidget extends StatefulWidget { + final FlutterErrorDetails details; + const FluffyChatErrorWidget(this.details, {super.key}); + + @override + State createState() => _FluffyChatErrorWidgetState(); +} + +class _FluffyChatErrorWidgetState extends State { + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + ErrorReporter(context, 'Error Widget').onErrorCallback( + widget.details.exception, + widget.details.stack, + ); + }); + } + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.orange, + child: Placeholder( + child: Center( + child: Material( + color: Colors.white.withOpacity(0.9), + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + '😲 Oh no! Something is broken 😲\n${widget.details.exception}', + maxLines: 5, + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.black), + ), + ), + ), + ), + ), + ); + } +} From c10fe9163674716f6aa95aee5ea3fb6298b4f3f9 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Wed, 1 Nov 2023 17:32:47 +0100 Subject: [PATCH 63/93] fix: Emojipicker flickering because noRecent --- lib/pages/chat/chat_emoji_picker.dart | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart index 78bdda10c..5e45113be 100644 --- a/lib/pages/chat/chat_emoji_picker.dart +++ b/lib/pages/chat/chat_emoji_picker.dart @@ -33,10 +33,7 @@ class ChatEmojiPicker extends StatelessWidget { iconColor: theme.colorScheme.primary.withOpacity(0.5), iconColorSelected: theme.colorScheme.primary, indicatorColor: theme.colorScheme.primary, - noRecents: Text( - L10n.of(context)!.emoteKeyboardNoRecents, - style: theme.textTheme.bodyLarge, - ), + noRecents: const NoRecent(), skinToneDialogBgColor: Color.lerp( theme.colorScheme.background, theme.colorScheme.primaryContainer, @@ -49,3 +46,15 @@ class ChatEmojiPicker extends StatelessWidget { ); } } + +class NoRecent extends StatelessWidget { + const NoRecent({super.key}); + + @override + Widget build(BuildContext context) { + return Text( + L10n.of(context)!.emoteKeyboardNoRecents, + style: Theme.of(context).textTheme.bodyLarge, + ); + } +} From e1881a2691a6a66566336f85bb8b89b0b181b7a6 Mon Sep 17 00:00:00 2001 From: kramo <93832451+kra-mo@users.noreply.github.com> Date: Fri, 3 Nov 2023 23:20:26 +0100 Subject: [PATCH 64/93] fix: Properly capitalize Linux window title --- linux/my_application.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/my_application.cc b/linux/my_application.cc index 269a85872..6dea055f2 100644 --- a/linux/my_application.cc +++ b/linux/my_application.cc @@ -53,11 +53,11 @@ static void my_application_activate(GApplication* application) { if (use_header_bar) { GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "fluffychat"); + gtk_header_bar_set_title(header_bar, "FluffyChat"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { - gtk_window_set_title(window, "fluffychat"); + gtk_window_set_title(window, "FluffyChat"); } gtk_window_set_default_size(window, 864, 680); From b5a571de9e3437ca6a270ed4b1f609ea80448eba Mon Sep 17 00:00:00 2001 From: Aryan Arora Date: Sat, 4 Nov 2023 15:31:00 +0530 Subject: [PATCH 65/93] chore: added android audio sharing intent --- android/app/src/main/AndroidManifest.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f57cee1b7..c0595780a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -95,6 +95,11 @@ + + + + + Date: Wed, 1 Nov 2023 18:00:10 +0100 Subject: [PATCH 66/93] refactor: Store and fix missing persistence of some values --- lib/main.dart | 17 ++-- lib/pages/chat_list/chat_list.dart | 6 +- lib/pages/settings_style/settings_style.dart | 6 +- lib/utils/background_push.dart | 23 +++-- lib/utils/client_manager.dart | 51 +++++------ lib/utils/famedlysdk_store.dart | 43 --------- lib/utils/push_helper.dart | 6 +- lib/utils/voip_plugin.dart | 6 +- lib/widgets/fluffy_chat_app.dart | 4 + lib/widgets/matrix.dart | 95 ++++++++++---------- lib/widgets/settings_switch_list_tile.dart | 22 ++--- scripts/enable-android-google-services.patch | 6 +- 12 files changed, 120 insertions(+), 165 deletions(-) delete mode 100644 lib/utils/famedlysdk_store.dart diff --git a/lib/main.dart b/lib/main.dart index da84c7f44..c3dd3466a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/client_manager.dart'; @@ -21,7 +22,8 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); Logs().nativeColors = !PlatformInfos.isIOS; - final clients = await ClientManager.getClients(); + final store = await SharedPreferences.getInstance(); + final clients = await ClientManager.getClients(store: store); // If the app starts in detached mode, we assume that it is in // background fetch mode for processing push notifications. This is @@ -32,7 +34,7 @@ void main() async { // starting the Flutter engine but process incoming push notifications. BackgroundPush.clientOnly(clients.first); // To start the flutter engine afterwards we add an custom observer. - WidgetsBinding.instance.addObserver(AppStarter(clients)); + WidgetsBinding.instance.addObserver(AppStarter(clients, store)); Logs().i( '${AppConfig.applicationName} started in background-fetch mode. No GUI will be created unless the app is no longer detached.', ); @@ -43,11 +45,11 @@ void main() async { Logs().i( '${AppConfig.applicationName} started in foreground mode. Rendering GUI...', ); - await startGui(clients); + await startGui(clients, store); } /// Fetch the pincode for the applock and start the flutter engine. -Future startGui(List clients) async { +Future startGui(List clients, SharedPreferences store) async { // Fetch the pin for the applock if existing for mobile applications. String? pin; if (PlatformInfos.isMobile) { @@ -65,16 +67,17 @@ Future startGui(List clients) async { await firstClient?.accountDataLoading; ErrorWidget.builder = (details) => FluffyChatErrorWidget(details); - runApp(FluffyChatApp(clients: clients, pincode: pin)); + runApp(FluffyChatApp(clients: clients, pincode: pin, store: store)); } /// Watches the lifecycle changes to start the application when it /// is no longer detached. class AppStarter with WidgetsBindingObserver { final List clients; + final SharedPreferences store; bool guiStarted = false; - AppStarter(this.clients); + AppStarter(this.clients, this.store); @override void didChangeAppLifecycleState(AppLifecycleState state) { @@ -84,7 +87,7 @@ class AppStarter with WidgetsBindingObserver { Logs().i( '${AppConfig.applicationName} switches from the detached background-fetch mode to ${state.name} mode. Rendering GUI...', ); - startGui(clients); + startGui(clients, store); // We must make sure that the GUI is only started once. guiStarted = true; } diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 9b202c49d..8828081bf 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -17,7 +17,6 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat_list/chat_list_view.dart'; import 'package:fluffychat/pages/settings_security/settings_security.dart'; -import 'package:fluffychat/utils/famedlysdk_store.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -189,7 +188,7 @@ class ChatListController extends State ], ); if (newServer == null) return; - Store().setItem(_serverStoreNamespace, newServer.single); + Matrix.of(context).store.setString(_serverStoreNamespace, newServer.single); setState(() { searchServer = newServer.single; }); @@ -382,7 +381,8 @@ class ChatListController extends State CallKeepManager().initialize(); WidgetsBinding.instance.addPostFrameCallback((_) async { if (mounted) { - searchServer = await Store().getItem(_serverStoreNamespace); + searchServer = + Matrix.of(context).store.getString(_serverStoreNamespace); Matrix.of(context).backgroundPush?.setupPush(); } diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index 727839892..67c6d9738 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -30,13 +30,13 @@ class SettingsStyleController extends State { if (pickedFile == null) return; await Matrix.of(context) .store - .setItem(SettingKeys.wallpaper, pickedFile.path); + .setString(SettingKeys.wallpaper, pickedFile.path!); setState(() {}); } void deleteWallpaperAction() async { Matrix.of(context).wallpaper = null; - await Matrix.of(context).store.deleteItem(SettingKeys.wallpaper); + await Matrix.of(context).store.remove(SettingKeys.wallpaper); setState(() {}); } @@ -76,7 +76,7 @@ class SettingsStyleController extends State { void changeFontSizeFactor(double d) { setState(() => AppConfig.fontSizeFactor = d); - Matrix.of(context).store.setItem( + Matrix.of(context).store.setString( SettingKeys.fontSizeFactor, AppConfig.fontSizeFactor.toString(), ); diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 8853cf623..8e67ae922 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -37,7 +37,6 @@ import 'package:fluffychat/widgets/fluffy_chat_app.dart'; import '../config/app_config.dart'; import '../config/setting_keys.dart'; import '../widgets/matrix.dart'; -import 'famedlysdk_store.dart'; import 'platform_infos.dart'; //import 'package:fcm_shared_isolate/fcm_shared_isolate.dart'; @@ -55,8 +54,7 @@ class BackgroundPush { String? _fcmToken; void Function(String errorMsg, {Uri? link})? onFcmError; L10n? l10n; - Store? _store; - Store get store => _store ??= Store(); + Future loadLocale() async { final context = matrix?.context; // inspired by _lookupL10n in .dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -271,7 +269,7 @@ class BackgroundPush { if (matrix == null) { return; } - if (await store.getItemBool(SettingKeys.showNoGoogle, false) == true) { + if ((matrix?.store.getBool(SettingKeys.showNoGoogle) ?? false) == true) { return; } await loadLocale(); @@ -374,16 +372,17 @@ class BackgroundPush { oldTokens: oldTokens, useDeviceSpecificAppId: true, ); - await store.setItem(SettingKeys.unifiedPushEndpoint, newEndpoint); - await store.setItemBool(SettingKeys.unifiedPushRegistered, true); + await matrix?.store.setString(SettingKeys.unifiedPushEndpoint, newEndpoint); + await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, true); } Future _upUnregistered(String i) async { upAction = true; Logs().i('[Push] Removing UnifiedPush endpoint...'); - final oldEndpoint = await store.getItem(SettingKeys.unifiedPushEndpoint); - await store.setItemBool(SettingKeys.unifiedPushRegistered, false); - await store.deleteItem(SettingKeys.unifiedPushEndpoint); + final oldEndpoint = + matrix?.store.getString(SettingKeys.unifiedPushEndpoint); + await matrix?.store.setBool(SettingKeys.unifiedPushRegistered, false); + await matrix?.store.remove(SettingKeys.unifiedPushEndpoint); if (oldEndpoint?.isNotEmpty ?? false) { // remove the old pusher await setupPusher( @@ -409,12 +408,12 @@ class BackgroundPush { /// Workaround for the problem that local notification IDs must be int but we /// sort by [roomId] which is a String. To make sure that we don't have duplicated - /// IDs we map the [roomId] to a number and store this number. + /// IDs we map the [roomId] to a number and matrix?.store this number. late Map idMap; Future _loadIdMap() async { idMap = Map.from( json.decode( - (await store.getItem(SettingKeys.notificationCurrentIds)) ?? '{}', + (matrix?.store.getString(SettingKeys.notificationCurrentIds)) ?? '{}', ), ); } @@ -488,7 +487,7 @@ class BackgroundPush { } } if (changed) { - await store.setItem( + await matrix?.store.setString( SettingKeys.notificationCurrentIds, json.encode(idMap), ); diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index 1ee71d491..01510ef96 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -1,21 +1,22 @@ -import 'dart:convert'; - import 'package:flutter/foundation.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:matrix/encryption/utils/key_verification.dart'; import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/utils/custom_http_client.dart'; import 'package:fluffychat/utils/custom_image_resizer.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart'; import 'package:fluffychat/utils/platform_infos.dart'; -import 'famedlysdk_store.dart'; abstract class ClientManager { static const String clientNamespace = 'im.fluffychat.store.clients'; - static Future> getClients({bool initialize = true}) async { + static Future> getClients({ + bool initialize = true, + required SharedPreferences store, + }) async { if (PlatformInfos.isLinux) { Hive.init((await getApplicationSupportDirectory()).path); } else { @@ -23,19 +24,15 @@ abstract class ClientManager { } final clientNames = {}; try { - final rawClientNames = await Store().getItem(clientNamespace); - if (rawClientNames != null) { - final clientNamesList = - (jsonDecode(rawClientNames) as List).cast(); - clientNames.addAll(clientNamesList); - } + final clientNamesList = store.getStringList(clientNamespace) ?? []; + clientNames.addAll(clientNamesList); } catch (e, s) { Logs().w('Client names in store are corrupted', e, s); - await Store().deleteItem(clientNamespace); + await store.remove(clientNamespace); } if (clientNames.isEmpty) { clientNames.add(PlatformInfos.clientName); - await Store().setItem(clientNamespace, jsonEncode(clientNames.toList())); + await store.setStringList(clientNamespace, clientNames.toList()); } final clients = clientNames.map(createClient).toList(); if (initialize) { @@ -61,31 +58,27 @@ abstract class ClientManager { clientNames.remove(client.clientName); clients.remove(client); } - await Store().setItem(clientNamespace, jsonEncode(clientNames.toList())); + await store.setStringList(clientNamespace, clientNames.toList()); } return clients; } - static Future addClientNameToStore(String clientName) async { - final clientNamesList = []; - final rawClientNames = await Store().getItem(clientNamespace); - if (rawClientNames != null) { - final stored = (jsonDecode(rawClientNames) as List).cast(); - clientNamesList.addAll(stored); - } + static Future addClientNameToStore( + String clientName, + SharedPreferences store, + ) async { + final clientNamesList = store.getStringList(clientNamespace) ?? []; clientNamesList.add(clientName); - await Store().setItem(clientNamespace, jsonEncode(clientNamesList)); + await store.setStringList(clientNamespace, clientNamesList); } - static Future removeClientNameFromStore(String clientName) async { - final clientNamesList = []; - final rawClientNames = await Store().getItem(clientNamespace); - if (rawClientNames != null) { - final stored = (jsonDecode(rawClientNames) as List).cast(); - clientNamesList.addAll(stored); - } + static Future removeClientNameFromStore( + String clientName, + SharedPreferences store, + ) async { + final clientNamesList = store.getStringList(clientNamespace) ?? []; clientNamesList.remove(clientName); - await Store().setItem(clientNamespace, jsonEncode(clientNamesList)); + await store.setStringList(clientNamespace, clientNamesList); } static NativeImplementations get nativeImplementations => kIsWeb diff --git a/lib/utils/famedlysdk_store.dart b/lib/utils/famedlysdk_store.dart deleted file mode 100644 index 2f16b5963..000000000 --- a/lib/utils/famedlysdk_store.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:core'; - -import 'package:shared_preferences/shared_preferences.dart'; - -class Store { - SharedPreferences? _prefs; - - Future _setupLocalStorage() async { - _prefs ??= await SharedPreferences.getInstance(); - } - - Future getItem(String key) async { - await _setupLocalStorage(); - return _prefs!.getString(key); - } - - Future getItemBool(String key, [bool? defaultValue]) async { - await _setupLocalStorage(); - return _prefs!.getBool(key) ?? defaultValue ?? true; - } - - Future setItem(String key, String? value) async { - await _setupLocalStorage(); - if (value == null) { - await _prefs!.remove(key); - return; - } - await _prefs!.setString(key, value); - return; - } - - Future setItemBool(String key, bool value) async { - await _setupLocalStorage(); - await _prefs!.setBool(key, value); - return; - } - - Future deleteItem(String key) async { - await _setupLocalStorage(); - await _prefs!.remove(key); - return; - } -} diff --git a/lib/utils/push_helper.dart b/lib/utils/push_helper.dart index b5111c4c0..12b348713 100644 --- a/lib/utils/push_helper.dart +++ b/lib/utils/push_helper.dart @@ -98,7 +98,11 @@ Future _tryPushHelper( //onDidReceiveBackgroundNotificationResponse: onSelectNotification, ); - client ??= (await ClientManager.getClients(initialize: false)).first; + client ??= (await ClientManager.getClients( + initialize: false, + store: await SharedPreferences.getInstance(), + )) + .first; final event = await client.getEventByPushNotification( notification, storeInDatabase: isBackgroundMessage, diff --git a/lib/utils/voip_plugin.dart b/lib/utils/voip_plugin.dart index 3cc9d5161..4626c1c49 100644 --- a/lib/utils/voip_plugin.dart +++ b/lib/utils/voip_plugin.dart @@ -11,7 +11,6 @@ import 'package:webrtc_interface/webrtc_interface.dart' hide Navigator; import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/dialer/dialer.dart'; import 'package:fluffychat/utils/platform_infos.dart'; -import '../../utils/famedlysdk_store.dart'; import '../../utils/voip/callkeep_manager.dart'; import '../../utils/voip/user_media_manager.dart'; import '../widgets/matrix.dart'; @@ -133,7 +132,8 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { } else { try { final wasForeground = await FlutterForegroundTask.isAppOnForeground; - await Store().setItem( + + await matrix.store.setString( 'wasForeground', wasForeground == true ? 'true' : 'false', ); @@ -172,7 +172,7 @@ class VoipPlugin with WidgetsBindingObserver implements WebRTCDelegate { if (PlatformInfos.isAndroid) { FlutterForegroundTask.setOnLockScreenVisibility(false); FlutterForegroundTask.stopService(); - final wasForeground = await Store().getItem('wasForeground'); + final wasForeground = matrix.store.getString('wasForeground'); wasForeground == 'false' ? FlutterForegroundTask.minimizeApp() : null; } } diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index 708f814fc..d9d2f042a 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -3,6 +3,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:shared_preferences/shared_preferences.dart'; import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/config/themes.dart'; @@ -16,11 +17,13 @@ class FluffyChatApp extends StatelessWidget { final Widget? testWidget; final List clients; final String? pincode; + final SharedPreferences store; const FluffyChatApp({ super.key, this.testWidget, required this.clients, + required this.store, this.pincode, }); @@ -55,6 +58,7 @@ class FluffyChatApp extends StatelessWidget { onGenerateRoute: (_) => MaterialPageRoute( builder: (_) => Matrix( clients: clients, + store: store, child: testWidget ?? child, ), ), diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 26ad915e6..026ce4d6f 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -15,6 +15,7 @@ import 'package:image_picker/image_picker.dart'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:universal_html/html.dart' as html; import 'package:url_launcher/url_launcher_string.dart'; @@ -29,7 +30,6 @@ import '../config/setting_keys.dart'; import '../pages/key_verification/key_verification_dialog.dart'; import '../utils/account_bundles.dart'; import '../utils/background_push.dart'; -import '../utils/famedlysdk_store.dart'; import 'local_notifications_extension.dart'; // import 'package:flutter_secure_storage/flutter_secure_storage.dart'; @@ -41,9 +41,12 @@ class Matrix extends StatefulWidget { final Map? queryParameters; + final SharedPreferences store; + const Matrix({ this.child, required this.clients, + required this.store, this.queryParameters, super.key, }); @@ -59,7 +62,7 @@ class Matrix extends StatefulWidget { class MatrixState extends State with WidgetsBindingObserver { int _activeClient = -1; String? activeBundle; - Store store = Store(); + SharedPreferences get store => widget.store; HomeserverSummary? loginHomeserverSummary; XFile? loginAvatar; @@ -158,7 +161,10 @@ class MatrixState extends State with WidgetsBindingObserver { if (!widget.clients.contains(_loginClientCandidate)) { widget.clients.add(_loginClientCandidate!); } - ClientManager.addClientNameToStore(_loginClientCandidate!.clientName); + ClientManager.addClientNameToStore( + _loginClientCandidate!.clientName, + store, + ); _registerSubs(_loginClientCandidate!.clientName); _loginClientCandidate = null; FluffyChatApp.router.go('/rooms'); @@ -187,7 +193,7 @@ class MatrixState extends State with WidgetsBindingObserver { try { if (client.isLogged()) { // TODO: Figure out how this works in multi account - final statusMsg = await store.getItem(SettingKeys.ownStatusMessage); + final statusMsg = store.getString(SettingKeys.ownStatusMessage); if (statusMsg?.isNotEmpty ?? false) { Logs().v('Send cached status message: "$statusMsg"'); await client.setPresence( @@ -314,7 +320,7 @@ class MatrixState extends State with WidgetsBindingObserver { if (loggedInWithMultipleClients && state != LoginState.loggedIn) { _cancelSubs(c.clientName); widget.clients.remove(c); - ClientManager.removeClientNameFromStore(c.clientName); + ClientManager.removeClientNameFromStore(c.clientName, store); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(L10n.of(context)!.oneClientLoggedOut), @@ -391,7 +397,7 @@ class MatrixState extends State with WidgetsBindingObserver { ); } if (result == OkCancelResult.cancel) { - await store.setItemBool(SettingKeys.showNoGoogle, true); + await store.setBool(SettingKeys.showNoGoogle, true); } }, ); @@ -401,7 +407,7 @@ class MatrixState extends State with WidgetsBindingObserver { } void createVoipPlugin() async { - if (await store.getItemBool(SettingKeys.experimentalVoip) == false) { + if (store.getBool(SettingKeys.experimentalVoip) == false) { voipPlugin = null; return; } @@ -419,47 +425,40 @@ class MatrixState extends State with WidgetsBindingObserver { } void initSettings() { - store.getItem(SettingKeys.wallpaper).then((final path) async { - if (path == null) return; - final file = File(path); - if (await file.exists()) { - wallpaper = file; - } - }); - store.getItem(SettingKeys.fontSizeFactor).then( - (value) => AppConfig.fontSizeFactor = - double.tryParse(value ?? '') ?? AppConfig.fontSizeFactor, - ); - store - .getItemBool(SettingKeys.renderHtml, AppConfig.renderHtml) - .then((value) => AppConfig.renderHtml = value); - store - .getItemBool( - SettingKeys.hideRedactedEvents, - AppConfig.hideRedactedEvents, - ) - .then((value) => AppConfig.hideRedactedEvents = value); - store - .getItemBool(SettingKeys.hideUnknownEvents, AppConfig.hideUnknownEvents) - .then((value) => AppConfig.hideUnknownEvents = value); - store - .getItemBool(SettingKeys.separateChatTypes, AppConfig.separateChatTypes) - .then((value) => AppConfig.separateChatTypes = value); - store - .getItemBool(SettingKeys.autoplayImages, AppConfig.autoplayImages) - .then((value) => AppConfig.autoplayImages = value); - store - .getItemBool( - SettingKeys.sendTypingNotifications, - AppConfig.sendTypingNotifications, - ) - .then((value) => AppConfig.sendTypingNotifications = value); - store - .getItemBool(SettingKeys.sendOnEnter, AppConfig.sendOnEnter) - .then((value) => AppConfig.sendOnEnter = value); - store - .getItemBool(SettingKeys.experimentalVoip, AppConfig.experimentalVoip) - .then((value) => AppConfig.experimentalVoip = value); + final path = store.getString(SettingKeys.wallpaper); + if (path != null) wallpaper = File(path); + + AppConfig.fontSizeFactor = + double.tryParse(store.getString(SettingKeys.fontSizeFactor) ?? '') ?? + AppConfig.fontSizeFactor; + + AppConfig.renderHtml = + store.getBool(SettingKeys.renderHtml) ?? AppConfig.renderHtml; + + AppConfig.hideRedactedEvents = + store.getBool(SettingKeys.hideRedactedEvents) ?? + AppConfig.hideRedactedEvents; + + AppConfig.hideUnknownEvents = + store.getBool(SettingKeys.hideUnknownEvents) ?? + AppConfig.hideUnknownEvents; + + AppConfig.separateChatTypes = + store.getBool(SettingKeys.separateChatTypes) ?? + AppConfig.separateChatTypes; + + AppConfig.autoplayImages = + store.getBool(SettingKeys.autoplayImages) ?? AppConfig.autoplayImages; + + AppConfig.sendTypingNotifications = + store.getBool(SettingKeys.sendTypingNotifications) ?? + AppConfig.sendTypingNotifications; + + AppConfig.sendOnEnter = + store.getBool(SettingKeys.sendOnEnter) ?? AppConfig.sendOnEnter; + + AppConfig.experimentalVoip = store.getBool(SettingKeys.experimentalVoip) ?? + AppConfig.experimentalVoip; } @override diff --git a/lib/widgets/settings_switch_list_tile.dart b/lib/widgets/settings_switch_list_tile.dart index 268e5f187..8f4285a4b 100644 --- a/lib/widgets/settings_switch_list_tile.dart +++ b/lib/widgets/settings_switch_list_tile.dart @@ -23,19 +23,15 @@ class SettingsSwitchListTile extends StatefulWidget { class SettingsSwitchListTileState extends State { @override Widget build(BuildContext context) { - return FutureBuilder( - future: Matrix.of(context) - .store - .getItemBool(widget.storeKey, widget.defaultValue), - builder: (context, snapshot) => SwitchListTile.adaptive( - value: snapshot.data ?? widget.defaultValue, - title: Text(widget.title), - onChanged: (bool newValue) async { - widget.onChanged?.call(newValue); - await Matrix.of(context).store.setItemBool(widget.storeKey, newValue); - setState(() {}); - }, - ), + return SwitchListTile.adaptive( + value: Matrix.of(context).store.getBool(widget.storeKey) ?? + widget.defaultValue, + title: Text(widget.title), + onChanged: (bool newValue) async { + widget.onChanged?.call(newValue); + await Matrix.of(context).store.setBool(widget.storeKey, newValue); + setState(() {}); + }, ); } } diff --git a/scripts/enable-android-google-services.patch b/scripts/enable-android-google-services.patch index 2abc2acab..10800f419 100644 --- a/scripts/enable-android-google-services.patch +++ b/scripts/enable-android-google-services.patch @@ -123,11 +123,11 @@ index 85aa8647..3b7e09e7 100644 } diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart -index cd79b0ab..c2db0f1e 100644 +index 8e67ae92..da4da5c3 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -39,7 +39,7 @@ import '../config/setting_keys.dart'; - import 'famedlysdk_store.dart'; + import '../widgets/matrix.dart'; import 'platform_infos.dart'; -//import 'package:fcm_shared_isolate/fcm_shared_isolate.dart'; @@ -135,7 +135,7 @@ index cd79b0ab..c2db0f1e 100644 class NoTokenException implements Exception { String get cause => 'Cannot get firebase token'; -@@ -65,7 +65,7 @@ class BackgroundPush { +@@ -64,7 +64,7 @@ class BackgroundPush { final pendingTests = >{}; From c0ee1bbdfc51c35ea821f719d05eeb5b3dc84667 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 4 Nov 2023 18:20:01 +0100 Subject: [PATCH 67/93] fix: More stable scroll up to event --- lib/pages/chat/chat.dart | 14 ++++++++++-- lib/pages/chat/chat_event_list.dart | 34 +++++++++-------------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index b2b2c12fd..eef437bf6 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -275,9 +275,19 @@ class ChatController extends State { if (timeline?.allowNewEvent == false || scrollController.position.pixels > 0 && _scrolledUp == false) { setState(() => _scrolledUp = true); - } else if (scrollController.position.pixels == 0 && _scrolledUp == true) { + } else if (scrollController.position.pixels <= 0 && _scrolledUp == true) { setState(() => _scrolledUp = false); } + + if (scrollController.position.pixels == 0 || + scrollController.position.pixels == 64) { + requestFuture(); + } else if (scrollController.position.pixels == + scrollController.position.maxScrollExtent || + scrollController.position.pixels + 64 == + scrollController.position.maxScrollExtent) { + requestHistory(); + } } void _loadDraft() async { @@ -904,7 +914,7 @@ class ChatController extends State { } await scrollController.scrollToIndex( eventIndex, - preferPosition: AutoScrollPosition.end, + preferPosition: AutoScrollPosition.middle, ); _updateScrollController(); } diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 2fd094735..848b46928 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -53,18 +53,11 @@ class ChatEventList extends StatelessWidget { ); } if (controller.timeline!.canRequestFuture) { - return Builder( - builder: (context) { - WidgetsBinding.instance.addPostFrameCallback( - (_) => controller.requestFuture(), - ); - return Center( - child: IconButton( - onPressed: controller.requestFuture, - icon: const Icon(Icons.refresh_outlined), - ), - ); - }, + return Center( + child: IconButton( + onPressed: controller.requestFuture, + icon: const Icon(Icons.refresh_outlined), + ), ); } return Column( @@ -84,18 +77,11 @@ class ChatEventList extends StatelessWidget { ); } if (controller.timeline!.canRequestHistory) { - return Builder( - builder: (context) { - WidgetsBinding.instance.addPostFrameCallback( - (_) => controller.requestHistory(), - ); - return Center( - child: IconButton( - onPressed: controller.requestHistory, - icon: const Icon(Icons.refresh_outlined), - ), - ); - }, + return Center( + child: IconButton( + onPressed: controller.requestHistory, + icon: const Icon(Icons.refresh_outlined), + ), ); } return const SizedBox.shrink(); From 1e4147e31c46f61198772d5f11d5879655b7d1a0 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 4 Nov 2023 19:08:11 +0100 Subject: [PATCH 68/93] fix: SSO with no identity providers --- .../homeserver_picker/homeserver_picker.dart | 21 ++++++++++++------- .../homeserver_picker_view.dart | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index e29cabccb..18b6e5ca5 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -113,19 +113,23 @@ class HomeserverPickerController extends State { Map? _rawLoginTypes; - void ssoLoginAction(String id) async { + void ssoLoginAction(String? id) async { final redirectUrl = kIsWeb ? '${html.window.origin!}/web/auth.html' : isDefaultPlatform ? '${AppConfig.appOpenUrlScheme.toLowerCase()}://login' : 'http://localhost:3001//login'; - final url = - '${Matrix.of(context).getLoginClient().homeserver?.toString()}/_matrix/client/r0/login/sso/redirect/${Uri.encodeComponent(id)}?redirectUrl=${Uri.encodeQueryComponent(redirectUrl)}'; + + final url = Matrix.of(context).getLoginClient().homeserver!.replace( + path: '/_matrix/client/r0/login/sso/redirect${id == null ? '' : '/$id'}', + queryParameters: {'redirectUrl': redirectUrl}, + ); + final urlScheme = isDefaultPlatform ? Uri.parse(redirectUrl).scheme : "http://localhost:3001"; final result = await FlutterWebAuth2.authenticate( - url: url, + url: url.toString(), callbackUrlScheme: urlScheme, ); final token = Uri.parse(result).queryParameters['loginToken']; @@ -144,9 +148,12 @@ class HomeserverPickerController extends State { List? get identityProviders { final loginTypes = _rawLoginTypes; if (loginTypes == null) return null; - final rawProviders = loginTypes.tryGetList('flows')!.singleWhere( - (flow) => flow['type'] == AuthenticationTypes.sso, - )['identity_providers']; + final List? rawProviders = loginTypes.tryGetList('flows')!.singleWhere( + (flow) => flow['type'] == AuthenticationTypes.sso, + )['identity_providers'] ?? + [ + {'id': null}, + ]; final list = (rawProviders as List) .map((json) => IdentityProvider.fromJson(json)) .toList(); diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index 6d25e9ea4..701569990 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -124,7 +124,7 @@ class HomeserverPickerView extends StatelessWidget { L10n.of(context)!.singlesignon, ), onPressed: () => - controller.ssoLoginAction(provider.id!), + controller.ssoLoginAction(provider.id), ), ), ], From 3cea32e1c8022506759320feb7fd5c347ed85ab7 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 4 Nov 2023 19:16:32 +0100 Subject: [PATCH 69/93] refactor: Do not init client in background mode on Android This might make push notifications even faster on android --- lib/main.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index c3dd3466a..26f0e61ac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,15 +21,19 @@ void main() async { // widget bindings are initialized already. WidgetsFlutterBinding.ensureInitialized(); + final backgroundMode = PlatformInfos.isAndroid && + AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState; Logs().nativeColors = !PlatformInfos.isIOS; final store = await SharedPreferences.getInstance(); - final clients = await ClientManager.getClients(store: store); + final clients = await ClientManager.getClients( + store: store, + initialize: !backgroundMode, + ); // If the app starts in detached mode, we assume that it is in // background fetch mode for processing push notifications. This is // currently only supported on Android. - if (PlatformInfos.isAndroid && - AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState) { + if (backgroundMode) { // In the background fetch mode we do not want to waste ressources with // starting the Flutter engine but process incoming push notifications. BackgroundPush.clientOnly(clients.first); From 3946838f337288acb89ccd6c3c0da193656b38cb Mon Sep 17 00:00:00 2001 From: Aryan Arora Date: Sat, 4 Nov 2023 00:47:41 +0530 Subject: [PATCH 70/93] fix: LoadProfileBottomSheet accessing disposed outerContext --- lib/pages/user_bottom_sheet/user_bottom_sheet.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart index 9dbbb84d2..9d87f559c 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet.dart @@ -233,7 +233,10 @@ class UserBottomSheetController extends State { ); if (roomIdResult.error != null) return; widget.outerContext.go('/rooms/${roomIdResult.result!}'); - Navigator.of(context, rootNavigator: false).pop(); + Navigator.of(context, rootNavigator: false) + ..pop() + ..pop(); + widget.outerContext.go('/rooms/${roomIdResult.result!}'); break; case UserBottomSheetAction.ignore: context.go('/rooms/settings/security/ignorelist'); From 17892119aeb2a007520192f2e56f24f33c94a053 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 4 Nov 2023 21:21:06 +0100 Subject: [PATCH 71/93] Revert "refactor: Do not init client in background mode on Android" This reverts commit 3cea32e1c8022506759320feb7fd5c347ed85ab7. --- lib/main.dart | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 26f0e61ac..c3dd3466a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,19 +21,15 @@ void main() async { // widget bindings are initialized already. WidgetsFlutterBinding.ensureInitialized(); - final backgroundMode = PlatformInfos.isAndroid && - AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState; Logs().nativeColors = !PlatformInfos.isIOS; final store = await SharedPreferences.getInstance(); - final clients = await ClientManager.getClients( - store: store, - initialize: !backgroundMode, - ); + final clients = await ClientManager.getClients(store: store); // If the app starts in detached mode, we assume that it is in // background fetch mode for processing push notifications. This is // currently only supported on Android. - if (backgroundMode) { + if (PlatformInfos.isAndroid && + AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState) { // In the background fetch mode we do not want to waste ressources with // starting the Flutter engine but process incoming push notifications. BackgroundPush.clientOnly(clients.first); From 19c67fa3de8b7cde6f3082a59140a9bb994b73c2 Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Fri, 3 Nov 2023 16:47:49 +0000 Subject: [PATCH 72/93] Translated using Weblate (Arabic) Currently translated at 100.0% (599 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/ar/ --- assets/l10n/intl_ar.arb | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_ar.arb b/assets/l10n/intl_ar.arb index c8a5cf503..4aca37af8 100644 --- a/assets/l10n/intl_ar.arb +++ b/assets/l10n/intl_ar.arb @@ -771,7 +771,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "يبدو أنك لا تستخدم خدمات غوغل على هاتفك. هذا قرار جيد للحفاظ على خصوصيتك! من أجل استلام الإشعارات في FluffyChat نقترح استخدام https://microg.org أو https://unifiedpush.org.", + "noGoogleServicesWarning": "يبدو أن خدمة Firebase Cloud Messaging غير متاحة على جهازك. لمواصلة تلقي الإشعارات، نوصي بتثبيت ntfy. باستخدام ntfy أو أي مزود خدمة Unified Push آخر، يمكنك تلقي إشعارات الدفع بطريقة آمنة للبيانات. يمكنك تنزيل ntfy من PlayStore أو من F-Droid.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2627,5 +2627,37 @@ } }, "pleaseEnterANumber": "الرجاء إدخال رقم أكبر من 0", - "@pleaseEnterANumber": {} + "@pleaseEnterANumber": {}, + "banUserDescription": "سيتم حظر المستخدم من الدردشة ولن يتمكن من الدخول إلى الدردشة مرة أخرى حتى يتم رفع الحظر عنه.", + "@banUserDescription": {}, + "removeDevicesDescription": "سيتم تسجيل خروجك من هذا الجهاز ولن تتمكن بعد ذلك من تلقي الرسائل.", + "@removeDevicesDescription": {}, + "unbanUserDescription": "سيتمكن المستخدم من الدخول إلى الدردشة مرة أخرى إذا حاول.", + "@unbanUserDescription": {}, + "todoLists": "(اختباري) لقوائم المهام", + "@todoLists": {}, + "editTodo": "تعديل المهام", + "@editTodo": {}, + "pushNotificationsNotAvailable": "دفع الإخطارات غير متوفرة", + "@pushNotificationsNotAvailable": {}, + "pleaseAddATitle": "يرجى إضافة عنوان", + "@pleaseAddATitle": {}, + "makeAdminDescription": "بمجرد تعيين هذا المستخدم كمسؤول، قد لا تتمكن من التراجع عن هذا لأنه سيكون لديه نفس الأذونات التي تتمتع بها.", + "@makeAdminDescription": {}, + "noTodosYet": "لم تتم إضافة أي مهام إلى هذه الدردشة حتى الآن. أنشئ أول ما يجب عليك فعله وابدأ في التعاون مع الآخرين. 📝", + "@noTodosYet": {}, + "archiveRoomDescription": "سيتم نقل الدردشة إلى الأرشيف. سيتمكن المستخدمون الآخرون من رؤية أنك غادرت الدردشة.", + "@archiveRoomDescription": {}, + "todosUnencrypted": "يرجى ملاحظة أن جميع المهام مرئية للجميع في الدردشة وليست مشفرة تمامًا.", + "@todosUnencrypted": {}, + "newTodo": "ما يجب القيام به جديد", + "@newTodo": {}, + "learnMore": "تعلم المزيد", + "@learnMore": {}, + "todoListChangedError": "عفوًا... لقد تم تغيير قائمة المهام أثناء قيامك بتحريرها.", + "@todoListChangedError": {}, + "roomUpgradeDescription": "سيتم بعد ذلك إعادة إنشاء الدردشة باستخدام إصدار الغرفة الجديد. سيتم إخطار جميع المشاركين بأنهم بحاجة إلى التبديل إلى الدردشة الجديدة. يمكنك معرفة المزيد حول إصدارات الغرف على https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "kickUserDescription": "يتم طرد المستخدم من الدردشة ولكن لا يتم حظره. في الدردشات العامة، يمكن للمستخدم الانضمام مرة أخرى في أي وقت.", + "@kickUserDescription": {} } From eef0541e3d81b4fdf992e862a990b66fbdc56331 Mon Sep 17 00:00:00 2001 From: Haui Date: Wed, 1 Nov 2023 12:46:09 +0000 Subject: [PATCH 73/93] Translated using Weblate (German) Currently translated at 100.0% (599 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/de/ --- assets/l10n/intl_de.arb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 44daefa5f..e472d80ec 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -1888,7 +1888,7 @@ "type": "text", "placeholders": {} }, - "verify": "Bestätigen", + "verify": "Verifizieren", "@verify": { "type": "text", "placeholders": {} @@ -2211,7 +2211,7 @@ "@openChat": {}, "confirmEventUnpin": "Möchtest du das Ereignis wirklich dauerhaft lösen?", "@confirmEventUnpin": {}, - "dismiss": "Ablehnen", + "dismiss": "Verwerfen", "@dismiss": {}, "switchToAccount": "Zu Konto {number} wechseln", "@switchToAccount": { @@ -2657,5 +2657,7 @@ "roomUpgradeDescription": "Der Chat wird dann mit der neuen Raumversion neu erstellt. Alle Teilnehmer werden benachrichtigt, dass sie zum neuen Chat wechseln müssen. Mehr über Raumversionen erfährst du unter https://spec.matrix.org/latest/rooms/", "@roomUpgradeDescription": {}, "kickUserDescription": "Der Benutzer wird aus dem Chat geworfen, aber nicht gebannt. In öffentlichen Chats kann der Benutzer jederzeit wieder beitreten.", - "@kickUserDescription": {} + "@kickUserDescription": {}, + "todosUnencrypted": "Bitte beachte, dass Todos für jeden im Chat sichtbar und nicht Ende zu Ende verschlüsselt sind.", + "@todosUnencrypted": {} } From 2a419603e4d1b2c844e15a14207957cf771927db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Thu, 2 Nov 2023 09:38:12 +0000 Subject: [PATCH 74/93] Translated using Weblate (Estonian) Currently translated at 100.0% (599 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/et/ --- assets/l10n/intl_et.arb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_et.arb b/assets/l10n/intl_et.arb index 3dc383a42..39920bec7 100644 --- a/assets/l10n/intl_et.arb +++ b/assets/l10n/intl_et.arb @@ -1164,7 +1164,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Tundub, et sinu nutiseadmes pole Firebase Cloud Messaging teenuseid. Sinu privaatsuse mõttes on see kindlasti hea otsus! Kui sa soovid FluffyChat'is näha tõuketeavitusi, siis soovitame, et selle jaoks kasutad MicroG või Unified Push liidestust.", + "noGoogleServicesWarning": "Tundub, et sinu nutiseadmes pole Firebase Cloud Messaging teenuseid. Sinu privaatsuse mõttes on see kindlasti hea otsus! Kui sa soovid FluffyChat'is näha tõuketeavitusi, siis soovitame, et selle jaoks kasutad ntfy liidestust. Kasutades ntfy'd või mõnda muud Unified Push standardil põhinevat liidestust saad tõuketeavitusi turvalisel moel. Ntfy rakendus on saadaval nii PlayStore kui F-Droid'i rakendusepoodides.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2655,5 +2655,7 @@ "pleaseEnterANumber": "Palun sisesta 0'st suurem number", "@pleaseEnterANumber": {}, "kickUserDescription": "See kasutaja on nüüd jutuoast välja müksatud, kuid talle pole seatud suhtluskeeldu. Avaliku jututoa puhul saab ta alati uuesti liituda.", - "@kickUserDescription": {} + "@kickUserDescription": {}, + "todosUnencrypted": "Palun arvesta, et tegemiste loend on nähtav kõikidele vestluses osalejatele ja pole läbivalt krüptitud.", + "@todosUnencrypted": {} } From 516e00081b10252d0971c96a26122015c940418a Mon Sep 17 00:00:00 2001 From: xabirequejo Date: Thu, 2 Nov 2023 17:52:45 +0000 Subject: [PATCH 75/93] Translated using Weblate (Basque) Currently translated at 99.8% (598 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/eu/ --- assets/l10n/intl_eu.arb | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/assets/l10n/intl_eu.arb b/assets/l10n/intl_eu.arb index 228b6b268..ea4bfdb3b 100644 --- a/assets/l10n/intl_eu.arb +++ b/assets/l10n/intl_eu.arb @@ -84,7 +84,7 @@ "username": {} } }, - "banFromChat": "Ezarri txatera batzeko debekua", + "banFromChat": "Txatera batzeko debekua ezarri", "@banFromChat": { "type": "text", "placeholders": {} @@ -226,7 +226,7 @@ "type": "text", "placeholders": {} }, - "changeTheNameOfTheGroup": "Aldatu taldearen izena", + "changeTheNameOfTheGroup": "Taldearen izena aldatu", "@changeTheNameOfTheGroup": { "type": "text", "placeholders": {} @@ -372,7 +372,7 @@ "type": "text", "placeholders": {} }, - "deleteMessage": "Ezabatu mezua", + "deleteMessage": "Mezuak ezabatu", "@deleteMessage": { "type": "text", "placeholders": {} @@ -521,12 +521,12 @@ "displayname": {} } }, - "guestsAreForbidden": "Bisitariak debekatuta daude", + "guestsAreForbidden": "Ez, bisitariak ez daude baimenduta", "@guestsAreForbidden": { "type": "text", "placeholders": {} }, - "guestsCanJoin": "Bisitariak batu daitezke", + "guestsCanJoin": "Bai, bisitariak batu daitezke", "@guestsCanJoin": { "type": "text", "placeholders": {} @@ -559,7 +559,7 @@ "type": "text", "placeholders": {} }, - "inviteContact": "Gonbidatu kontaktua", + "inviteContact": "Kontaktuak gonbidatu", "@inviteContact": { "type": "text", "placeholders": {} @@ -630,7 +630,7 @@ "targetName": {} } }, - "kickFromChat": "Kanporatu txatetik", + "kickFromChat": "Txatetik kanporatu", "@kickFromChat": { "type": "text", "placeholders": {} @@ -1021,7 +1021,7 @@ "type": "text", "placeholders": {} }, - "setInvitationLink": "Ezarri gonbidapen-esteka", + "setInvitationLink": "Gonbidapen-esteka ezarri", "@setInvitationLink": { "type": "text", "placeholders": {} @@ -1364,7 +1364,7 @@ "type": "text", "placeholders": {} }, - "editRoomAvatar": "Editatu gelaren abatarra", + "editRoomAvatar": "Gelaren abatarra editatu", "@editRoomAvatar": { "type": "text", "placeholders": {} @@ -1697,7 +1697,7 @@ }, "chatHasBeenAddedToThisSpace": "Txata gune honetara gehitu da", "@chatHasBeenAddedToThisSpace": {}, - "configureChat": "Konfiguratu txata", + "configureChat": "Txata konfiguratu", "@configureChat": { "type": "text", "placeholders": {} @@ -2101,7 +2101,7 @@ "@sendAsText": { "type": "text" }, - "sendMessages": "Bidali mezuak", + "sendMessages": "Mezuak bidali", "@sendMessages": { "type": "text", "placeholders": {} @@ -2643,10 +2643,12 @@ "@learnMore": {}, "todoListChangedError": "Hara… zeregin-zerrenda aldatu da editatzen ari zinen bitartean.", "@todoListChangedError": {}, - "roomUpgradeDescription": "Gela bertsio berri gisa birsortuko da txata. Partehartzaile guztiei jakinaraziko zaie txat berrira aldatu behar direla. Gehiago irakur dezakezu gela bertsioei buruz ondorengo estekan: https://spec.matrix.org/latest/rooms/", + "roomUpgradeDescription": "Gela bertsio berri gisa birsortuko da txata. Partaide guztiei jakinaraziko zaie txat berrira aldatu behar direla. Gehiago irakur dezakezu gela bertsioei buruz ondorengo estekan: https://spec.matrix.org/latest/rooms/", "@roomUpgradeDescription": {}, "pleaseEnterANumber": "Sartu 0 baino zenbaki handiago bat", "@pleaseEnterANumber": {}, "kickUserDescription": "Erabiltzailea txatetik kanporatu da baina ez zaio debekua ezarri. Txat publikoen kasuan, edozein momentutan batu daiteke berriro.", - "@kickUserDescription": {} + "@kickUserDescription": {}, + "todosUnencrypted": "Kontuan izan zereginak txateko guztientzat daudela ikusgai, eta ez daudela ertzetik ertzera zifratuta.", + "@todosUnencrypted": {} } From b464e3ae56179971b96d6ec9cfa88f5de3cb4792 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 3 Nov 2023 11:15:47 +0000 Subject: [PATCH 76/93] Translated using Weblate (Ukrainian) Currently translated at 100.0% (599 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/uk/ --- assets/l10n/intl_uk.arb | 44 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/assets/l10n/intl_uk.arb b/assets/l10n/intl_uk.arb index dfac06603..b9f379f9d 100644 --- a/assets/l10n/intl_uk.arb +++ b/assets/l10n/intl_uk.arb @@ -736,7 +736,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Схоже, на вашому телефоні немає служб Google. Це гарне рішення для вашої приватності! Щоб отримувати push-сповіщення у FluffyChat, ми радимо використовувати https://microg.org/ або https://unifiedpush.org/.", + "noGoogleServicesWarning": "Схоже, Firebase Cloud Messaging недоступна на вашому пристрої. Щоб отримувати push-сповіщення, радимо встановити ntfy. За допомогою ntfy або іншого постачальника Unified Push ви можете отримувати push-сповіщення у безпечний спосіб. Ви можете завантажити ntfy з PlayStore або з F-Droid.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -2619,5 +2619,45 @@ "placeholders": { "seconds": {} } - } + }, + "banUserDescription": "Користувача буде заблоковано в бесіді, і він не зможе знову увійти в неї, поки його не буде розблоковано.", + "@banUserDescription": {}, + "removeDevicesDescription": "Ви вийдете з цього пристрою і більше не зможете отримувати повідомлення.", + "@removeDevicesDescription": {}, + "unbanUserDescription": "Користувач зможе знову увійти в бесіду, якщо спробує.", + "@unbanUserDescription": {}, + "todoLists": "(Бета) Завдання", + "@todoLists": {}, + "editTodo": "Змінити завдання", + "@editTodo": {}, + "pushNotificationsNotAvailable": "Push-сповіщення недоступні", + "@pushNotificationsNotAvailable": {}, + "pleaseAddATitle": "Додайте заголовок", + "@pleaseAddATitle": {}, + "makeAdminDescription": "Після того, як ви зробите цього користувача адміністратором, ви, можливо, не зможете це скасувати, оскільки він матиме ті самі права, що й ви.", + "@makeAdminDescription": {}, + "noTodosYet": "До цієї бесіди ще не додано жодного завдання. Створіть своє перше завдання і почніть співпрацювати з іншими. 📝", + "@noTodosYet": {}, + "archiveRoomDescription": "Бесіду буде переміщено до архіву. Інші користувачі зможуть побачити, що ви вийшли з неї.", + "@archiveRoomDescription": {}, + "todosUnencrypted": "Зауважте, що завдання бачать усі учасники бесіди, і вони не захищені наскрізним шифруванням.", + "@todosUnencrypted": {}, + "hasKnocked": "{user} стукає до вас", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "newTodo": "Нове завдання", + "@newTodo": {}, + "learnMore": "Докладніше", + "@learnMore": {}, + "todoListChangedError": "Отакої... Список завдань було змінено, поки ви його редагували.", + "@todoListChangedError": {}, + "roomUpgradeDescription": "Після цього бесіду буде відтворено з новою версією кімнати. Усі учасники отримають сповіщення, що їм потрібно перейти до нової бесіди. Ви можете дізнатися більше про версії кімнат на https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "pleaseEnterANumber": "Введіть число більше ніж 0", + "@pleaseEnterANumber": {}, + "kickUserDescription": "Користувача вигнали з бесіди, але не заблокували. До загальнодоступних бесід користувач може приєднатися будь-коли.", + "@kickUserDescription": {} } From 1e66432a76b04dc208dd304e32477759c21fbb06 Mon Sep 17 00:00:00 2001 From: Eric Date: Fri, 3 Nov 2023 03:07:02 +0000 Subject: [PATCH 77/93] Translated using Weblate (Chinese (Simplified)) Currently translated at 97.3% (583 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/zh_Hans/ --- assets/l10n/intl_zh.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_zh.arb b/assets/l10n/intl_zh.arb index 8f41bd13e..38f47563b 100644 --- a/assets/l10n/intl_zh.arb +++ b/assets/l10n/intl_zh.arb @@ -1123,7 +1123,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "看起来你手机上没有 Firebase Cloud Messaging。如果仍希望接收 FluffyChat 的推送通知,推荐安装 MicroG 或 Unified Push。", + "noGoogleServicesWarning": "看起来你手机上没有 Firebase Cloud Messaging。如果仍希望接收 FluffyChat 的推送通知,推荐安装 ntfy。借助 ntfy 或另一个 Unified Push 程序,你可以以一种数据安全的方式接收推送通知。你可以从 PlayStore 或 F-Droid 商店下载 ntfy。", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} From 170fa610b71e2d621447e7b98e46fc3358187da3 Mon Sep 17 00:00:00 2001 From: Aminda Suomalainen Date: Thu, 2 Nov 2023 12:36:50 +0000 Subject: [PATCH 78/93] Translated using Weblate (Finnish) Currently translated at 97.6% (585 of 599 strings) Translation: FluffyChat/Translations Translate-URL: https://hosted.weblate.org/projects/fluffychat/translations/fi/ --- assets/l10n/intl_fi.arb | 129 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 5 deletions(-) diff --git a/assets/l10n/intl_fi.arb b/assets/l10n/intl_fi.arb index b0aceb2de..0b3839887 100644 --- a/assets/l10n/intl_fi.arb +++ b/assets/l10n/intl_fi.arb @@ -764,7 +764,7 @@ "type": "text", "placeholders": {} }, - "inviteText": "{username} kutsui sinutFluffyChattiin. \n1. Asenna FluffyChat osoitteesta: https://fluffychat.im \n2. Rekisteröidy tai kirjaudu sisään\n3. Avaa kutsulinkki: {link}", + "inviteText": "{username} kutsui sinut FluffyChattiin.\n1. Viereaile sivulla: https://fluffychat.im ja asenna sovellus\n2. Rekisteröidy tai kirjaudu sisään\n3. Avaa kutsulinkki:\n{link}", "@inviteText": { "type": "text", "placeholders": { @@ -1249,7 +1249,7 @@ "type": "text", "placeholders": {} }, - "noGoogleServicesWarning": "Vaikuttaa siltä, ettei puhelimessasi ole Google-palveluita. Se on hyvä päätös yksityisyytesi kannalta! Vastaanottaaksesi push-notifikaatioita FluffyChätissä suosittelemme https://microg.org/ tai https://unifiedpush.org/ käyttämistä.", + "noGoogleServicesWarning": "Firebase Cloud Messaging -palvelu ei vaikuta olevan saatavilla laitteellasi. Saadaksesi push-ilmoituksia silti, suosittelemme Ntfy-sovelluksen asentamista. Käyttämällä Ntfy-sovellusta tai muuta Unified Push -tarjoajaa, saat push-ilmoitukset tietoturvallisella tavalla. Voit ladata Ntfy-sovelluksen Play Kaupasta tai F-Droidista.", "@noGoogleServicesWarning": { "type": "text", "placeholders": {} @@ -1776,7 +1776,7 @@ "type": "text", "placeholders": {} }, - "wallpaper": "Taustakuva", + "wallpaper": "Taustakuva:", "@wallpaper": { "type": "text", "placeholders": {} @@ -2366,7 +2366,7 @@ "@whyIsThisMessageEncrypted": {}, "noKeyForThisMessage": "Tämä voi tapahtua mikäli viesti lähetettiin ennen sisäänkirjautumistasi tälle laitteelle.\n\nOn myös mahdollista, että lähettäjä on estänyt tämän laitteen tai jokin meni pieleen verkkoyhteyden kanssa.\n\nPystytkö lukemaan viestin toisella istunnolla? Siinä tapauksessa voit siirtää viestin siltä! Mene Asetukset > Laitteet ja varmista, että laitteesi ovat varmistaneet toisensa. Seuraavankerran avatessasi huoneen ja molempien istuntojen ollessa etualalla, avaimet siirretään automaattisesti.\n\nHaluatko varmistaa ettet menetä avaimia uloskirjautuessa tai laitteita vaihtaessa? Varmista avainvarmuuskopion käytössäolo asetuksista.", "@noKeyForThisMessage": {}, - "commandHint_markasdm": "Merkitse yksityiskeskusteluksi", + "commandHint_markasdm": "Merkitse yksityiskeskusteluksi syötetyn Matrix IDn kanssa", "@commandHint_markasdm": {}, "foregroundServiceRunning": "Tämä ilmoitus näkyy etualapalvelun ollessa käynnissä.", "@foregroundServiceRunning": {}, @@ -2501,5 +2501,124 @@ "continueWith": "Jatka käyttäen:", "@continueWith": {}, "pleaseTryAgainLaterOrChooseDifferentServer": "Yritä myöhemmin uudelleen tai valitse toinen palvelin.", - "@pleaseTryAgainLaterOrChooseDifferentServer": {} + "@pleaseTryAgainLaterOrChooseDifferentServer": {}, + "setColorTheme": "Aseta väriteema:", + "@setColorTheme": {}, + "requests": "Pyynnöt", + "@requests": {}, + "tryAgain": "Yritä uudelleen", + "@tryAgain": {}, + "messagesStyle": "Viestit:", + "@messagesStyle": {}, + "chatDescription": "Keskustelun kuvaus", + "@chatDescription": {}, + "invalidServerName": "Virheellinen palvelimen nimi", + "@invalidServerName": {}, + "chatPermissions": "Keskustelun oikeudet", + "@chatPermissions": {}, + "setChatDescription": "Asetti keskustelun kuvauksen", + "@setChatDescription": {}, + "importFromZipFile": "Tuo .zip -tiedostosta", + "@importFromZipFile": {}, + "redactedBy": "Poistanut {username}", + "@redactedBy": { + "type": "text", + "placeholders": { + "username": {} + } + }, + "signInWith": "Kirjaudu sisään palvelulla {provider}", + "@signInWith": { + "type": "text", + "placeholders": { + "provider": {} + } + }, + "optionalRedactReason": "(Vapaaehtoinen) Syy tämän viestin poistamiselle...", + "@optionalRedactReason": {}, + "archiveRoomDescription": "Keskustelu siirretään arkistoon. Muut käyttäjät näkevät sinun poistuneen keskustelusta.", + "@archiveRoomDescription": {}, + "exportEmotePack": "Vie emotepaketti .zip-tiedostona", + "@exportEmotePack": {}, + "savedEmotePack": "Tallennettiin emotepaketti sijaintiin {path}!", + "@savedEmotePack": { + "type": "text", + "placeholders": { + "path": {} + } + }, + "inviteContactToGroupQuestion": "Tahdotko kutsua yhteystiedon {contact} keskusteluun \"{groupName}\"?", + "@inviteContactToGroupQuestion": {}, + "redactedByBecause": "Poistanut {username} syystä: \"{reason}\"", + "@redactedByBecause": { + "type": "text", + "placeholders": { + "username": {}, + "reason": {} + } + }, + "importZipFile": "Tuo zip-tiedosto", + "@importZipFile": {}, + "anyoneCanKnock": "Kaikki voivat koputtaa", + "@anyoneCanKnock": {}, + "redactMessageDescription": "Viesti poistetaan kaikilta keskustelun osallistujilta. Tätä ei voida kumota.", + "@redactMessageDescription": {}, + "invalidInput": "Virheellinen syöte!", + "@invalidInput": {}, + "addChatDescription": "Lisää keskustelulle kuvaus", + "@addChatDescription": {}, + "hasKnocked": "{user} on koputtanut", + "@hasKnocked": { + "placeholders": { + "user": {} + } + }, + "directChat": "Yksityiskeskustelu", + "@directChat": {}, + "noOneCanJoin": "Kukaan ei voi liittyä", + "@noOneCanJoin": {}, + "wrongPinEntered": "Väärä pin-koodi! Yritä uudelleen {seconds} sekuntin kuluttua...", + "@wrongPinEntered": { + "type": "text", + "placeholders": { + "seconds": {} + } + }, + "sendTypingNotifications": "Lähetä kirjoitusilmoituksia", + "@sendTypingNotifications": {}, + "inviteGroupChat": "Kutsu ryhmäkeskusteluun", + "@inviteGroupChat": {}, + "invitePrivateChat": "Kutsu yksityiskeskusteluun", + "@invitePrivateChat": {}, + "importEmojis": "Tuo emojit", + "@importEmojis": {}, + "noChatDescriptionYet": "Keskustelun kuvausta ei ole vielä luotu.", + "@noChatDescriptionYet": {}, + "notAnImage": "Tämä ei ole kuvatiedosto.", + "@notAnImage": {}, + "chatDescriptionHasBeenChanged": "Keskustelun kuvaus muutettu", + "@chatDescriptionHasBeenChanged": {}, + "roomUpgradeDescription": "Keskustelu luodaan uudelleen uudella huoneversiolla. Kaikille osallistujille ilmoitetaan, että heidän tulee siirtyä uuteen keskusteluun. Voit lukea lisää huoneversioista osoitteesta https://spec.matrix.org/latest/rooms/", + "@roomUpgradeDescription": {}, + "pleaseEnterANumber": "Syötä suurempi luku kuin 0", + "@pleaseEnterANumber": {}, + "profileNotFound": "Käyttäjää ei löydy palvelimelta. Tämä voi olla yhteysongelma tai käyttäjä ei ole olemassa.", + "@profileNotFound": {}, + "shareInviteLink": "Jaa kutsulinkki", + "@shareInviteLink": {}, + "emoteKeyboardNoRecents": "Viimeaikoina käytetyt emotet tulevat näkymään täällä...", + "@emoteKeyboardNoRecents": { + "type": "text", + "placeholders": {} + }, + "setTheme": "Aseta teema:", + "@setTheme": {}, + "replace": "Korvaa", + "@replace": {}, + "createGroup": "Luo ryhmä", + "@createGroup": {}, + "importNow": "Tuo nyt", + "@importNow": {}, + "invite": "Kutsu", + "@invite": {} } From c9d0279405b865781c2ccf115d30de3c996f2391 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 09:11:14 +0100 Subject: [PATCH 79/93] docs: Fix auto labels for issue templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index af0a4e863..485bfd8c5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: 🐛 Bug report description: Create a report to help us improve -labels: ["Bug"] +labels: bug body: - type: textarea id: bug-description diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index a29949d66..c9f3b8acb 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,6 +1,6 @@ name: 💡 Feature Request description: Suggest an idea for this project -labels: ["Enhancement"] +labels: feature body: - type: textarea id: feature-description From 0dffdc03275ccfcd211ccf1e59111df616f82b66 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 09:14:34 +0100 Subject: [PATCH 80/93] docs: Add test label --- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- .github/ISSUE_TEMPLATE/test_report.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index c9f3b8acb..4007c8837 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,6 +1,6 @@ name: 💡 Feature Request description: Suggest an idea for this project -labels: feature +labels: enhancement body: - type: textarea id: feature-description diff --git a/.github/ISSUE_TEMPLATE/test_report.md b/.github/ISSUE_TEMPLATE/test_report.md index 4ae34f303..beda783fb 100644 --- a/.github/ISSUE_TEMPLATE/test_report.md +++ b/.github/ISSUE_TEMPLATE/test_report.md @@ -2,6 +2,7 @@ name: 📝 Test about: A detailed protocol for testing all features title: 'Test Report' +label: test --- 1. App receives push notifications over Firebase Cloud Messaging when it is in background/terminated: From bc8eb748127f9229d73e658e7a0f99897e773ec5 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 09:24:32 +0100 Subject: [PATCH 81/93] docs: Stale and close issues and PRs automatically --- .github/workflows/issue_pr_management.yaml | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/issue_pr_management.yaml diff --git a/.github/workflows/issue_pr_management.yaml b/.github/workflows/issue_pr_management.yaml new file mode 100644 index 000000000..f9a3af0b5 --- /dev/null +++ b/.github/workflows/issue_pr_management.yaml @@ -0,0 +1,26 @@ +name: Close inactive issues +on: + schedule: + - cron: "30 1 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + days-before-issue-stale: 30 + days-before-issue-close: 14 + stale-issue-label: "stale" + stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." + close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." + stale-pr-message: "This pull request is stale because it has been open for 30 days with no activity." + close-pr-message: "This pull request was closed because it has been inactive for 14 days since being marked as stale." + days-before-pr-stale: 30 + days-before-pr-close: 14 + exempt-milestones: true + stale-issue-label: stale + repo-token: ${{ secrets.GITHUB_TOKEN }} From e70ff68c48e6cffe90eb7c8b3a19ba1bad75c16f Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 09:36:29 +0100 Subject: [PATCH 82/93] docs: Update inactive issue workflow --- .github/workflows/issue_pr_management.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/issue_pr_management.yaml b/.github/workflows/issue_pr_management.yaml index f9a3af0b5..9062b68f8 100644 --- a/.github/workflows/issue_pr_management.yaml +++ b/.github/workflows/issue_pr_management.yaml @@ -1,4 +1,4 @@ -name: Close inactive issues +name: Close Inactive Issues And PRs on: schedule: - cron: "30 1 * * *" @@ -23,4 +23,5 @@ jobs: days-before-pr-close: 14 exempt-milestones: true stale-issue-label: stale + exempt-assignees: krille-chan repo-token: ${{ secrets.GITHUB_TOKEN }} From 9f0bcd5523eb8ceb5813c1d9347601f6e5e5b548 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 10:09:02 +0100 Subject: [PATCH 83/93] fix: Remove failed to sent events --- lib/pages/chat/chat.dart | 19 +++++++++++++++ lib/pages/chat/chat_input_row.dart | 38 ++++++++++++++++++++++-------- lib/pages/chat/chat_view.dart | 27 +++++++++++---------- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index eef437bf6..ecdb20af6 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -772,6 +772,24 @@ class ChatController extends State { ); } + void deleteErrorEventsAction() async { + try { + if (selectedEvents.any((event) => event.status != EventStatus.error)) { + throw Exception( + 'Tried to delete failed to send events but one event is not failed to sent', + ); + } + for (final event in selectedEvents) { + await event.remove(); + } + } catch (e, s) { + ErrorReporter( + context, + 'Error while delete error events action', + ).onErrorCallback(e, s); + } + } + void redactEventsAction() async { final reasonInput = selectedEvents.any((event) => event.status.isSent) ? await showTextInputDialog( @@ -832,6 +850,7 @@ class ChatController extends State { if (isArchived) return false; final clients = Matrix.of(context).currentBundle; for (final event in selectedEvents) { + if (!event.status.isSent) return false; if (event.canRedact == false && !(clients!.any((cl) => event.senderId == cl!.userID))) return false; } diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index c7401d0dd..6f5fe4289 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -30,18 +30,36 @@ class ChatInputRow extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: controller.selectMode ? [ - SizedBox( - height: 56, - child: TextButton( - onPressed: controller.forwardEventsAction, - child: Row( - children: [ - const Icon(Icons.keyboard_arrow_left_outlined), - Text(L10n.of(context)!.forward), - ], + if (controller.selectedEvents + .every((event) => event.status == EventStatus.error)) + SizedBox( + height: 56, + child: TextButton( + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).colorScheme.error, + ), + onPressed: controller.deleteErrorEventsAction, + child: Row( + children: [ + const Icon(Icons.delete), + Text(L10n.of(context)!.delete), + ], + ), + ), + ) + else + SizedBox( + height: 56, + child: TextButton( + onPressed: controller.forwardEventsAction, + child: Row( + children: [ + const Icon(Icons.keyboard_arrow_left_outlined), + Text(L10n.of(context)!.forward), + ], + ), ), ), - ), controller.selectedEvents.length == 1 ? controller.selectedEvents.first .getDisplayEvent(controller.timeline!) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 9e8ec9f09..798af551a 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -91,20 +91,21 @@ class ChatView extends StatelessWidget { ], ), ), - PopupMenuItem( - value: _EventContextAction.report, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.shield_outlined, - color: Colors.red, - ), - const SizedBox(width: 12), - Text(L10n.of(context)!.reportMessage), - ], + if (controller.selectedEvents.single.status.isSent) + PopupMenuItem( + value: _EventContextAction.report, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.shield_outlined, + color: Colors.red, + ), + const SizedBox(width: 12), + Text(L10n.of(context)!.reportMessage), + ], + ), ), - ), ], ), ]; From 2d7301ea50cd8b107f4eef177c356b720bedb4a9 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 10:24:59 +0100 Subject: [PATCH 84/93] fix: Routing glitch when using SSO on desktop --- .../homeserver_picker/homeserver_app_bar.dart | 1 + .../homeserver_picker/homeserver_picker.dart | 57 +++++++++++++------ .../homeserver_picker_view.dart | 25 ++++---- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/lib/pages/homeserver_picker/homeserver_app_bar.dart b/lib/pages/homeserver_picker/homeserver_app_bar.dart index e29aeb7e6..3b7611ed2 100644 --- a/lib/pages/homeserver_picker/homeserver_app_bar.dart +++ b/lib/pages/homeserver_picker/homeserver_app_bar.dart @@ -50,6 +50,7 @@ class HomeserverAppBar extends StatelessWidget { controller.checkHomeserverAction(); }, textFieldConfiguration: TextFieldConfiguration( + enabled: !controller.isLoggingIn, controller: controller.homeserverController, decoration: InputDecoration( prefixIcon: Navigator.of(context).canPop() diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index 18b6e5ca5..cf23595b2 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -8,7 +8,6 @@ import 'package:collection/collection.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_web_auth_2/flutter_web_auth_2.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:matrix/matrix.dart'; @@ -33,6 +32,8 @@ class HomeserverPicker extends StatefulWidget { class HomeserverPickerController extends State { bool isLoading = false; + bool isLoggingIn = false; + final TextEditingController homeserverController = TextEditingController( text: AppConfig.defaultHomeserver, ); @@ -135,14 +136,27 @@ class HomeserverPickerController extends State { final token = Uri.parse(result).queryParameters['loginToken']; if (token?.isEmpty ?? false) return; - await showFutureLoadingDialog( - context: context, - future: () => Matrix.of(context).getLoginClient().login( + setState(() { + error = null; + isLoading = isLoggingIn = true; + }); + try { + await Matrix.of(context).getLoginClient().login( LoginType.mLoginToken, token: token, initialDeviceDisplayName: PlatformInfos.clientName, - ), - ); + ); + } catch (e) { + setState(() { + error = e.toLocalizedString(context); + }); + } finally { + if (mounted) { + setState(() { + isLoading = isLoggingIn = false; + }); + } + } } List? get identityProviders { @@ -181,18 +195,25 @@ class HomeserverPickerController extends State { ); final file = picked?.files.firstOrNull; if (file == null) return; - await showFutureLoadingDialog( - context: context, - future: () async { - try { - final client = Matrix.of(context).getLoginClient(); - await client.importDump(String.fromCharCodes(file.bytes!)); - Matrix.of(context).initMatrix(); - } catch (e, s) { - Logs().e('Future error:', e, s); - } - }, - ); + setState(() { + error = null; + isLoading = isLoggingIn = true; + }); + try { + final client = Matrix.of(context).getLoginClient(); + await client.importDump(String.fromCharCodes(file.bytes!)); + Matrix.of(context).initMatrix(); + } catch (e) { + setState(() { + error = e.toLocalizedString(context); + }); + } finally { + if (mounted) { + setState(() { + isLoading = isLoggingIn = false; + }); + } + } } } diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index 701569990..c3a585a2c 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -57,15 +57,6 @@ class HomeserverPickerView extends StatelessWidget { ? const Center(child: CircularProgressIndicator.adaptive()) : ListView( children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: FluffyThemes.isColumnMode(context) - ? Image.asset( - 'assets/info-logo.png', - height: 96, - ) - : Image.asset('assets/banner_transparent.png'), - ), const SizedBox(height: 12), if (errorText != null) ...[ const Center( @@ -97,8 +88,18 @@ class HomeserverPickerView extends StatelessWidget { ), ), ), - const SizedBox(height: 12), - ], + const SizedBox(height: 36), + ] else + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8.0), + child: FluffyThemes.isColumnMode(context) + ? Image.asset( + 'assets/info-logo.png', + height: 96, + ) + : Image.asset('assets/banner_transparent.png'), + ), if (identityProviders != null) ...[ ...identityProviders.map( (provider) => _LoginButton( @@ -143,6 +144,8 @@ class HomeserverPickerView extends StatelessWidget { style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), + foregroundColor: + Theme.of(context).colorScheme.secondary, ), onPressed: controller.restoreBackup, child: Text( From 7d7e23414214311a83b58258aeccee65af3e04c4 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 12:38:19 +0100 Subject: [PATCH 85/93] chore: Follow up delete failed to send events --- lib/pages/chat/chat.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index ecdb20af6..836b41fd1 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -782,6 +782,7 @@ class ChatController extends State { for (final event in selectedEvents) { await event.remove(); } + setState(selectedEvents.clear); } catch (e, s) { ErrorReporter( context, From 809ee213b68bb025dd12a5d37f837b8d2f95a282 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 14:07:07 +0100 Subject: [PATCH 86/93] feat: Make all text in chat selectable on desktop --- lib/pages/chat/chat_event_list.dart | 184 +++++++++++----------- lib/pages/chat/events/message.dart | 204 ++++++++++++------------- lib/utils/custom_scroll_behaviour.dart | 1 - lib/widgets/hover_builder.dart | 29 ++++ 4 files changed, 223 insertions(+), 195 deletions(-) create mode 100644 lib/widgets/hover_builder.dart diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 848b46928..baa2e8806 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -31,104 +31,106 @@ class ChatEventList extends StatelessWidget { thisEventsKeyMap[controller.timeline!.events[i].eventId] = i; } - return ListView.custom( - padding: EdgeInsets.only( - top: 16, - bottom: 4, - left: horizontalPadding, - right: horizontalPadding, - ), - reverse: true, - controller: controller.scrollController, - keyboardDismissBehavior: PlatformInfos.isIOS - ? ScrollViewKeyboardDismissBehavior.onDrag - : ScrollViewKeyboardDismissBehavior.manual, - childrenDelegate: SliverChildBuilderDelegate( - (BuildContext context, int i) { - // Footer to display typing indicator and read receipts: - if (i == 0) { - if (controller.timeline!.isRequestingFuture) { - return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), + return SelectionArea( + child: ListView.custom( + padding: EdgeInsets.only( + top: 16, + bottom: 4, + left: horizontalPadding, + right: horizontalPadding, + ), + reverse: true, + controller: controller.scrollController, + keyboardDismissBehavior: PlatformInfos.isIOS + ? ScrollViewKeyboardDismissBehavior.onDrag + : ScrollViewKeyboardDismissBehavior.manual, + childrenDelegate: SliverChildBuilderDelegate( + (BuildContext context, int i) { + // Footer to display typing indicator and read receipts: + if (i == 0) { + if (controller.timeline!.isRequestingFuture) { + return const Center( + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ); + } + if (controller.timeline!.canRequestFuture) { + return Center( + child: IconButton( + onPressed: controller.requestFuture, + icon: const Icon(Icons.refresh_outlined), + ), + ); + } + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SeenByRow(controller), + TypingIndicators(controller), + ], ); } - if (controller.timeline!.canRequestFuture) { - return Center( - child: IconButton( - onPressed: controller.requestFuture, - icon: const Icon(Icons.refresh_outlined), - ), - ); - } - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SeenByRow(controller), - TypingIndicators(controller), - ], - ); - } - // Request history button or progress indicator: - if (i == controller.timeline!.events.length + 1) { - if (controller.timeline!.isRequestingHistory) { - return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ); + // Request history button or progress indicator: + if (i == controller.timeline!.events.length + 1) { + if (controller.timeline!.isRequestingHistory) { + return const Center( + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ); + } + if (controller.timeline!.canRequestHistory) { + return Center( + child: IconButton( + onPressed: controller.requestHistory, + icon: const Icon(Icons.refresh_outlined), + ), + ); + } + return const SizedBox.shrink(); } - if (controller.timeline!.canRequestHistory) { - return Center( - child: IconButton( - onPressed: controller.requestHistory, - icon: const Icon(Icons.refresh_outlined), - ), - ); - } - return const SizedBox.shrink(); - } - // The message at this index: - final event = controller.timeline!.events[i - 1]; + // The message at this index: + final event = controller.timeline!.events[i - 1]; - return AutoScrollTag( - key: ValueKey(event.eventId), - index: i - 1, - controller: controller.scrollController, - child: event.isVisibleInGui - ? Message( - event, - onSwipe: (direction) => - controller.replyAction(replyTo: event), - onInfoTab: controller.showEventInfo, - onAvatarTab: (Event event) => showAdaptiveBottomSheet( - context: context, - builder: (c) => UserBottomSheet( - user: event.senderFromMemoryOrFallback, - outerContext: context, - onMention: () => controller.sendController.text += - '${event.senderFromMemoryOrFallback.mention} ', + return AutoScrollTag( + key: ValueKey(event.eventId), + index: i - 1, + controller: controller.scrollController, + child: event.isVisibleInGui + ? Message( + event, + onSwipe: (direction) => + controller.replyAction(replyTo: event), + onInfoTab: controller.showEventInfo, + onAvatarTab: (Event event) => showAdaptiveBottomSheet( + context: context, + builder: (c) => UserBottomSheet( + user: event.senderFromMemoryOrFallback, + outerContext: context, + onMention: () => controller.sendController.text += + '${event.senderFromMemoryOrFallback.mention} ', + ), ), - ), - onSelect: controller.onSelectMessage, - scrollToEventId: (String eventId) => - controller.scrollToEventId(eventId), - longPressSelect: controller.selectedEvents.isNotEmpty, - selected: controller.selectedEvents - .any((e) => e.eventId == event.eventId), - timeline: controller.timeline!, - displayReadMarker: - controller.readMarkerEventId == event.eventId && - controller.timeline?.allowNewEvent == false, - nextEvent: i < controller.timeline!.events.length - ? controller.timeline!.events[i] - : null, - ) - : const SizedBox.shrink(), - ); - }, - childCount: controller.timeline!.events.length + 2, - findChildIndexCallback: (key) => - controller.findChildIndexCallback(key, thisEventsKeyMap), + onSelect: controller.onSelectMessage, + scrollToEventId: (String eventId) => + controller.scrollToEventId(eventId), + longPressSelect: controller.selectedEvents.isNotEmpty, + selected: controller.selectedEvents + .any((e) => e.eventId == event.eventId), + timeline: controller.timeline!, + displayReadMarker: + controller.readMarkerEventId == event.eventId && + controller.timeline?.allowNewEvent == false, + nextEvent: i < controller.timeline!.events.length + ? controller.timeline!.events[i] + : null, + ) + : const SizedBox.shrink(), + ); + }, + childCount: controller.timeline!.events.length + 2, + findChildIndexCallback: (key) => + controller.findChildIndexCallback(key, thisEventsKeyMap), + ), ), ); } diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 477af9153..84083d2db 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../../config/app_config.dart'; +import '../../../widgets/hover_builder.dart'; import 'message_content.dart'; import 'message_reactions.dart'; import 'reply_content.dart'; @@ -44,10 +45,6 @@ class Message extends StatelessWidget { super.key, }); - /// Indicates wheither the user may use a mouse instead - /// of touchscreen. - static bool useMouse = false; - @override Widget build(BuildContext context) { if (!{ @@ -117,31 +114,42 @@ class Message extends StatelessWidget { : Theme.of(context).colorScheme.primaryContainer; } - final row = Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: rowMainAxisAlignment, - children: [ - sameSender || ownMessage - ? SizedBox( + final row = InkWell( + onTap: longPressSelect ? () => onSelect!(event) : null, + onLongPress: () => onSelect!(event), + child: HoverBuilder( + builder: (context, hovered) => Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: rowMainAxisAlignment, + children: [ + if (hovered || selected) + SizedBox( width: Avatar.defaultSize, - child: Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Center( - child: SizedBox( - width: 16, - height: 16, - child: event.status == EventStatus.sending - ? const CircularProgressIndicator.adaptive( - strokeWidth: 2, - ) - : event.status == EventStatus.error - ? const Icon(Icons.error, color: Colors.red) - : null, - ), + height: Avatar.defaultSize - 8, + child: Checkbox.adaptive( + value: selected, + onChanged: (_) => onSelect?.call(event), + ), + ) + else if (sameSender || ownMessage) + SizedBox( + width: Avatar.defaultSize, + child: Center( + child: SizedBox( + width: 16, + height: 16, + child: event.status == EventStatus.sending + ? const CircularProgressIndicator.adaptive( + strokeWidth: 2, + ) + : event.status == EventStatus.error + ? const Icon(Icons.error, color: Colors.red) + : null, ), ), ) - : FutureBuilder( + else + FutureBuilder( future: event.fetchSenderUser(), builder: (context, snapshot) { final user = @@ -153,61 +161,59 @@ class Message extends StatelessWidget { ); }, ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - if (!sameSender) - Padding( - padding: const EdgeInsets.only(left: 8.0, bottom: 4), - child: ownMessage || event.room.isDirectChat - ? const SizedBox(height: 12) - : FutureBuilder( - future: event.fetchSenderUser(), - builder: (context, snapshot) { - final displayname = - snapshot.data?.calcDisplayname() ?? - event.senderFromMemoryOrFallback - .calcDisplayname(); - return Text( - displayname, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: (Theme.of(context).brightness == - Brightness.light - ? displayname.color - : displayname.lightColorText), - ), - ); - }, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (!sameSender) + Padding( + padding: const EdgeInsets.only(left: 8.0, bottom: 4), + child: ownMessage || event.room.isDirectChat + ? const SizedBox(height: 12) + : FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + final displayname = + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback + .calcDisplayname(); + return Text( + displayname, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: (Theme.of(context).brightness == + Brightness.light + ? displayname.color + : displayname.lightColorText), + ), + ); + }, + ), + ), + Container( + alignment: alignment, + padding: const EdgeInsets.only(left: 8), + child: Material( + color: noBubble ? Colors.transparent : color, + borderRadius: borderRadius, + clipBehavior: Clip.antiAlias, + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), ), - ), - Container( - alignment: alignment, - padding: const EdgeInsets.only(left: 8), - child: Material( - color: noBubble ? Colors.transparent : color, - borderRadius: borderRadius, - clipBehavior: Clip.antiAlias, - child: Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - ), - padding: noBubble || noPadding - ? EdgeInsets.zero - : const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 1.5, - ), - child: Stack( - children: [ - Column( + padding: noBubble || noPadding + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 1.5, + ), + child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -284,15 +290,15 @@ class Message extends StatelessWidget { ), ], ), - ], + ), ), ), - ), + ], ), - ], - ), + ), + ], ), - ], + ), ); Widget container; if (event.hasAggregatedEvents(timeline, RelationshipTypes.reaction) || @@ -392,26 +398,18 @@ class Message extends StatelessWidget { direction: SwipeDirection.endToStart, onSwipe: onSwipe, child: Center( - child: MouseRegion( - onEnter: (_) => useMouse = true, - onExit: (_) => useMouse = false, - child: InkWell( - onTap: longPressSelect || useMouse ? () => onSelect!(event) : null, - onLongPress: () => onSelect!(event), - child: Container( - color: selected - ? Theme.of(context).primaryColor.withAlpha(100) - : Theme.of(context).primaryColor.withAlpha(0), - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 2.5, - ), - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - child: container, - ), + child: Container( + color: selected + ? Theme.of(context).primaryColor.withAlpha(100) + : Theme.of(context).primaryColor.withAlpha(0), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 2.5, ), + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: container, ), ), ); diff --git a/lib/utils/custom_scroll_behaviour.dart b/lib/utils/custom_scroll_behaviour.dart index 719b75473..ba25e9564 100644 --- a/lib/utils/custom_scroll_behaviour.dart +++ b/lib/utils/custom_scroll_behaviour.dart @@ -5,7 +5,6 @@ class CustomScrollBehavior extends MaterialScrollBehavior { @override Set get dragDevices => { PointerDeviceKind.touch, - PointerDeviceKind.mouse, PointerDeviceKind.trackpad, }; } diff --git a/lib/widgets/hover_builder.dart b/lib/widgets/hover_builder.dart new file mode 100644 index 000000000..f895d8532 --- /dev/null +++ b/lib/widgets/hover_builder.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class HoverBuilder extends StatefulWidget { + final Widget Function(BuildContext context, bool hovered) builder; + const HoverBuilder({required this.builder, super.key}); + + @override + State createState() => _HoverBuilderState(); +} + +class _HoverBuilderState extends State { + bool hovered = false; + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: (_) => hovered + ? null + : setState(() { + hovered = true; + }), + onExit: (_) => !hovered + ? null + : setState(() { + hovered = false; + }), + child: widget.builder(context, hovered), + ); + } +} From c313b03f71318f696a333c28a9de6021d590eeba Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sun, 5 Nov 2023 17:02:30 +0100 Subject: [PATCH 87/93] chore: Follow up select event handling --- lib/pages/chat/chat_event_list.dart | 3 +- lib/pages/chat/events/message.dart | 185 ++++++++++++++-------------- 2 files changed, 94 insertions(+), 94 deletions(-) diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index baa2e8806..ea4af0daa 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -98,8 +98,7 @@ class ChatEventList extends StatelessWidget { child: event.isVisibleInGui ? Message( event, - onSwipe: (direction) => - controller.replyAction(replyTo: event), + onSwipe: () => controller.replyAction(replyTo: event), onInfoTab: controller.showEventInfo, onAvatarTab: (Event event) => showAdaptiveBottomSheet( context: context, diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 84083d2db..05ea42870 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -25,7 +25,7 @@ class Message extends StatelessWidget { final void Function(Event)? onAvatarTab; final void Function(Event)? onInfoTab; final void Function(String)? scrollToEventId; - final void Function(SwipeDirection) onSwipe; + final void Function() onSwipe; final bool longPressSelect; final bool selected; final Timeline timeline; @@ -116,7 +116,6 @@ class Message extends StatelessWidget { final row = InkWell( onTap: longPressSelect ? () => onSelect!(event) : null, - onLongPress: () => onSelect!(event), child: HoverBuilder( builder: (context, hovered) => Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -195,100 +194,102 @@ class Message extends StatelessWidget { Container( alignment: alignment, padding: const EdgeInsets.only(left: 8), - child: Material( - color: noBubble ? Colors.transparent : color, - borderRadius: borderRadius, - clipBehavior: Clip.antiAlias, - child: Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - ), - padding: noBubble || noPadding - ? EdgeInsets.zero - : const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 1.5, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (event.relationshipType == - RelationshipTypes.reply) - FutureBuilder( - future: event.getReplyEvent(timeline), - builder: (BuildContext context, snapshot) { - final replyEvent = snapshot.hasData - ? snapshot.data! - : Event( - eventId: event.relationshipEventId!, - content: { - 'msgtype': 'm.text', - 'body': '...', - }, - senderId: event.senderId, - type: 'm.room.message', - room: event.room, - status: EventStatus.sent, - originServerTs: DateTime.now(), - ); - return InkWell( - onTap: () { - if (scrollToEventId != null) { - scrollToEventId!(replyEvent.eventId); - } - }, - child: AbsorbPointer( - child: Container( - margin: const EdgeInsets.symmetric( - vertical: 4.0, - ), - child: ReplyContent( - replyEvent, - ownMessage: ownMessage, - timeline: timeline, + child: IgnorePointer( + child: Material( + color: noBubble ? Colors.transparent : color, + borderRadius: borderRadius, + clipBehavior: Clip.antiAlias, + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + ), + padding: noBubble || noPadding + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 1.5, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (event.relationshipType == + RelationshipTypes.reply) + FutureBuilder( + future: event.getReplyEvent(timeline), + builder: (BuildContext context, snapshot) { + final replyEvent = snapshot.hasData + ? snapshot.data! + : Event( + eventId: event.relationshipEventId!, + content: { + 'msgtype': 'm.text', + 'body': '...', + }, + senderId: event.senderId, + type: 'm.room.message', + room: event.room, + status: EventStatus.sent, + originServerTs: DateTime.now(), + ); + return InkWell( + onTap: () { + if (scrollToEventId != null) { + scrollToEventId!(replyEvent.eventId); + } + }, + child: AbsorbPointer( + child: Container( + margin: const EdgeInsets.symmetric( + vertical: 4.0, + ), + child: ReplyContent( + replyEvent, + ownMessage: ownMessage, + timeline: timeline, + ), ), ), - ), - ); - }, - ), - MessageContent( - displayEvent, - textColor: textColor, - onInfoTab: onInfoTab, - ), - if (event.hasAggregatedEvents( - timeline, - RelationshipTypes.edit, - )) - Padding( - padding: const EdgeInsets.only( - top: 4.0, + ); + }, ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.edit_outlined, - color: textColor.withAlpha(164), - size: 14, - ), - Text( - ' - ${displayEvent.originServerTs.localizedTimeShort(context)}', - style: TextStyle( + MessageContent( + displayEvent, + textColor: textColor, + onInfoTab: onInfoTab, + ), + if (event.hasAggregatedEvents( + timeline, + RelationshipTypes.edit, + )) + Padding( + padding: const EdgeInsets.only( + top: 4.0, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.edit_outlined, color: textColor.withAlpha(164), - fontSize: 12, + size: 14, ), - ), - ], + Text( + ' - ${displayEvent.originServerTs.localizedTimeShort(context)}', + style: TextStyle( + color: textColor.withAlpha(164), + fontSize: 12, + ), + ), + ], + ), ), - ), - ], + ], + ), ), ), ), @@ -392,11 +393,11 @@ class Message extends StatelessWidget { background: const Padding( padding: EdgeInsets.symmetric(horizontal: 12.0), child: Center( - child: Icon(Icons.reply_outlined), + child: Icon(Icons.check_outlined), ), ), direction: SwipeDirection.endToStart, - onSwipe: onSwipe, + onSwipe: (_) => onSelect?.call(event), child: Center( child: Container( color: selected From c1f0ac1c91a62ca06724aa025a107705cd44bc82 Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 6 Nov 2023 11:10:23 +0100 Subject: [PATCH 88/93] ci: Fix stale issue workflow --- .github/workflows/issue_pr_management.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/issue_pr_management.yaml b/.github/workflows/issue_pr_management.yaml index 9062b68f8..490c9a91c 100644 --- a/.github/workflows/issue_pr_management.yaml +++ b/.github/workflows/issue_pr_management.yaml @@ -22,6 +22,5 @@ jobs: days-before-pr-stale: 30 days-before-pr-close: 14 exempt-milestones: true - stale-issue-label: stale exempt-assignees: krille-chan repo-token: ${{ secrets.GITHUB_TOKEN }} From 1759043ee806e6def645c84b6ee1369b25f92568 Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 6 Nov 2023 14:24:57 +0100 Subject: [PATCH 89/93] chore: Follow up fix select text --- lib/pages/chat/events/message.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 05ea42870..f9f287ec6 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -115,7 +115,7 @@ class Message extends StatelessWidget { } final row = InkWell( - onTap: longPressSelect ? () => onSelect!(event) : null, + onTap: () => onSelect!(event), child: HoverBuilder( builder: (context, hovered) => Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -397,7 +397,7 @@ class Message extends StatelessWidget { ), ), direction: SwipeDirection.endToStart, - onSwipe: (_) => onSelect?.call(event), + onSwipe: (_) => onSwipe(), child: Center( child: Container( color: selected From 60bcc6b89fd665fcc99a0a070e2468f2f0a41a05 Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 6 Nov 2023 14:25:56 +0100 Subject: [PATCH 90/93] fix: Cannot pin messages of other users --- lib/pages/chat/chat.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 836b41fd1..3ba0c4ffe 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -865,8 +865,7 @@ class ChatController extends State { !selectedEvents.single.status.isSent) { return false; } - return currentRoomBundle - .any((cl) => selectedEvents.first.senderId == cl!.userID); + return true; } bool get canEditSelectedEvents { From 814899951211372a507f5fc532848942dd24e93a Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 6 Nov 2023 15:57:46 +0100 Subject: [PATCH 91/93] chore: Follow up selectable text --- lib/pages/chat/events/html_message.dart | 149 ++++++++++++------------ 1 file changed, 76 insertions(+), 73 deletions(-) diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index acf8f3f71..b050409b8 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -74,79 +74,82 @@ class HtmlMessage extends StatelessWidget { ); // there is no need to pre-validate the html, as we validate it while rendering - return Html( - data: linkifiedRenderHtml, - style: { - '*': Style( - color: textColor, - margin: Margins.all(0), - fontSize: FontSize(fontSize), - ), - 'a': Style(color: linkColor, textDecorationColor: linkColor), - 'h1': Style( - fontSize: FontSize(fontSize * 2), - lineHeight: LineHeight.number(1.5), - fontWeight: FontWeight.w600, - ), - 'h2': Style( - fontSize: FontSize(fontSize * 1.75), - lineHeight: LineHeight.number(1.5), - fontWeight: FontWeight.w500, - ), - 'h3': Style( - fontSize: FontSize(fontSize * 1.5), - lineHeight: LineHeight.number(1.5), - ), - 'h4': Style( - fontSize: FontSize(fontSize * 1.25), - lineHeight: LineHeight.number(1.5), - ), - 'h5': Style( - fontSize: FontSize(fontSize * 1.25), - lineHeight: LineHeight.number(1.5), - ), - 'h6': Style( - fontSize: FontSize(fontSize), - lineHeight: LineHeight.number(1.5), - ), - 'blockquote': blockquoteStyle, - 'tg-forward': blockquoteStyle, - 'hr': Style( - border: Border.all(color: textColor, width: 0.5), - ), - 'table': Style( - border: Border.all(color: textColor, width: 0.5), - ), - 'tr': Style( - border: Border.all(color: textColor, width: 0.5), - ), - 'td': Style( - border: Border.all(color: textColor, width: 0.5), - padding: HtmlPaddings.all(2), - ), - 'th': Style( - border: Border.all(color: textColor, width: 0.5), - ), - }, - extensions: [ - RoomPillExtension(context, room), - CodeExtension(fontSize: fontSize), - MatrixMathExtension( - style: TextStyle(fontSize: fontSize, color: textColor), - ), - const TableHtmlExtension(), - SpoilerExtension(textColor: textColor), - const ImageExtension(), - FontColorExtension(), - ], - onLinkTap: (url, _, __) => UrlLauncher(context, url).launchUrl(), - onlyRenderTheseTags: const { - ...allowedHtmlTags, - // Needed to make it work properly - 'body', - 'html', - }, - shrinkWrap: true, + return MouseRegion( + cursor: SystemMouseCursors.text, + child: Html( + data: linkifiedRenderHtml, + style: { + '*': Style( + color: textColor, + margin: Margins.all(0), + fontSize: FontSize(fontSize), + ), + 'a': Style(color: linkColor, textDecorationColor: linkColor), + 'h1': Style( + fontSize: FontSize(fontSize * 2), + lineHeight: LineHeight.number(1.5), + fontWeight: FontWeight.w600, + ), + 'h2': Style( + fontSize: FontSize(fontSize * 1.75), + lineHeight: LineHeight.number(1.5), + fontWeight: FontWeight.w500, + ), + 'h3': Style( + fontSize: FontSize(fontSize * 1.5), + lineHeight: LineHeight.number(1.5), + ), + 'h4': Style( + fontSize: FontSize(fontSize * 1.25), + lineHeight: LineHeight.number(1.5), + ), + 'h5': Style( + fontSize: FontSize(fontSize * 1.25), + lineHeight: LineHeight.number(1.5), + ), + 'h6': Style( + fontSize: FontSize(fontSize), + lineHeight: LineHeight.number(1.5), + ), + 'blockquote': blockquoteStyle, + 'tg-forward': blockquoteStyle, + 'hr': Style( + border: Border.all(color: textColor, width: 0.5), + ), + 'table': Style( + border: Border.all(color: textColor, width: 0.5), + ), + 'tr': Style( + border: Border.all(color: textColor, width: 0.5), + ), + 'td': Style( + border: Border.all(color: textColor, width: 0.5), + padding: HtmlPaddings.all(2), + ), + 'th': Style( + border: Border.all(color: textColor, width: 0.5), + ), + }, + extensions: [ + RoomPillExtension(context, room), + CodeExtension(fontSize: fontSize), + MatrixMathExtension( + style: TextStyle(fontSize: fontSize, color: textColor), + ), + const TableHtmlExtension(), + SpoilerExtension(textColor: textColor), + const ImageExtension(), + FontColorExtension(), + ], + onLinkTap: (url, _, __) => UrlLauncher(context, url).launchUrl(), + onlyRenderTheseTags: const { + ...allowedHtmlTags, + // Needed to make it work properly + 'body', + 'html', + }, + shrinkWrap: true, + ), ); } From c7bdd9269a5d28d017dab4a5286abbb9a5893603 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Mon, 6 Nov 2023 18:19:44 +0100 Subject: [PATCH 92/93] chore: Follow up select event --- lib/pages/chat/events/message.dart | 368 ++++++++++++++--------------- 1 file changed, 184 insertions(+), 184 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index f9f287ec6..1df69a539 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -114,191 +114,187 @@ class Message extends StatelessWidget { : Theme.of(context).colorScheme.primaryContainer; } - final row = InkWell( - onTap: () => onSelect!(event), - child: HoverBuilder( - builder: (context, hovered) => Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: rowMainAxisAlignment, - children: [ - if (hovered || selected) - SizedBox( - width: Avatar.defaultSize, - height: Avatar.defaultSize - 8, - child: Checkbox.adaptive( - value: selected, - onChanged: (_) => onSelect?.call(event), - ), - ) - else if (sameSender || ownMessage) - SizedBox( - width: Avatar.defaultSize, - child: Center( - child: SizedBox( - width: 16, - height: 16, - child: event.status == EventStatus.sending - ? const CircularProgressIndicator.adaptive( - strokeWidth: 2, - ) - : event.status == EventStatus.error - ? const Icon(Icons.error, color: Colors.red) - : null, - ), - ), - ) - else - FutureBuilder( - future: event.fetchSenderUser(), - builder: (context, snapshot) { - final user = - snapshot.data ?? event.senderFromMemoryOrFallback; - return Avatar( - mxContent: user.avatarUrl, - name: user.calcDisplayname(), - onTap: () => onAvatarTab!(event), - ); - }, + final row = HoverBuilder( + builder: (context, hovered) => Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: rowMainAxisAlignment, + children: [ + if (hovered || selected) + SizedBox( + width: Avatar.defaultSize, + height: Avatar.defaultSize - 8, + child: Checkbox.adaptive( + value: selected, + onChanged: (_) => onSelect?.call(event), ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - if (!sameSender) - Padding( - padding: const EdgeInsets.only(left: 8.0, bottom: 4), - child: ownMessage || event.room.isDirectChat - ? const SizedBox(height: 12) - : FutureBuilder( - future: event.fetchSenderUser(), - builder: (context, snapshot) { - final displayname = - snapshot.data?.calcDisplayname() ?? - event.senderFromMemoryOrFallback - .calcDisplayname(); - return Text( - displayname, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: (Theme.of(context).brightness == - Brightness.light - ? displayname.color - : displayname.lightColorText), - ), - ); - }, - ), - ), - Container( - alignment: alignment, - padding: const EdgeInsets.only(left: 8), - child: IgnorePointer( - child: Material( - color: noBubble ? Colors.transparent : color, - borderRadius: borderRadius, - clipBehavior: Clip.antiAlias, - child: Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - ), - padding: noBubble || noPadding - ? EdgeInsets.zero - : const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, + ) + else if (sameSender || ownMessage) + SizedBox( + width: Avatar.defaultSize, + child: Center( + child: SizedBox( + width: 16, + height: 16, + child: event.status == EventStatus.sending + ? const CircularProgressIndicator.adaptive( + strokeWidth: 2, + ) + : event.status == EventStatus.error + ? const Icon(Icons.error, color: Colors.red) + : null, + ), + ), + ) + else + FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + final user = snapshot.data ?? event.senderFromMemoryOrFallback; + return Avatar( + mxContent: user.avatarUrl, + name: user.calcDisplayname(), + onTap: () => onAvatarTab!(event), + ); + }, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (!sameSender) + Padding( + padding: const EdgeInsets.only(left: 8.0, bottom: 4), + child: ownMessage || event.room.isDirectChat + ? const SizedBox(height: 12) + : FutureBuilder( + future: event.fetchSenderUser(), + builder: (context, snapshot) { + final displayname = + snapshot.data?.calcDisplayname() ?? + event.senderFromMemoryOrFallback + .calcDisplayname(); + return Text( + displayname, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: (Theme.of(context).brightness == + Brightness.light + ? displayname.color + : displayname.lightColorText), ), - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 1.5, + ); + }, ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (event.relationshipType == - RelationshipTypes.reply) - FutureBuilder( - future: event.getReplyEvent(timeline), - builder: (BuildContext context, snapshot) { - final replyEvent = snapshot.hasData - ? snapshot.data! - : Event( - eventId: event.relationshipEventId!, - content: { - 'msgtype': 'm.text', - 'body': '...', - }, - senderId: event.senderId, - type: 'm.room.message', - room: event.room, - status: EventStatus.sent, - originServerTs: DateTime.now(), - ); - return InkWell( - onTap: () { - if (scrollToEventId != null) { - scrollToEventId!(replyEvent.eventId); - } - }, - child: AbsorbPointer( - child: Container( - margin: const EdgeInsets.symmetric( - vertical: 4.0, - ), - child: ReplyContent( - replyEvent, - ownMessage: ownMessage, - timeline: timeline, - ), - ), - ), - ); - }, - ), - MessageContent( - displayEvent, - textColor: textColor, - onInfoTab: onInfoTab, + ), + Container( + alignment: alignment, + padding: const EdgeInsets.only(left: 8), + child: IgnorePointer( + child: Material( + color: noBubble ? Colors.transparent : color, + borderRadius: borderRadius, + clipBehavior: Clip.antiAlias, + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + ), + padding: noBubble || noPadding + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, ), - if (event.hasAggregatedEvents( - timeline, - RelationshipTypes.edit, - )) - Padding( - padding: const EdgeInsets.only( - top: 4.0, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.edit_outlined, - color: textColor.withAlpha(164), - size: 14, - ), - Text( - ' - ${displayEvent.originServerTs.localizedTimeShort(context)}', - style: TextStyle( - color: textColor.withAlpha(164), - fontSize: 12, + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 1.5, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (event.relationshipType == + RelationshipTypes.reply) + FutureBuilder( + future: event.getReplyEvent(timeline), + builder: (BuildContext context, snapshot) { + final replyEvent = snapshot.hasData + ? snapshot.data! + : Event( + eventId: event.relationshipEventId!, + content: { + 'msgtype': 'm.text', + 'body': '...', + }, + senderId: event.senderId, + type: 'm.room.message', + room: event.room, + status: EventStatus.sent, + originServerTs: DateTime.now(), + ); + return InkWell( + onTap: () { + if (scrollToEventId != null) { + scrollToEventId!(replyEvent.eventId); + } + }, + child: AbsorbPointer( + child: Container( + margin: const EdgeInsets.symmetric( + vertical: 4.0, + ), + child: ReplyContent( + replyEvent, + ownMessage: ownMessage, + timeline: timeline, ), ), - ], - ), + ), + ); + }, + ), + MessageContent( + displayEvent, + textColor: textColor, + onInfoTab: onInfoTab, + ), + if (event.hasAggregatedEvents( + timeline, + RelationshipTypes.edit, + )) + Padding( + padding: const EdgeInsets.only( + top: 4.0, ), - ], - ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.edit_outlined, + color: textColor.withAlpha(164), + size: 14, + ), + Text( + ' - ${displayEvent.originServerTs.localizedTimeShort(context)}', + style: TextStyle( + color: textColor.withAlpha(164), + fontSize: 12, + ), + ), + ], + ), + ), + ], ), ), ), ), - ], - ), + ), + ], ), - ], - ), + ), + ], ), ); Widget container; @@ -399,18 +395,22 @@ class Message extends StatelessWidget { direction: SwipeDirection.endToStart, onSwipe: (_) => onSwipe(), child: Center( - child: Container( - color: selected - ? Theme.of(context).primaryColor.withAlpha(100) - : Theme.of(context).primaryColor.withAlpha(0), - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 2.5, + child: InkWell( + onTap: longPressSelect ? () => onSelect!(event) : null, + onLongPress: () => onSelect!(event), + child: Container( + color: selected + ? Theme.of(context).primaryColor.withAlpha(100) + : Theme.of(context).primaryColor.withAlpha(0), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 2.5, + ), + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: container, ), - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - child: container, ), ), ); From 9d45f38400b84e02920b43ccf561a7e323f17a77 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Mon, 6 Nov 2023 20:01:52 +0100 Subject: [PATCH 93/93] chore: Follow up select text --- lib/pages/chat/events/message.dart | 181 ++++++++++++++--------------- 1 file changed, 89 insertions(+), 92 deletions(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 1df69a539..23f835255 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -191,102 +191,99 @@ class Message extends StatelessWidget { Container( alignment: alignment, padding: const EdgeInsets.only(left: 8), - child: IgnorePointer( - child: Material( - color: noBubble ? Colors.transparent : color, - borderRadius: borderRadius, - clipBehavior: Clip.antiAlias, - child: Container( - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - ), - padding: noBubble || noPadding - ? EdgeInsets.zero - : const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), - constraints: const BoxConstraints( - maxWidth: FluffyThemes.columnWidth * 1.5, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (event.relationshipType == - RelationshipTypes.reply) - FutureBuilder( - future: event.getReplyEvent(timeline), - builder: (BuildContext context, snapshot) { - final replyEvent = snapshot.hasData - ? snapshot.data! - : Event( - eventId: event.relationshipEventId!, - content: { - 'msgtype': 'm.text', - 'body': '...', - }, - senderId: event.senderId, - type: 'm.room.message', - room: event.room, - status: EventStatus.sent, - originServerTs: DateTime.now(), - ); - return InkWell( - onTap: () { - if (scrollToEventId != null) { - scrollToEventId!(replyEvent.eventId); - } - }, - child: AbsorbPointer( - child: Container( - margin: const EdgeInsets.symmetric( - vertical: 4.0, - ), - child: ReplyContent( - replyEvent, - ownMessage: ownMessage, - timeline: timeline, - ), - ), - ), - ); - }, - ), - MessageContent( - displayEvent, - textColor: textColor, - onInfoTab: onInfoTab, + child: Material( + color: noBubble ? Colors.transparent : color, + borderRadius: borderRadius, + clipBehavior: Clip.antiAlias, + child: Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + ), + padding: noBubble || noPadding + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, ), - if (event.hasAggregatedEvents( - timeline, - RelationshipTypes.edit, - )) - Padding( - padding: const EdgeInsets.only( - top: 4.0, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.edit_outlined, - color: textColor.withAlpha(164), - size: 14, - ), - Text( - ' - ${displayEvent.originServerTs.localizedTimeShort(context)}', - style: TextStyle( - color: textColor.withAlpha(164), - fontSize: 12, + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 1.5, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (event.relationshipType == RelationshipTypes.reply) + FutureBuilder( + future: event.getReplyEvent(timeline), + builder: (BuildContext context, snapshot) { + final replyEvent = snapshot.hasData + ? snapshot.data! + : Event( + eventId: event.relationshipEventId!, + content: { + 'msgtype': 'm.text', + 'body': '...', + }, + senderId: event.senderId, + type: 'm.room.message', + room: event.room, + status: EventStatus.sent, + originServerTs: DateTime.now(), + ); + return InkWell( + onTap: () { + if (scrollToEventId != null) { + scrollToEventId!(replyEvent.eventId); + } + }, + child: AbsorbPointer( + child: Container( + margin: const EdgeInsets.symmetric( + vertical: 4.0, + ), + child: ReplyContent( + replyEvent, + ownMessage: ownMessage, + timeline: timeline, ), ), - ], - ), + ), + ); + }, + ), + MessageContent( + displayEvent, + textColor: textColor, + onInfoTab: onInfoTab, + ), + if (event.hasAggregatedEvents( + timeline, + RelationshipTypes.edit, + )) + Padding( + padding: const EdgeInsets.only( + top: 4.0, ), - ], - ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.edit_outlined, + color: textColor.withAlpha(164), + size: 14, + ), + Text( + ' - ${displayEvent.originServerTs.localizedTimeShort(context)}', + style: TextStyle( + color: textColor.withAlpha(164), + fontSize: 12, + ), + ), + ], + ), + ), + ], ), ), ),