From 80bf7cfa932a43ffa9514f1d49f257a59418d357 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Thu, 16 Oct 2025 20:49:02 +0500 Subject: [PATCH 1/7] chore: remove unused goToRoom --- lib/utils/background_push.dart | 43 ---------------------------------- 1 file changed, 43 deletions(-) diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index e72e1e6..75f6d3a 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -363,49 +363,6 @@ class BackgroundPush { ); } - Future goToRoom(NotificationResponse? response) async { - try { - if (response?.payload == null) return; - final arr = response?.payload?.split(' '); - final roomId = arr?[0]; - final eventId = arr?[1]; - Logs().v('[Push] Attempting to go to room $roomId...'); - if (roomId == null || eventId == null) { - return; - } - await client.roomsLoading; - await client.accountDataLoading; - if (client.getRoomById(roomId) == null) { - await client - .waitForRoomInSync(roomId) - .timeout(const Duration(seconds: 30)); - } - if (response?.actionId == "read") { - if (AppConfig.sendPublicReadReceipts) { - await client.setReadMarker(roomId, mRead: eventId); - } else { - await client.setReadMarker(roomId, mReadPrivate: eventId); - } - return; - } else if (response?.actionId == "reply") { - final replyText = response?.input; - final room = client.getRoomById(roomId); - if (replyText != null && room != null) { - await room.sendTextEvent(replyText, - inReplyTo: await room.getEventById(eventId)); - } - return; - } - FluffyChatApp.router.go( - client.getRoomById(roomId)?.membership == Membership.invite - ? '/rooms' - : '/rooms/$roomId', - ); - } catch (e, s) { - Logs().e('[Push] Failed to open room', e, s); - } - } - Future setupUp() async { await UnifiedPushUi(matrix!.context, ["default"], UPFunctions()) .registerAppWithDialog(); From fc218e6d656a1cb47f4d0a6dbd230e3a8379201f Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Thu, 6 Nov 2025 11:36:59 +0500 Subject: [PATCH 2/7] steal some changes from fluffychat --- lib/pages/chat/events/poll_content.dart | 2 +- lib/pages/chat/recording_view_model.dart | 19 ++++++++++++------ .../matrix_sdk_extensions/matrix_locals.dart | 15 ++++++++++++++ pubspec.lock | 20 +++++++++---------- pubspec.yaml | 10 +++++----- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/lib/pages/chat/events/poll_content.dart b/lib/pages/chat/events/poll_content.dart index 33fb26b..876e7db 100644 --- a/lib/pages/chat/events/poll_content.dart +++ b/lib/pages/chat/events/poll_content.dart @@ -254,7 +254,7 @@ class PollWidgetState extends State { final event = widget.event; final content = event.content[PollEvents.PollStart] as Map; - final question = content?['question']?['m.text'] as String? ?? 'Poll'; + final question = content?['question']?['m.text'] as String? ?? content?['question']?['org.matrix.msc1767.text'] as String? ?? 'Poll'; final List answers = content?['answers'] ?? []; final maxSelections = content?['max_selections'] as int? ?? 1; final kind = content?['kind'] as String?; diff --git a/lib/pages/chat/recording_view_model.dart b/lib/pages/chat/recording_view_model.dart index 0054385..53c29e7 100644 --- a/lib/pages/chat/recording_view_model.dart +++ b/lib/pages/chat/recording_view_model.dart @@ -34,7 +34,6 @@ class RecordingViewModelState extends State { Timer? _recorderSubscription; Duration duration = Duration.zero; - bool error = false; bool isSending = false; bool get isRecording => _audioRecorder != null; @@ -88,7 +87,11 @@ class RecordingViewModelState extends State { final result = await audioRecorder.hasPermission(); if (result != true) { - setState(() => error = true); + showOkAlertDialog( + context: context, + title: L10n.of(context).oopsSomethingWentWrong, + message: L10n.of(context).noPermission, + ); return; } await WakelockPlus.enable(); @@ -107,9 +110,14 @@ class RecordingViewModelState extends State { ); setState(() => duration = Duration.zero); _subscribe(); - } catch (_) { - setState(() => error = true); - rethrow; + } catch (e, s) { + Logs().w('Unable to start voice message recording', e, s); + showOkAlertDialog( + context: context, + title: L10n.of(context).oopsSomethingWentWrong, + message: e.toString(), + ); + setState(_reset); } } @@ -139,7 +147,6 @@ class RecordingViewModelState extends State { _audioRecorder?.stop(); _audioRecorder = null; isSending = false; - error = false; fileName = null; duration = Duration.zero; amplitudeTimeline.clear(); diff --git a/lib/utils/matrix_sdk_extensions/matrix_locals.dart b/lib/utils/matrix_sdk_extensions/matrix_locals.dart index d57274d..f8c1839 100644 --- a/lib/utils/matrix_sdk_extensions/matrix_locals.dart +++ b/lib/utils/matrix_sdk_extensions/matrix_locals.dart @@ -7,6 +7,8 @@ class MatrixLocals extends MatrixLocalizations { MatrixLocals(this.l10n); + + @override String voiceMessage(String senderName, Duration? duration) { return l10n.voiceMessage; @@ -358,4 +360,17 @@ class MatrixLocals extends MatrixLocalizations { @override String get cancelledSend => l10n.sendCanceled; + + @override + // TODO: implement pollHasBeenEnded + String get pollHasBeenEnded => throw UnimplementedError(); + + @override + String startedAPoll(String senderName) { + // TODO: implement startedAPoll + throw UnimplementedError(); + } + + // This is currently not used, just to keep up with matrix-dart-sdk upstream. + // I will reimplement polls to match fluffychat's, I don't know when... } diff --git a/pubspec.lock b/pubspec.lock index 20451ab..85686cd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -253,10 +253,10 @@ packages: dependency: "direct main" description: name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239" url: "https://pub.dev" source: hosted - version: "0.3.4+2" + version: "0.3.5" crypto: dependency: transitive description: @@ -730,10 +730,10 @@ packages: dependency: "direct main" description: name: flutter_vodozemac - sha256: "54cd3790b6dfdc1afce928f8c46f7eeea9e4f8326f077400894935926f202057" + sha256: "16d4b44dd338689441fe42a80d0184e5c864e9563823de9e7e6371620d2c0590" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.4.1" flutter_web_auth_2: dependency: "direct main" description: @@ -1203,10 +1203,10 @@ packages: description: path: "." ref: main - resolved-ref: f678376e3a4825a6e1a00d89585d6bae0843950c + resolved-ref: "8272294736361eae8f3c81d220aaffcf30f38fb1" url: "https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git" source: git - version: "3.0.1" + version: "3.0.2" meta: dependency: transitive description: @@ -1563,10 +1563,10 @@ packages: dependency: "direct main" description: name: qr_code_scanner_plus - sha256: a0f1ac8e13299b3db2646635f252fe2ec67222b848b24ed34d11052faf080bfa + sha256: b764e5004251c58d9dee0c295e6006e05bd8d249e78ac3383abdb5afe0a996cd url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.0.14" qr_image: dependency: "direct main" description: @@ -2248,10 +2248,10 @@ packages: dependency: "direct main" description: name: vodozemac - sha256: "95cac62ffab94db99e134c8f9aac198f8131a4eed0bed76a6cfc9c72add229b9" + sha256: "39144e20740807731871c9248d811ed5a037b21d0aa9ffcfa630954de74139d9" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.4.0" wakelock_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3b37331..11a3e70 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: blurhash_dart: ^1.2.1 chewie: ^1.11.0 collection: ^1.18.0 - cross_file: ^0.3.4+2 + cross_file: ^0.3.5 cupertino_icons: any desktop_drop: ^0.4.4 desktop_notifications: ^0.6.3 @@ -47,7 +47,7 @@ dependencies: git: url: https://github.com/famedly/flutter_typeahead.git ref: main - flutter_vodozemac: ^0.3.0 + flutter_vodozemac: ^0.4.1 flutter_web_auth_2: ^3.1.1 # Version 4 blocked by https://github.com/MixinNetwork/flutter-plugins/issues/379 flutter_webrtc: ^0.12.9 geolocator: ^13.0.1 @@ -79,10 +79,10 @@ dependencies: pretty_qr_code: ^3.2.1 provider: ^6.0.2 punycode: ^1.0.0 - qr_code_scanner_plus: ^2.0.10+1 + qr_code_scanner_plus: ^2.0.14 qr_image: ^1.0.0 receive_sharing_intent: ^1.8.1 - record: ^6.1.1 + record: ^6.1.2 scroll_to_index: ^3.0.1 share_plus: ^10.0.2 shared_preferences: ^2.2.0 # Pinned because https://github.com/flutter/flutter/issues/118401 @@ -97,7 +97,7 @@ dependencies: url_launcher: ^6.2.5 video_compress: ^3.1.4 video_player: ^2.9.2 - vodozemac: ^0.3.0 + vodozemac: ^0.4.0 wakelock_plus: ^1.2.2 webrtc_interface: ^1.0.13 dio: ^5.9.0 From 9dbb75dada6a69e1240393a5ee7c3c2c94af41af Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Thu, 13 Nov 2025 20:19:51 +0500 Subject: [PATCH 3/7] Follow up notification action isolation handling fix: possible database corruption by allow multiple instances --- lib/config/app_config.dart | 3 + lib/main.dart | 14 ++ lib/utils/background_push.dart | 32 ++-- .../flutter_hive_collections_database.txt | 149 ------------------ .../builder.dart | 8 +- .../notification_background_handler.dart | 5 +- 6 files changed, 44 insertions(+), 167 deletions(-) delete mode 100644 lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.txt diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index c552835..631feef 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -3,6 +3,9 @@ import 'dart:ui'; import 'package:matrix/matrix.dart'; abstract class AppConfig { + static const String pushIsolatePortName = 'push_isolate'; + static const String mainIsolatePortName = 'main_isolate'; + static String _applicationName = 'Extera'; static String get applicationName => _applicationName; diff --git a/lib/main.dart b/lib/main.dart index 38151e7..e598192 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,6 @@ +import 'dart:isolate'; +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; @@ -15,9 +18,20 @@ import 'config/setting_keys.dart'; import 'utils/background_push.dart'; import 'widgets/fluffy_chat_app.dart'; +ReceivePort? mainIsolateReceivePort; + void main() async { Logs().i('Welcome to ${AppConfig.applicationName} <3'); + if (PlatformInfos.isAndroid) { + final port = mainIsolateReceivePort = ReceivePort(); + IsolateNameServer.removePortNameMapping('main_isolate'); + IsolateNameServer.registerPortWithName( + port.sendPort, + 'main_isolate', + ); + } + // Our background push shared isolate accesses flutter-internal things very early in the startup proccess // To make sure that the parts of flutter needed are started up already, we need to ensure that the // widget bindings are initialized already. diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 75f6d3a..a3c3daf 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -23,6 +23,7 @@ import 'dart:io'; import 'dart:isolate'; import 'dart:ui'; +import 'package:extera_next/main.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -75,6 +76,20 @@ class BackgroundPush { void _init() async { try { + mainIsolateReceivePort?.listen( + (message) async { + try { + await notificationTap( + NotificationResponseJson.fromJsonString(message), + client: client, + router: FluffyChatApp.router, + l10n: l10n, + ); + } catch (e, s) { + Logs().wtf('Main Notification Tap crashed', e, s); + } + }, + ); if (PlatformInfos.isAndroid) { final port = ReceivePort(); IsolateNameServer.removePortNameMapping('background_tab_port'); @@ -103,11 +118,10 @@ class BackgroundPush { iOS: DarwinInitializationSettings(), ), onDidReceiveNotificationResponse: (response) => notificationTap( - response, - client: client, - router: FluffyChatApp.router, - l10n: l10n - ), + response, + client: client, + router: FluffyChatApp.router, + l10n: l10n), onDidReceiveBackgroundNotificationResponse: notificationTapBackground, ); Logs().v('Flutter Local Notifications initialized'); @@ -312,12 +326,8 @@ class BackgroundPush { final response = details.notificationResponse; if (response != null) { - notificationTap( - response, - client: client, - router: FluffyChatApp.router, - l10n: l10n - ); + notificationTap(response, + client: client, router: FluffyChatApp.router, l10n: l10n); } }); } diff --git a/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.txt b/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.txt deleted file mode 100644 index 9ad5ce5..0000000 --- a/lib/utils/matrix_sdk_extensions/flutter_hive_collections_database.txt +++ /dev/null @@ -1,149 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/foundation.dart' hide Key; -import 'package:flutter/services.dart'; - -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:hive/hive.dart'; -import 'package:matrix/matrix.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:universal_html/html.dart' as html; - -// ignore: deprecated_member_use -class FlutterHiveCollectionsDatabase extends HiveCollectionsDatabase { - FlutterHiveCollectionsDatabase( - super.name, - String super.path, { - super.key, - }); - - static const String _cipherStorageKey = 'hive_encryption_key'; - - static Future databaseBuilder( - Client client, - ) async { - Logs().d('Open Hive...'); - HiveAesCipher? hiverCipher; - try { - // Workaround for secure storage is calling Platform.operatingSystem on web - if (kIsWeb) { - // ignore: unawaited_futures - html.window.navigator.storage?.persist(); - throw MissingPluginException(); - } - - const secureStorage = FlutterSecureStorage(); - final containsEncryptionKey = - await secureStorage.read(key: _cipherStorageKey) != null; - if (!containsEncryptionKey) { - // do not try to create a buggy secure storage for new Linux users - if (Platform.isLinux) throw MissingPluginException(); - final key = Hive.generateSecureKey(); - await secureStorage.write( - key: _cipherStorageKey, - value: base64UrlEncode(key), - ); - } - - // workaround for if we just wrote to the key and it still doesn't exist - final rawEncryptionKey = await secureStorage.read(key: _cipherStorageKey); - if (rawEncryptionKey == null) throw MissingPluginException(); - - hiverCipher = HiveAesCipher(base64Url.decode(rawEncryptionKey)); - } on MissingPluginException catch (_) { - const FlutterSecureStorage() - .delete(key: _cipherStorageKey) - .catchError((_) {}); - Logs().i('Hive encryption is not supported on this platform'); - } catch (e, s) { - const FlutterSecureStorage() - .delete(key: _cipherStorageKey) - .catchError((_) {}); - Logs().w('Unable to init Hive encryption', e, s); - } - - final db = FlutterHiveCollectionsDatabase( - 'hive_collections_${client.clientName.replaceAll(' ', '_').toLowerCase()}', - await findDatabasePath(client), - key: hiverCipher, - ); - try { - await db.open(); - } catch (e, s) { - Logs().w('Unable to open Hive. Delete database and storage key...', e, s); - const FlutterSecureStorage().delete(key: _cipherStorageKey); - await db.clear().catchError((_) {}); - await Hive.deleteFromDisk(); - rethrow; - } - Logs().d('Hive is ready'); - return db; - } - - static Future findDatabasePath(Client client) async { - var path = client.clientName; - if (!kIsWeb) { - Directory directory; - try { - if (Platform.isLinux) { - directory = await getApplicationSupportDirectory(); - } else { - directory = await getApplicationDocumentsDirectory(); - } - } catch (_) { - try { - directory = await getLibraryDirectory(); - } catch (_) { - directory = Directory.current; - } - } - // do not destroy your stable FluffyChat in debug mode - directory = Directory( - directory.uri.resolve(kDebugMode ? 'hive_debug' : 'hive').toFilePath(), - ); - directory.create(recursive: true); - path = directory.path; - } - return path; - } - - @override - int get maxFileSize => supportsFileStoring ? 100 * 1000 * 1000 : 0; - @override - bool get supportsFileStoring => !kIsWeb; - - Future _getFileStoreDirectory() async { - try { - try { - return (await getTemporaryDirectory()).path; - } catch (_) { - return (await getApplicationDocumentsDirectory()).path; - } - } catch (_) { - return (await getDownloadsDirectory())!.path; - } - } - - @override - Future getFile(Uri mxcUri) async { - if (!supportsFileStoring) return null; - final tempDirectory = await _getFileStoreDirectory(); - final file = - File('$tempDirectory/${Uri.encodeComponent(mxcUri.toString())}'); - if (await file.exists() == false) return null; - final bytes = await file.readAsBytes(); - return bytes; - } - - @override - Future storeFile(Uri mxcUri, Uint8List bytes, int time) async { - if (!supportsFileStoring) return null; - final tempDirectory = await _getFileStoreDirectory(); - final file = - File('$tempDirectory/${Uri.encodeComponent(mxcUri.toString())}'); - if (await file.exists()) return; - await file.writeAsBytes(bytes); - return; - } -} diff --git a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart index 351339a..6635d19 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart @@ -110,10 +110,10 @@ Future _constructDatabase(String clientName) async { final database = await factory.openDatabase( path, options: OpenDatabaseOptions( - version: 1, - // most important : apply encryption when opening the DB - onConfigure: helper?.applyPragmaKey, - singleInstance: false), + version: 1, + // most important : apply encryption when opening the DB + onConfigure: helper?.applyPragmaKey, + ), ); return await MatrixSdkDatabase.init( diff --git a/lib/utils/notification_background_handler.dart b/lib/utils/notification_background_handler.dart index bf46915..43ae6c1 100644 --- a/lib/utils/notification_background_handler.dart +++ b/lib/utils/notification_background_handler.dart @@ -46,11 +46,10 @@ extension NotificationResponseJson on NotificationResponse { void notificationTapBackground( NotificationResponse notificationResponse, ) async { - Logs().i('Notification tap in background'); - - final sendPort = IsolateNameServer.lookupPortByName('background_tab_port'); + final sendPort = IsolateNameServer.lookupPortByName(AppConfig.mainIsolatePortName); if (sendPort != null) { sendPort.send(notificationResponse.toJsonString()); + Logs().i('Notification tap sent to main isolate'); return; } From c372f7d79f3ba8bff296d6b0fb4b71932f67e5ae Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Fri, 14 Nov 2025 18:22:46 +0500 Subject: [PATCH 4/7] refactor: Improved UIA support for OIDC and SSO --- lib/utils/uia_request_manager.dart | 60 +++++++++++++++++++----------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/lib/utils/uia_request_manager.dart b/lib/utils/uia_request_manager.dart index eb57bc2..41ddcc1 100644 --- a/lib/utils/uia_request_manager.dart +++ b/lib/utils/uia_request_manager.dart @@ -1,9 +1,11 @@ import 'dart:async'; -import 'package:extera_next/generated/l10n/l10n.dart'; -import 'package:matrix/matrix.dart'; -import 'package:url_launcher/url_launcher_string.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import 'package:extera_next/generated/l10n/l10n.dart'; import 'package:extera_next/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:extera_next/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:extera_next/widgets/fluffy_chat_app.dart'; @@ -34,7 +36,7 @@ extension UiaRequestManager on MatrixState { minLines: 1, maxLines: 1, obscureText: true, - hintText: l10n.password, + hintText: L10n.of(context).password, )); if (input == null || input.isEmpty) { return uiaRequest.cancel(); @@ -80,24 +82,38 @@ extension UiaRequestManager on MatrixState { ), ); default: - final url = Uri.parse( - '${client.homeserver}/_matrix/client/r0/auth/$stage/fallback/web?session=${uiaRequest.session}', + final stageUrl = uiaRequest.params + .tryGetMap(stage) + ?.tryGet('url'); + final fallbackUrl = client.homeserver!.replace( + path: '/_matrix/client/v3/auth/$stage/fallback/web', + queryParameters: { + 'session': uiaRequest.session, + }, + ); + final url = stageUrl != null + ? (Uri.tryParse(stageUrl) ?? fallbackUrl) + : fallbackUrl; + + final consent = await showOkCancelAlertDialog( + useRootNavigator: false, + title: l10n.pleaseFollowInstructionsOnWeb, + context: navigatorContext, + okLabel: l10n.open, + cancelLabel: l10n.cancel, + ); + if (consent != OkCancelResult.ok) return uiaRequest.cancel(); + + launchUrl(url, mode: LaunchMode.inAppBrowserView); + final completer = Completer(); + final listener = + AppLifecycleListener(onResume: () => completer.complete()); + await completer.future; + listener.dispose(); + + return uiaRequest.completeStage( + AuthenticationData(session: uiaRequest.session), ); - launchUrlString(url.toString()); - if (OkCancelResult.ok == - await showOkCancelAlertDialog( - useRootNavigator: false, - title: l10n.pleaseFollowInstructionsOnWeb, - context: navigatorContext, - okLabel: l10n.next, - cancelLabel: l10n.cancel, - )) { - return uiaRequest.completeStage( - AuthenticationData(session: uiaRequest.session), - ); - } else { - return uiaRequest.cancel(); - } } } catch (e, s) { Logs().e('Error while background UIA', e, s); @@ -113,4 +129,4 @@ class UiaException implements Exception { @override String toString() => reason; -} +} \ No newline at end of file From 24d1f7f9b2951cd6bba9d9c588f6fba99bb2d050 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Fri, 14 Nov 2025 20:44:44 +0500 Subject: [PATCH 5/7] fix: import sticker packs --- .../settings_emotes/settings_emotes.dart | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/pages/settings_emotes/settings_emotes.dart b/lib/pages/settings_emotes/settings_emotes.dart index 1964a80..3462acf 100644 --- a/lib/pages/settings_emotes/settings_emotes.dart +++ b/lib/pages/settings_emotes/settings_emotes.dart @@ -276,26 +276,16 @@ class EmotesSettingsController extends State { } Future importEmojiZip() async { - final result = await showFutureLoadingDialog( - context: context, - future: () async { - final result = await selectFiles( - context, - type: FileSelectorType.zip, - ); - - if (result.isEmpty) return null; - - final buffer = await result.first.readAsBytes(); - - final archive = ZipDecoder().decodeBytes(buffer); - - return archive; - }, + final result = await selectFiles( + context, + type: FileSelectorType.zip, ); - final archive = result.result; - if (archive == null) return; + if (result.isEmpty) return; + + final buffer = InputMemoryStream(await result.single.readAsBytes()); + + final archive = ZipDecoder().decodeStream(buffer); await showDialog( context: context, @@ -338,8 +328,6 @@ class EmotesSettingsController extends State { '${pack.pack.displayName ?? client.userID?.localpart ?? 'emotes'}.zip'; final output = ZipEncoder().encode(archive); - if (output == null) return; - MatrixFile( name: fileName, bytes: Uint8List.fromList(output), From 272bf26d2857f5b54722bfe053dafc6d3493665e Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Fri, 14 Nov 2025 20:45:31 +0500 Subject: [PATCH 6/7] bump version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 11a3e70..fe59f82 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: extera_next description: Chat with your friends. publish_to: none # On version bump also increase the build number for F-Droid -version: 2.0.1 +version: 2.0.2 environment: sdk: ">=3.0.0 <4.0.0" From 7355a0bba828f322faae92503e4ad32b6d2c367c Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Fri, 14 Nov 2025 20:50:09 +0500 Subject: [PATCH 7/7] update unifiedpush --- lib/utils/background_push.dart | 17 +++- linux/flutter/generated_plugin_registrant.cc | 12 +++ linux/flutter/generated_plugins.cmake | 3 + macos/Flutter/GeneratedPluginRegistrant.swift | 6 ++ pubspec.lock | 96 +++++++++++++++++-- pubspec.yaml | 4 +- .../flutter/generated_plugin_registrant.cc | 9 ++ windows/flutter/generated_plugins.cmake | 3 + 8 files changed, 135 insertions(+), 15 deletions(-) diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index a3c3daf..21e135e 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -139,7 +139,7 @@ class BackgroundPush { if (Platform.isAndroid) { await UnifiedPush.initialize( onNewEndpoint: _newUpEndpoint, - onRegistrationFailed: _upUnregistered, + onRegistrationFailed: (_, i) => _upUnregistered(i), onUnregistered: _upUnregistered, onMessage: _onUpMessage, ); @@ -374,11 +374,17 @@ class BackgroundPush { } Future setupUp() async { - await UnifiedPushUi(matrix!.context, ["default"], UPFunctions()) - .registerAppWithDialog(); + await UnifiedPushUi( + context: matrix!.context, + instances: ["default"], + unifiedPushFunctions: UPFunctions(), + showNoDistribDialog: false, + onNoDistribDialogDismissed: () {}, // TODO: Implement me + ).registerAppWithDialog(); } - Future _newUpEndpoint(String newEndpoint, String i) async { + Future _newUpEndpoint(PushEndpoint newPushEndpoint, String i) async { + final newEndpoint = newPushEndpoint.url; upAction = true; if (newEndpoint.isEmpty) { await _upUnregistered(i); @@ -438,8 +444,9 @@ class BackgroundPush { } } - Future _onUpMessage(Uint8List message, String i) async { + Future _onUpMessage(PushMessage pushMessage, String i) async { upAction = true; + final message = pushMessage.content; final data = Map.from( json.decode(utf8.decode(message))['notification'], ); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 016e81f..ad8fc21 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -16,8 +16,11 @@ #include #include #include +#include #include #include +#include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -51,12 +54,21 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) record_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin"); record_linux_plugin_register_with_registrar(record_linux_registrar); + g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); + screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); g_autoptr(FlPluginRegistrar) sqlcipher_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); sqlite3_flutter_libs_plugin_register_with_registrar(sqlcipher_flutter_libs_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) webcrypto_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WebcryptoPlugin"); + webcrypto_plugin_register_with_registrar(webcrypto_registrar); + g_autoptr(FlPluginRegistrar) window_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); + window_manager_plugin_register_with_registrar(window_manager_registrar); g_autoptr(FlPluginRegistrar) window_to_front_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin"); window_to_front_plugin_register_with_registrar(window_to_front_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index a16d096..2c97996 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -13,8 +13,11 @@ list(APPEND FLUTTER_PLUGIN_LIST handy_window pasteboard record_linux + screen_retriever_linux sqlcipher_flutter_libs url_launcher_linux + webcrypto + window_manager window_to_front ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 016f51c..064c22b 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -24,6 +24,7 @@ import package_info_plus import pasteboard import path_provider_foundation import record_macos +import screen_retriever_macos import share_plus import shared_preferences_foundation import sqflite_darwin @@ -32,6 +33,8 @@ import url_launcher_macos import video_compress import video_player_avfoundation import wakelock_plus +import webcrypto +import window_manager import window_to_front func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -54,6 +57,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) + ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) @@ -62,5 +66,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) + WebcryptoPlugin.register(with: registry.registrar(forPlugin: "WebcryptoPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 85686cd..2f7350e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1679,6 +1679,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" scroll_to_index: dependency: "direct main" description: @@ -2056,34 +2096,50 @@ packages: dependency: "direct main" description: name: unifiedpush - sha256: "6dbed5a6305ca33f1865c7a3d814ae39476b79a2d23ca76a5708f023f405730f" + sha256: "8ed9767f750a1dc6159a77e2171641d0cb825dc87682d1ce1b8618689b79f58e" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.2.0" unifiedpush_android: dependency: transitive description: name: unifiedpush_android - sha256: "7443dece0a850ae956514f809983eb2b39fc518c2c7d24dbfe817198bec89134" + sha256: "556796c81e8151ee8e4275baea2f7191119e8b1412ec35523cc2ac1c44c348bf" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "3.4.0" + unifiedpush_linux: + dependency: transitive + description: + name: unifiedpush_linux + sha256: c062d5eedd1cec70bcd33270cc4e01ae0ff6501f33d471167c06b34a968adfeb + url: "https://pub.dev" + source: hosted + version: "1.0.0" unifiedpush_platform_interface: dependency: transitive description: name: unifiedpush_platform_interface - sha256: dd588d78a8b2bfc10430e30035526e98caa543d0b7364a6344b5eb4815721c6d + sha256: "83372bc8d794b8b12ef6993b518d7be907dcfc2191bdf6de0ece5c4445d89880" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "4.0.0" + unifiedpush_storage_interface: + dependency: transitive + description: + name: unifiedpush_storage_interface + sha256: b8d423a4695efc616aa21d8ab48fb5ef99d6288c68b56282b8faac1579ceabd9 + url: "https://pub.dev" + source: hosted + version: "1.0.0" unifiedpush_ui: dependency: "direct main" description: name: unifiedpush_ui - sha256: cf86f0214f37debd41f25c0425c8489df85e27f9f8784fed571eb7a86d39ba11 + sha256: "1b36b2aa0bc6b61577e2661c1183bd3442969ecf77b4c78174796d324f66dd1d" url: "https://pub.dev" source: hosted - version: "0.1.0" + version: "0.2.0" universal_html: dependency: "direct main" description: @@ -2300,6 +2356,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + webcrypto: + dependency: transitive + description: + name: webcrypto + sha256: e393b3d0b01694a8f81efecf278ed7392877130e6e7b29f578863e4f2d0b2ebd + url: "https://pub.dev" + source: hosted + version: "0.5.8" webdriver: dependency: transitive description: @@ -2316,6 +2380,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + webpush_encryption: + dependency: transitive + description: + name: webpush_encryption + sha256: "63046b7d6909f4a72ce3c153fa574726e257aaf21b1995ba063dc241a1b1520b" + url: "https://pub.dev" + source: hosted + version: "1.0.0" webrtc_interface: dependency: "direct main" description: @@ -2340,6 +2412,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.5" + window_manager: + dependency: transitive + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" window_to_front: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fe59f82..b3cac69 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -91,8 +91,8 @@ dependencies: sqlcipher_flutter_libs: ^0.6.1 swipe_to_action: ^0.3.0 tor_detector_web: ^1.1.0 - unifiedpush: ^5.0.1 - unifiedpush_ui: ^0.1.0 + unifiedpush: ^6.2.0 + unifiedpush_ui: ^0.2.0 universal_html: ^2.2.4 url_launcher: ^6.2.5 video_compress: ^3.1.4 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 7ab3698..6e7bfab 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -17,9 +17,12 @@ #include #include #include +#include #include #include #include +#include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -45,12 +48,18 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); RecordWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("RecordWindowsPluginCApi")); + ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); Sqlite3FlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WebcryptoPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WebcryptoPlugin")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); WindowToFrontPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowToFrontPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 7e3e64d..3ea4b7d 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -14,9 +14,12 @@ list(APPEND FLUTTER_PLUGIN_LIST pasteboard permission_handler_windows record_windows + screen_retriever_windows share_plus sqlcipher_flutter_libs url_launcher_windows + webcrypto + window_manager window_to_front )