From 5d590c2c757b6522b4f929a29f65a808b03a0ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 13:18:38 +0100 Subject: [PATCH 1/8] build: Update to flutter 3.38.3 --- .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 71fae1563..48b9dbee1 100644 --- a/.github/workflows/versions.env +++ b/.github/workflows/versions.env @@ -1,2 +1,2 @@ -FLUTTER_VERSION=3.38.1 +FLUTTER_VERSION=3.38.3 JAVA_VERSION=17 From 5165b1b596dd559d187e2bbc8675804e08453806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 13:31:51 +0100 Subject: [PATCH 2/8] fix: Do not auto load history in rooms with collapsed state only --- lib/pages/chat/chat_event_list.dart | 34 +++++++++++++++++++---------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 0059026f5..b3bbbbb32 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -1,3 +1,6 @@ +import 'dart:math'; + +import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -92,21 +95,28 @@ class ChatEventList extends StatelessWidget { // Request history button or progress indicator: if (i == events.length + 1) { - if (timeline.isRequestingHistory) { - return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ); - } - if (timeline.canRequestHistory && - controller.activeThreadId == null) { + if (controller.activeThreadId == null) { return Builder( builder: (context) { - WidgetsBinding.instance - .addPostFrameCallback(controller.requestHistory); + final visibleIndex = timeline.events.lastIndexWhere( + (event) => + !event.isCollapsedState && event.isVisibleInGui, + ); + if (visibleIndex > timeline.events.length - 50) { + WidgetsBinding.instance + .addPostFrameCallback(controller.requestHistory); + } return Center( - child: IconButton( - onPressed: controller.requestHistory, - icon: const Icon(Icons.refresh_outlined), + child: AnimatedSwitcher( + duration: FluffyThemes.animationDuration, + child: timeline.canRequestHistory + ? IconButton( + onPressed: controller.requestHistory, + icon: const Icon(Icons.refresh_outlined), + ) + : const CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), ), ); }, From 70e8b5333386e62a963cfa867c68764c784696fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 13:31:58 +0100 Subject: [PATCH 3/8] build: Update build lock files --- lib/pages/chat/chat_event_list.dart | 4 +--- macos/Podfile.lock | 2 +- pubspec.lock | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index b3bbbbb32..1ed35f3be 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -1,9 +1,7 @@ -import 'dart:math'; - -import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:collection/collection.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:fluffychat/config/themes.dart'; diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 5e31606a6..3e928cef2 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -191,7 +191,7 @@ SPEC CHECKSUMS: dynamic_color: cb7c2a300ee67ed3bd96c3e852df3af0300bf610 emoji_picker_flutter: 51ca408e289d84d1e460016b2a28721ec754fcf7 file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a - file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31 + file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7 flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0 flutter_new_badger: 6fe9bf7e42793a164032c21f164c0ad9985cd0f2 flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54 diff --git a/pubspec.lock b/pubspec.lock index 5d7cb0ea9..19d6e5fc4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2258,5 +2258,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.32.0" + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.35.0" From 621d52e993abc419a244fecb053b13c6def19d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 13:36:26 +0100 Subject: [PATCH 4/8] chore: ignore for now deprecated regex --- analysis_options.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/analysis_options.yaml b/analysis_options.yaml index d74b36355..a18542ab5 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -15,6 +15,7 @@ analyzer: errors: todo: ignore use_build_context_synchronously: ignore + deprecated_member_use: ignore exclude: - lib/generated_plugin_registrant.dart - lib/l10n/*.dart From f279b40c6733cd6062f7f3e8037226da64e5098b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 13:42:00 +0100 Subject: [PATCH 5/8] chore: Make stickers smaller than normal image messages --- lib/pages/chat/events/message_content.dart | 3 ++- lib/pages/chat/sticker_picker_dialog.dart | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 46beaa441..12ef8f112 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -118,7 +118,8 @@ class MessageContent extends StatelessWidget { case MessageTypes.Image: case MessageTypes.Sticker: if (event.redacted) continue textmessage; - const maxSize = 256.0; + final maxSize = + event.messageType == MessageTypes.Sticker ? 128.0 : 256.0; final w = event.content .tryGetMap('info') ?.tryGet('w'); diff --git a/lib/pages/chat/sticker_picker_dialog.dart b/lib/pages/chat/sticker_picker_dialog.dart index 090be367b..6ca1ac08c 100644 --- a/lib/pages/chat/sticker_picker_dialog.dart +++ b/lib/pages/chat/sticker_picker_dialog.dart @@ -67,7 +67,9 @@ class StickerPickerDialogState extends State { GridView.builder( itemCount: imageKeys.length, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 128, + maxCrossAxisExtent: 84, + mainAxisSpacing: 8.0, + crossAxisSpacing: 8.0, ), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), From 34a58c5962816058cf0e9cd53ce8b7dd549616fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 13:51:13 +0100 Subject: [PATCH 6/8] chore: Link how do I get stickers FAQ entry --- lib/config/app_config.dart | 2 ++ lib/pages/chat/sticker_picker_dialog.dart | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 709ecc946..c5a332a0e 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -27,6 +27,8 @@ abstract class AppConfig { 'https://fluffy.chat/faq/#how_to_use_end_to_end_encryption'; static const String startChatTutorial = 'https://fluffy.chat/faq/#how_do_i_find_other_users'; + static const String howDoIGetStickersTutorial = + 'https://fluffy.chat/faq/#how_do_i_get_stickers'; static const String appId = 'im.fluffychat.FluffyChat'; static const String appOpenUrlScheme = 'im.fluffychat'; diff --git a/lib/pages/chat/sticker_picker_dialog.dart b/lib/pages/chat/sticker_picker_dialog.dart index 6ca1ac08c..088ed58ea 100644 --- a/lib/pages/chat/sticker_picker_dialog.dart +++ b/lib/pages/chat/sticker_picker_dialog.dart @@ -144,7 +144,7 @@ class StickerPickerDialogState extends State { OutlinedButton.icon( onPressed: () => UrlLauncher( context, - 'https://matrix.to/#/#fluffychat-stickers:janian.de', + AppConfig.howDoIGetStickersTutorial, ).launchUrl(), icon: const Icon(Icons.explore_outlined), label: Text(L10n.of(context).discover), From 31a204f1ea6c3a8b2bbe33fffd31c88048468e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 14:58:07 +0100 Subject: [PATCH 7/8] refactor: Always open Chat Backup as page right after login --- lib/config/routes.dart | 12 ++ lib/l10n/intl_en.arb | 8 +- lib/pages/bootstrap/bootstrap_dialog.dart | 111 +++++++++++------- lib/pages/chat/events/message_content.dart | 6 +- lib/pages/chat_list/chat_list.dart | 10 -- lib/pages/settings/settings.dart | 6 +- .../settings_security/settings_security.dart | 7 -- lib/widgets/matrix.dart | 4 +- 8 files changed, 95 insertions(+), 69 deletions(-) diff --git a/lib/config/routes.dart b/lib/config/routes.dart index a598ffd70..f33d9044b 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -7,6 +7,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/archive/archive.dart'; +import 'package:fluffychat/pages/bootstrap/bootstrap_dialog.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; @@ -101,6 +102,17 @@ abstract class AppRoutes { const ConfigViewer(), ), ), + GoRoute( + path: '/backup', + redirect: loggedOutRedirect, + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + BootstrapDialog( + wipe: state.uri.queryParameters['wipe'] == 'true', + ), + ), + ), ShellRoute( // Never use a transition on the shell route. Changing the PageBuilder // here based on a MediaQuery causes the child to briefly be rendered diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 99e856036..b09d93862 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -502,7 +502,7 @@ "type": "String", "placeholders": {} }, - "chatBackupDescription": "Your old messages are secured with a recovery key. Please make sure you don't lose it.", + "chatBackupDescription": "Your messages are secured with a recovery key. Please make sure you don't lose it.", "@chatBackupDescription": { "type": "String", "placeholders": {} @@ -3460,5 +3460,9 @@ "stickerPackNameAlreadyExists": "Sticker pack name already exists", "newStickerPack": "New sticker pack", "stickerPackName": "Sticker pack name", - "attribution": "Attribution" + "attribution": "Attribution", + "skipChatBackup": "Skip chat backup", + "skipChatBackupWarning": "Are you sure? Without enabling the chat backup you may lose access to your messages if you switch your device.", + "loadingMessages": "Loading messages", + "setupChatBackup": "Set up chat backup" } diff --git a/lib/pages/bootstrap/bootstrap_dialog.dart b/lib/pages/bootstrap/bootstrap_dialog.dart index a303c3530..3d8a2c2ba 100644 --- a/lib/pages/bootstrap/bootstrap_dialog.dart +++ b/lib/pages/bootstrap/bootstrap_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:go_router/go_router.dart'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; @@ -10,26 +11,21 @@ import 'package:fluffychat/utils/error_reporter.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/sync_status_localization.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; -import '../../utils/adaptive_bottom_sheet.dart'; +import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import '../key_verification/key_verification_dialog.dart'; class BootstrapDialog extends StatefulWidget { final bool wipe; - final Client client; const BootstrapDialog({ super.key, this.wipe = false, - required this.client, }); - Future show(BuildContext context) => showAdaptiveBottomSheet( - context: context, - builder: (context) => this, - ); - @override BootstrapDialogState createState() => BootstrapDialogState(); } @@ -38,7 +34,7 @@ class BootstrapDialogState extends State { final TextEditingController _recoveryKeyTextEditingController = TextEditingController(); - late Bootstrap bootstrap; + Bootstrap? bootstrap; String? _recoveryKeyInputError; @@ -54,7 +50,7 @@ class BootstrapDialogState extends State { bool? _wipe; String get _secureStorageKey => - 'ssss_recovery_key_${bootstrap.client.userID}'; + 'ssss_recovery_key_${bootstrap!.client.userID}'; bool get _supportsSecureStorage => PlatformInfos.isMobile || PlatformInfos.isDesktop; @@ -69,18 +65,42 @@ class BootstrapDialogState extends State { return L10n.of(context).storeSecurlyOnThisDevice; } + late final Client client; + @override void initState() { - _createBootstrap(widget.wipe); super.initState(); + client = Matrix.of(context).client; + _createBootstrap(widget.wipe); } + void _cancelAction() async { + final consent = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).skipChatBackup, + message: L10n.of(context).skipChatBackupWarning, + okLabel: L10n.of(context).skip, + isDestructive: true, + ); + if (consent != OkCancelResult.ok) return; + if (!mounted) return; + _goBackAction(false); + } + + void _goBackAction(bool success) => + context.canPop() ? context.pop(success) : context.go('/rooms'); + void _createBootstrap(bool wipe) async { + await client.roomsLoading; + await client.accountDataLoading; + await client.userDeviceKeysLoading; + while (client.prevBatch == null) { + await client.onSync.stream.first; + } _wipe = wipe; titleText = null; _recoveryKeyStored = false; - bootstrap = - widget.client.encryption!.bootstrap(onUpdate: (_) => setState(() {})); + bootstrap = client.encryption!.bootstrap(onUpdate: (_) => setState(() {})); final key = await const FlutterSecureStorage().read(key: _secureStorageKey); if (key == null) return; _recoveryKeyTextEditingController.text = key; @@ -89,22 +109,45 @@ class BootstrapDialogState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final bootstrap = this.bootstrap; + if (bootstrap == null) { + return LoginScaffold( + appBar: AppBar( + centerTitle: true, + leading: CloseButton(onPressed: _cancelAction), + title: Text(L10n.of(context).loadingMessages), + ), + body: Center( + child: StreamBuilder( + stream: client.onSyncStatus.stream, + builder: (context, snapshot) { + final status = snapshot.data; + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator.adaptive(value: status?.progress), + if (status != null) Text(status.calcLocalizedString(context)), + ], + ); + }, + ), + ), + ); + } + _wipe ??= widget.wipe; final buttons = []; - Widget body = const CircularProgressIndicator.adaptive(); + Widget body = const Center(child: CircularProgressIndicator.adaptive()); titleText = L10n.of(context).loadingPleaseWait; if (bootstrap.newSsssKey?.recoveryKey != null && _recoveryKeyStored == false) { final key = bootstrap.newSsssKey!.recoveryKey; titleText = L10n.of(context).recoveryKey; - return Scaffold( + return LoginScaffold( appBar: AppBar( centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ), + leading: CloseButton(onPressed: _cancelAction), title: Text(L10n.of(context).recoveryKey), ), body: Center( @@ -220,14 +263,11 @@ class BootstrapDialogState extends State { break; case BootstrapState.openExistingSsss: _recoveryKeyStored = true; - return Scaffold( + return LoginScaffold( appBar: AppBar( centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.close), - onPressed: Navigator.of(context).pop, - ), - title: Text(L10n.of(context).chatBackup), + leading: CloseButton(onPressed: _cancelAction), + title: Text(L10n.of(context).setupChatBackup), ), body: Center( child: ConstrainedBox( @@ -373,16 +413,14 @@ class BootstrapDialogState extends State { context: context, delay: false, future: () async { - await widget.client.updateUserDeviceKeys(); - return widget.client - .userDeviceKeys[widget.client.userID!]! + await client.updateUserDeviceKeys(); + return client.userDeviceKeys[client.userID!]! .startVerification(); }, ); if (req.error != null) return; await KeyVerificationDialog(request: req.result!) .show(context); - Navigator.of(context, rootNavigator: false).pop(); }, ), const SizedBox(height: 16), @@ -446,8 +484,7 @@ class BootstrapDialogState extends State { body = const Icon(Icons.error_outline, color: Colors.red, size: 80); buttons.add( ElevatedButton( - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(false), + onPressed: () => _goBackAction(false), child: Text(L10n.of(context).close), ), ); @@ -472,8 +509,7 @@ class BootstrapDialogState extends State { ); buttons.add( ElevatedButton( - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(false), + onPressed: () => _goBackAction(false), child: Text(L10n.of(context).close), ), ); @@ -481,14 +517,9 @@ class BootstrapDialogState extends State { } } - return Scaffold( + return LoginScaffold( appBar: AppBar( - leading: Center( - child: CloseButton( - onPressed: () => - Navigator.of(context, rootNavigator: false).pop(true), - ), - ), + leading: CloseButton(onPressed: _cancelAction), title: Text(titleText ?? L10n.of(context).loadingPleaseWait), ), body: Center( diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 12ef8f112..833291068 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/setting_keys.dart'; @@ -17,7 +18,6 @@ import '../../../config/app_config.dart'; import '../../../utils/event_checkbox_extension.dart'; import '../../../utils/platform_infos.dart'; import '../../../utils/url_launcher.dart'; -import '../../bootstrap/bootstrap_dialog.dart'; import 'audio_player.dart'; import 'cute_events.dart'; import 'html_message.dart'; @@ -59,9 +59,7 @@ class MessageContent extends StatelessWidget { } final client = Matrix.of(context).client; if (client.isUnknownSession && client.encryption!.crossSigning.enabled) { - final success = await BootstrapDialog( - client: Matrix.of(context).client, - ).show(context); + final success = await context.push('/backup'); if (success != true) return; } event.requestKey(); diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 4f96aff6d..cd1d542a4 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -28,7 +28,6 @@ import '../../../utils/account_bundles.dart'; import '../../config/setting_keys.dart'; import '../../utils/url_launcher.dart'; import '../../widgets/matrix.dart'; -import '../bootstrap/bootstrap_dialog.dart'; enum PopupMenuAction { settings, @@ -754,15 +753,6 @@ class ChatListController extends State setState(() { waitForFirstSync = true; }); - - // Display first login bootstrap if enabled - if (client.encryption?.keyManager.enabled == true) { - if (await client.encryption?.keyManager.isCached() == false || - await client.encryption?.crossSigning.isCached() == false || - client.isUnknownSession && !mounted) { - await BootstrapDialog(client: client).show(context); - } - } } if (!mounted) return; setState(() { diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index bb8fa9a10..c386b18d3 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:matrix/matrix.dart'; @@ -14,7 +15,6 @@ import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog. import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../../widgets/matrix.dart'; -import '../bootstrap/bootstrap_dialog.dart'; import 'settings_view.dart'; class Settings extends StatefulWidget { @@ -197,9 +197,7 @@ class SettingsController extends State { ); return; } - await BootstrapDialog( - client: Matrix.of(context).client, - ).show(context); + await context.push('/backup'); checkBootstrap(); } diff --git a/lib/pages/settings_security/settings_security.dart b/lib/pages/settings_security/settings_security.dart index 2ec3747c0..db12783f9 100644 --- a/lib/pages/settings_security/settings_security.dart +++ b/lib/pages/settings_security/settings_security.dart @@ -9,7 +9,6 @@ import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart' import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import '../bootstrap/bootstrap_dialog.dart'; import 'settings_security_view.dart'; class SettingsSecurity extends StatefulWidget { @@ -101,12 +100,6 @@ class SettingsSecurityController extends State { ); } - void showBootstrapDialog(BuildContext context) async { - await BootstrapDialog( - client: Matrix.of(context).client, - ).show(context); - } - Future dehydrateAction() => Matrix.of(context).dehydrateAction(context); void changeShareKeysWith(ShareKeysWith? shareKeysWith) async { diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 55fd267ad..63b7ee117 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -170,7 +170,7 @@ class MatrixState extends State with WidgetsBindingObserver { ); _registerSubs(_loginClientCandidate!.clientName); _loginClientCandidate = null; - FluffyChatApp.router.go('/rooms'); + FluffyChatApp.router.go('/backup'); }); if (widget.clients.isEmpty) widget.clients.add(candidate); return candidate; @@ -283,7 +283,7 @@ class MatrixState extends State with WidgetsBindingObserver { } } else { FluffyChatApp.router - .go(state == LoginState.loggedIn ? '/rooms' : '/home'); + .go(state == LoginState.loggedIn ? '/backup' : '/home'); } }); onUiaRequest[name] ??= c.onUiaRequest.stream.listen(uiaRequestHandler); From 22eb82f7d2b0e0b495bb1902806261aac7e1dc1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ku=C3=9Fowski?= Date: Sat, 22 Nov 2025 15:04:58 +0100 Subject: [PATCH 8/8] fix: Do not display empty file description body --- lib/utils/file_description.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/utils/file_description.dart b/lib/utils/file_description.dart index 639d11632..54f72e159 100644 --- a/lib/utils/file_description.dart +++ b/lib/utils/file_description.dart @@ -12,11 +12,16 @@ extension FileDescriptionExtension on Event { return null; } final formattedBody = content.tryGet('formatted_body'); - if (formattedBody != null) return formattedBody; + if (formattedBody != null && formattedBody.isNotEmpty) return formattedBody; final filename = content.tryGet('filename'); final body = content.tryGet('body'); - if (filename != body && body != null && filename != null) return body; + if (filename != body && + body != null && + filename != null && + body.isNotEmpty) { + return body; + } return null; } }