From 08cf4913ebc26442657d2f558b15811242cbb666 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Tue, 21 Oct 2025 21:45:05 +0500 Subject: [PATCH 01/22] adapt to current matrix-dart-sdk changes use proxy by default (gonna remove later) --- lib/pages/chat/chat.dart | 2 +- lib/utils/client_manager.dart | 2 +- lib/utils/custom_http_client.dart | 32 ++++++++++++++++++++----------- pubspec.lock | 10 ++++------ pubspec.yaml | 7 ++++--- 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 84d63ff..d978346 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -105,7 +105,7 @@ class ChatController extends State late Client sendingClient; - Timeline? timeline; + RoomTimeline? timeline; late final String readMarkerEventId; diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index f8741f0..8e3afb2 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -115,7 +115,7 @@ abstract class ClientManager { return Client( clientName, httpClient: - PlatformInfos.isAndroid ? CustomHttpClient.createHTTPClient() : null, + CustomHttpClient.createHTTPClient(), verificationMethods: { KeyVerificationMethod.numbers, if (kIsWeb || PlatformInfos.isMobile || PlatformInfos.isLinux) diff --git a/lib/utils/custom_http_client.dart b/lib/utils/custom_http_client.dart index c310fdd..96b17fc 100644 --- a/lib/utils/custom_http_client.dart +++ b/lib/utils/custom_http_client.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:extera_next/utils/platform_infos.dart'; import 'package:http/http.dart' as http; import 'package:http/io_client.dart'; @@ -10,20 +11,29 @@ class CustomHttpClient { static HttpClient customHttpClient(String? cert) { final context = SecurityContext.defaultContext; - try { - if (cert != null) { - final bytes = utf8.encode(cert); - context.setTrustedCertificatesBytes(bytes); - } - } on TlsException catch (e) { - if (e.osError != null && - e.osError!.message.contains('CERT_ALREADY_IN_HASH_TABLE')) { - } else { - rethrow; + if (PlatformInfos.isAndroid) { + try { + if (cert != null) { + final bytes = utf8.encode(cert); + context.setTrustedCertificatesBytes(bytes); + } + } on TlsException catch (e) { + if (e.osError != null && + e.osError!.message.contains('CERT_ALREADY_IN_HASH_TABLE')) { + } else { + rethrow; + } } } - return HttpClient(context: context); + // Use Nekoray mixed proxy + // Made it for myself, remove later + final httpClient = HttpClient(context: context); + httpClient.findProxy = (uri) { + return 'PROXY localhost:2080;'; + }; + + return httpClient; } static http.Client createHTTPClient() => IOClient(customHttpClient(ISRG_X1)); diff --git a/pubspec.lock b/pubspec.lock index feacc4c..06fcbca 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1201,12 +1201,10 @@ packages: matrix: dependency: "direct main" description: - path: "." - ref: main - resolved-ref: "58c4cf19d010d9ae193e9df10bd1f8fdf02277b0" - url: "https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git" - source: git - version: "2.0.1" + path: "/home/officialdakari/repos/matrix-dart-sdk" + relative: false + source: path + version: "3.0.1" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 619a8e1..5a88b0c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,9 +65,10 @@ dependencies: linkify: ^5.0.0 material: ^1.0.0+2 matrix: - git: - url: https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git - ref: main + path: /home/officialdakari/repos/matrix-dart-sdk + # git: + # url: https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git + # ref: feature/threads mime: ^1.0.6 native_imaging: ^0.2.0 opus_caf_converter_dart: ^1.0.1 From 74c05be615643daa564a33d103bff9c4b51555ed Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Fri, 24 Oct 2025 21:20:53 +0500 Subject: [PATCH 02/22] gonna continue later (a long time) --- lib/pages/chat/chat.dart | 35 ++++++++++++++----- lib/pages/chat/chat_event_list.dart | 7 +++- lib/pages/chat/events/message.dart | 7 ++++ lib/pages/chat_list/chat_list_item.dart | 2 +- .../filtered_timeline_extension.dart | 12 ++++++- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index d978346..bbb73e5 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -317,6 +317,8 @@ class ChatController extends State if (kIsWeb) { onFocusSub = html.window.onFocus.listen((_) => setReadMarker()); } + + _getThreads(); } void _tryLoadTimeline() async { @@ -376,6 +378,7 @@ class ChatController extends State } Future? loadTimelineFuture; + Map? threads; int? animateInEventIndex; @@ -418,6 +421,15 @@ class ChatController extends State return; } + Future _getThreads() async { + try { + threads = await room.getThreads(); + Logs().w('Thread amount: ${threads?.length}'); + } catch (e, s) { + Logs().w('Unable to load threads in $roomId', e, s); + } + } + String? scrollToEventIdMarker; @override @@ -754,7 +766,7 @@ class ChatController extends State return; } - Navigator.of(context).push(new MaterialPageRoute( + Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext ctx) { return RecoveredEventDialog( event: recoveredEvent, @@ -774,7 +786,7 @@ class ChatController extends State } final event = selectedEvents.single; var text = event.isRichMessage ? event.formattedText : event.text; - var content = {...event.content}; + final content = {...event.content}; try { text = await Translator.translate( text, PlatformDispatcher.instance.locale.languageCode); @@ -873,18 +885,23 @@ class ChatController extends State void endPollAction() async { final event = selectedEvents.first; - if (event == null) return; final client = currentRoomBundle.firstWhere( (cl) => selectedEvents.first.senderId == cl!.userID, orElse: () => null, ); if (client == null) return; - if (event.senderId != client!.userID) return; - await room.sendEvent({ - 'org.matrix.msc1767.text': 'Ended poll', - 'm.relates_to': {'rel_type': 'm.reference', 'event_id': event.eventId}, - 'body': 'Ended poll' - }, type: 'org.matrix.msc3381.poll.end'); + if (event.senderId != client.userID) return; + await room.sendEvent( + { + 'org.matrix.msc1767.text': 'Ended poll', + 'm.relates_to': { + 'rel_type': 'm.reference', + 'event_id': event.eventId, + }, + 'body': 'Ended poll', + }, + type: 'org.matrix.msc3381.poll.end', + ); } void redactEventsAction() async { diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 3fc2937..1db76b2 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -37,7 +37,7 @@ class ChatEventList extends StatelessWidget { final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - final events = timeline.events.filterByVisibleInGui(); + final events = timeline.events.filterByVisibleInGui().filterByThreaded(false); final animateInEventIndex = controller.animateInEventIndex; // create a map of eventId --> index to greatly improve performance of @@ -120,6 +120,10 @@ class ChatEventList extends StatelessWidget { final animateIn = animateInEventIndex != null && timeline.events.length > animateInEventIndex && event == timeline.events[animateInEventIndex]; + + final thread = (controller.threads?.containsKey(event.eventId) ?? false) + ? controller.threads![event.eventId] + : null; return AutoScrollTag( key: ValueKey(event.eventId), @@ -128,6 +132,7 @@ class ChatEventList extends StatelessWidget { child: Message( event, animateIn: animateIn, + thread: thread, resetAnimateIn: () { controller.animateInEventIndex = null; }, diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 0f4d26b..cb55680 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -45,6 +45,7 @@ class Message extends StatelessWidget { final List colors; final bool gradient; final bool singleSelected; + final Thread? thread; const Message( this.event, { @@ -54,6 +55,7 @@ class Message extends StatelessWidget { this.longPressSelect = false, this.gradient = false, this.singleSelected = false, + this.thread, required this.onSelect, required this.onInfoTab, required this.scrollToEventId, @@ -688,6 +690,11 @@ class Message extends StatelessWidget { : const SizedBox.shrink(), ), ), + Text( + thread == null + ? 'No thread' + : 'Has thread, last event: ${thread!.lastEvent != null ? thread!.lastEvent!.eventId : 'None'}', + ), ], ), ), diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 9ccac97..ffa49a4 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -253,7 +253,7 @@ class ChatListItem extends StatelessWidget { children: [ if (typingText.isEmpty && ownMessage && - room.lastEvent!.status.isSending) ...[ + (room.lastEvent?.status.isSending ?? false)) ...[ const SizedBox( width: 16, height: 16, diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index 756d23f..4732c56 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -4,6 +4,10 @@ import 'package:matrix/matrix.dart'; import '../../config/app_config.dart'; extension VisibleInGuiExtension on List { + List filterByThreaded(bool threaded) { + return where((e) => e.isThreaded == threaded).toList(); + } + List filterByVisibleInGui({String? exceptionEventId}) { final visibleEvents = where((e) => e.isVisibleInGui || e.eventId == exceptionEventId) @@ -46,7 +50,9 @@ extension IsStateExtension on Event { // if we enabled to hide all redacted events, don't show those (!AppConfig.hideRedactedEvents || !redacted) && // if we enabled to hide all unknown events, don't show those - (!AppConfig.hideUnknownEvents || isEventTypeKnown || type == PollEvents.PollStart) && + (!AppConfig.hideUnknownEvents || + isEventTypeKnown || + type == PollEvents.PollStart) && // remove state events that we don't want to render (isState || !AppConfig.hideAllStateEvents) && // hide simple join/leave member events in public rooms @@ -56,5 +62,9 @@ extension IsStateExtension on Event { content.tryGet('membership') == 'ban' || stateKey != senderId); + bool get isThreaded => + relationshipEventId != null && + relationshipType == RelationshipTypes.thread; + bool get isState => this.stateKey != null; } From 6e4ab00b57714b54a1888ab736ee444d65332e85 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Fri, 24 Oct 2025 21:22:34 +0500 Subject: [PATCH 03/22] fix typo --- assets/l10n/intl_ru.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index 052efcb..f652018 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -41,7 +41,7 @@ "type": "String", "placeholders": {} }, - "cleanExifDescription": "Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений еред отправкой.", + "cleanExifDescription": "Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений перед отправкой.", "@cleanExifDescription": { "type": "String", "placeholders": {} From d84735e03b52807c5942a6377d9800ced281c03e Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sat, 25 Oct 2025 14:55:53 +0500 Subject: [PATCH 04/22] add AppConfig.httpProxy --- lib/config/app_config.dart | 2 ++ lib/config/setting_keys.dart | 1 + lib/utils/client_manager.dart | 2 +- lib/utils/custom_http_client.dart | 38 +++++++++++++++++++++---------- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 8704c22..c552835 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -15,6 +15,8 @@ abstract class AppConfig { static bool enableGradient = true; static bool cleanExif = true; + + static String? httpProxy; static String get defaultHomeserver => _defaultHomeserver; static double fontSizeFactor = 1; static const Color chatColor = primaryColor; diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index 1ee36b5..ebeb3b2 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -1,6 +1,7 @@ import 'package:shared_preferences/shared_preferences.dart'; abstract class SettingKeys { + static const String httpProxy = 'xyz.extera.next.httpProxy'; static const String cleanExif = 'xyz.extera.next.cleanExif'; static const String displayNavigationRail = 'chat.fluffy.displayNavigationRail'; static const String hideAvatarsInInvites = 'xyz.extera.next.hideAvatarsInInvites'; diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index f8741f0..8e3afb2 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -115,7 +115,7 @@ abstract class ClientManager { return Client( clientName, httpClient: - PlatformInfos.isAndroid ? CustomHttpClient.createHTTPClient() : null, + CustomHttpClient.createHTTPClient(), verificationMethods: { KeyVerificationMethod.numbers, if (kIsWeb || PlatformInfos.isMobile || PlatformInfos.isLinux) diff --git a/lib/utils/custom_http_client.dart b/lib/utils/custom_http_client.dart index c310fdd..c6c5060 100644 --- a/lib/utils/custom_http_client.dart +++ b/lib/utils/custom_http_client.dart @@ -1,29 +1,43 @@ import 'dart:convert'; import 'dart:io'; +import 'package:extera_next/config/app_config.dart'; +import 'package:extera_next/utils/platform_infos.dart'; import 'package:http/http.dart' as http; import 'package:http/io_client.dart'; import 'package:extera_next/config/isrg_x1.dart'; class CustomHttpClient { - static HttpClient customHttpClient(String? cert) { + static HttpClient? customHttpClient(String? cert) { + if (PlatformInfos.isWeb) return null; + final context = SecurityContext.defaultContext; - try { - if (cert != null) { - final bytes = utf8.encode(cert); - context.setTrustedCertificatesBytes(bytes); - } - } on TlsException catch (e) { - if (e.osError != null && - e.osError!.message.contains('CERT_ALREADY_IN_HASH_TABLE')) { - } else { - rethrow; + if (PlatformInfos.isAndroid) { + try { + if (cert != null) { + final bytes = utf8.encode(cert); + context.setTrustedCertificatesBytes(bytes); + } + } on TlsException catch (e) { + if (e.osError != null && + e.osError!.message.contains('CERT_ALREADY_IN_HASH_TABLE')) { + } else { + rethrow; + } } } - return HttpClient(context: context); + final client = HttpClient(context: context); + + if (AppConfig.httpProxy != null) { + client.findProxy = (uri) { + return "PROXY ${AppConfig.httpProxy};"; + }; + } + + return client; } static http.Client createHTTPClient() => IOClient(customHttpClient(ISRG_X1)); From 251ca9893dba0239df50cd22a2fff2c9a8a822c0 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sat, 25 Oct 2025 18:43:43 +0500 Subject: [PATCH 05/22] add /room/:room/:thread route edit chat.dart to support threads edit SendFileDialog and SendPollDialog to support threads --- lib/config/routes.dart | 28 +++++++ lib/pages/chat/chat.dart | 105 ++++++++++++++++++++++----- lib/pages/chat/chat_event_list.dart | 2 +- lib/pages/chat/events/message.dart | 59 +++++++++++++-- lib/pages/chat/send_file_dialog.dart | 4 + lib/pages/chat/send_poll_dialog.dart | 29 +++++--- lib/pages/thread/thread.dart | 47 ++++++++++++ pubspec.lock | 2 +- pubspec.yaml | 1 + 9 files changed, 240 insertions(+), 37 deletions(-) create mode 100644 lib/pages/thread/thread.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index cc987a9..4ba6ab2 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:extera_next/pages/thread/thread.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -148,6 +149,21 @@ abstract class AppRoutes { ), ), redirect: loggedOutRedirect, + routes: [ + GoRoute( + path: ':threadroot', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ThreadPage( + roomId: state.pathParameters['roomid']!, + threadRootEventId: + state.pathParameters['threadroot']!, + eventId: state.uri.queryParameters['event'], + ), + ), + ), + ], ), ], redirect: loggedOutRedirect, @@ -352,6 +368,18 @@ abstract class AppRoutes { }, redirect: loggedOutRedirect, routes: [ + GoRoute( + path: ':threadroot', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ThreadPage( + roomId: state.pathParameters['roomid']!, + threadRootEventId: state.pathParameters['threadroot']!, + eventId: state.uri.queryParameters['event'], + ), + ), + ), GoRoute( path: 'search', pageBuilder: (context, state) => defaultPageBuilder( diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index bbb73e5..048c4d5 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -85,12 +85,14 @@ class ChatPage extends StatelessWidget { class ChatPageWithRoom extends StatefulWidget { final Room room; + final Thread? thread; final List? shareItems; final String? eventId; const ChatPageWithRoom({ super.key, required this.room, + this.thread, this.shareItems, this.eventId, }); @@ -102,14 +104,18 @@ class ChatPageWithRoom extends StatefulWidget { class ChatController extends State with WidgetsBindingObserver { Room get room => sendingClient.getRoomById(roomId) ?? widget.room; + Thread? get thread => + sendingClient.getRoomById(roomId)?.threads[threadRootEventId] ?? + widget.room.threads[threadRootEventId]; late Client sendingClient; - RoomTimeline? timeline; + Timeline? timeline; late final String readMarkerEventId; String get roomId => widget.room.id; + String? get threadRootEventId => widget.thread?.rootEvent.eventId; final AutoScrollController scrollController = AutoScrollController(); @@ -134,6 +140,7 @@ class ChatController extends State builder: (c) => SendFileDialog( files: details.files, room: room, + thread: thread, replyEvent: replyEvent, outerContext: context, ), @@ -274,6 +281,7 @@ class ChatController extends State builder: (c) => SendFileDialog( files: files, room: room, + thread: thread, outerContext: context, replyEvent: replyEvent, ), @@ -324,6 +332,7 @@ class ChatController extends State void _tryLoadTimeline() async { final initialEventId = widget.eventId; loadTimelineFuture = _getTimeline(); + Logs().v("Trying to load timeline..."); try { await loadTimelineFuture; if (initialEventId != null) scrollToEventId(initialEventId); @@ -387,15 +396,7 @@ class ChatController extends State animateInEventIndex = i; } - Future _getTimeline({ - String? eventContextId, - }) async { - await Matrix.of(context).client.roomsLoading; - await Matrix.of(context).client.accountDataLoading; - if (eventContextId != null && - (!eventContextId.isValidMatrixId || eventContextId.sigil != '\$')) { - eventContextId = null; - } + Future _loadRoomTimeline({String? eventContextId}) async { try { timeline?.cancelSubscriptions(); timeline = await room.getTimeline( @@ -415,6 +416,56 @@ class ChatController extends State _showScrollUpMaterialBanner(eventContextId!); } } + } + + Future _loadThreadTimeline({String? eventContextId}) async { + if (thread == null) { + throw Exception( + "_loadThreadTimeline should not be called, thread == null", + ); + } + try { + timeline?.cancelSubscriptions(); + timeline = await thread!.getTimeline( + onUpdate: updateView, + eventContextId: eventContextId, + onInsert: onInsert, + ); + if (timeline is ThreadTimeline) { + (timeline as ThreadTimeline).getThreadEvents(); + } + Logs().v("Thread timeline loaded"); + } catch (e, s) { + Logs().w( + 'Unable to load timeline on event ID $eventContextId (in thread)', + e, + s); + if (!mounted) return; + timeline = await thread!.getTimeline( + onUpdate: updateView, + onInsert: onInsert, + ); + if (!mounted) return; + if (e is TimeoutException || e is IOException) { + _showScrollUpMaterialBanner(eventContextId!); + } + } + } + + Future _getTimeline({ + String? eventContextId, + }) async { + await Matrix.of(context).client.roomsLoading; + await Matrix.of(context).client.accountDataLoading; + if (eventContextId != null && + (!eventContextId.isValidMatrixId || eventContextId.sigil != '\$')) { + eventContextId = null; + } + if (thread == null) { + await _loadRoomTimeline(eventContextId: eventContextId); + } else { + await _loadThreadTimeline(eventContextId: eventContextId); + } timeline!.requestKeys(onlineKeyBackupOnly: false); if (room.markedUnread) room.markUnread(false); @@ -471,9 +522,13 @@ class ChatController extends State .then((_) { _setReadMarkerFuture = null; }); - if (eventId == null || eventId == timeline.room.lastEvent?.eventId) { - Matrix.of(context).backgroundPush?.cancelNotification(roomId); + + if (timeline is RoomTimeline) { + if (eventId == null || eventId == timeline.room.lastEvent?.eventId) { + Matrix.of(context).backgroundPush?.cancelNotification(roomId); + } } + // TODO same for Threads } @override @@ -540,12 +595,13 @@ class ChatController extends State } // ignore: unawaited_futures - room.sendTextEvent( - sendController.text, - inReplyTo: replyEvent, - editEventId: editEvent?.eventId, - parseCommands: parseCommands, - ); + room.sendTextEvent(sendController.text, + inReplyTo: replyEvent, + editEventId: editEvent?.eventId, + parseCommands: parseCommands, + threadRootEventId: thread?.rootEvent.eventId, + threadLastEventId: + thread?.lastEvent?.eventId ?? thread?.rootEvent.eventId); sendController.value = TextEditingValue( text: pendingText, selection: const TextSelection.collapsed(offset: 0), @@ -562,8 +618,13 @@ class ChatController extends State void sendPollAction() async { await showAdaptiveDialog( - context: context, - builder: (c) => SendPollDialog(room: room, outerContext: context)); + context: context, + builder: (c) => SendPollDialog( + room: room, + thread: thread, + outerContext: context, + ), + ); replyEvent = null; } @@ -582,6 +643,7 @@ class ChatController extends State builder: (c) => SendFileDialog( files: files, room: room, + thread: thread, outerContext: context, replyEvent: replyEvent, ), @@ -596,6 +658,7 @@ class ChatController extends State builder: (c) => SendFileDialog( files: [XFile.fromData(image)], room: room, + thread: thread, outerContext: context, ), ); @@ -612,6 +675,7 @@ class ChatController extends State builder: (c) => SendFileDialog( files: [file], room: room, + thread: thread, outerContext: context, ), ); @@ -631,6 +695,7 @@ class ChatController extends State builder: (c) => SendFileDialog( files: [file], room: room, + thread: thread, outerContext: context, ), ); diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 1db76b2..178dc72 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -37,7 +37,7 @@ class ChatEventList extends StatelessWidget { final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - final events = timeline.events.filterByVisibleInGui().filterByThreaded(false); + final events = timeline.events.filterByVisibleInGui().filterByThreaded(controller.thread != null); final animateInEventIndex = controller.animateInEventIndex; // create a map of eventId --> index to greatly improve performance of diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index cb55680..9ba3ef3 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -3,10 +3,12 @@ import 'dart:ui' as ui; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:extera_next/utils/adaptive_bottom_sheet.dart'; import 'package:extera_next/utils/poll_events.dart'; +import 'package:extera_next/widgets/mxc_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:extera_next/generated/l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import 'package:swipe_to_action/swipe_to_action.dart'; @@ -690,11 +692,58 @@ class Message extends StatelessWidget { : const SizedBox.shrink(), ), ), - Text( - thread == null - ? 'No thread' - : 'Has thread, last event: ${thread!.lastEvent != null ? thread!.lastEvent!.eventId : 'None'}', - ), + thread != null + ? Align( + alignment: ownMessage + ? Alignment.bottomRight + : Alignment.bottomLeft, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: InkWell( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.chat_bubble_outline, + color: Colors.grey[200], + size: 20, + ), + const SizedBox(width: 16), + thread!.lastEvent != null + ? FutureBuilder( + future: thread!.lastEvent! + .fetchSenderUser(), + builder: + (context, snapshot) { + final user = snapshot + .data ?? + event + .senderFromMemoryOrFallback; + + return Avatar( + mxContent: + user.avatarUrl, + name: user + .calcDisplayname(), + size: 24, + ); + }, + ) + : const SizedBox.shrink(), + const SizedBox(width: 6), + thread!.lastEvent != null + ? Text( + thread!.lastEvent!.text) + : const Text('Thread'), + ], + ), + onTap: () => context.go( + '/rooms/${event.roomId}/${event.eventId}', + ), + ), + ), + ) + : const SizedBox.shrink(), ], ), ), diff --git a/lib/pages/chat/send_file_dialog.dart b/lib/pages/chat/send_file_dialog.dart index 853ca2a..e00f3b2 100644 --- a/lib/pages/chat/send_file_dialog.dart +++ b/lib/pages/chat/send_file_dialog.dart @@ -23,12 +23,14 @@ import 'package:html_unescape/html_unescape.dart'; class SendFileDialog extends StatefulWidget { final Room room; + final Thread? thread; final List files; final BuildContext outerContext; final Event? replyEvent; const SendFileDialog({ required this.room, + required this.thread, required this.files, required this.outerContext, this.replyEvent, @@ -151,6 +153,8 @@ class SendFileDialogState extends State { thumbnail: thumbnail, shrinkImageMaxDimension: compress ? 1600 : null, extraContent: extraContent, + threadLastEventId: widget.thread?.lastEvent?.eventId ?? widget.thread?.rootEvent.eventId, + threadRootEventId: widget.thread?.rootEvent.eventId ); } on MatrixException catch (e) { final retryAfterMs = e.retryAfterMs; diff --git a/lib/pages/chat/send_poll_dialog.dart b/lib/pages/chat/send_poll_dialog.dart index 6f81ffd..3142829 100644 --- a/lib/pages/chat/send_poll_dialog.dart +++ b/lib/pages/chat/send_poll_dialog.dart @@ -1,5 +1,4 @@ import 'package:uuid/uuid.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:extera_next/generated/l10n/l10n.dart'; import 'package:matrix/matrix.dart'; @@ -8,9 +7,11 @@ class SendPollDialog extends StatefulWidget { final Room room; final BuildContext outerContext; final Event? replyEvent; + final Thread? thread; const SendPollDialog({ required this.room, + required this.thread, required this.outerContext, this.replyEvent, super.key, @@ -74,11 +75,13 @@ class SendPollDialogState extends State { 'm.text': question, }, 'answers': answers - .map((answer) => { - 'id': const Uuid().v4(), - 'org.matrix.msc1767.text': answer, - 'm.text': answer, - }) + .map( + (answer) => { + 'id': const Uuid().v4(), + 'org.matrix.msc1767.text': answer, + 'm.text': answer, + }, + ) .toList(), 'max_selections': _maxSelections, 'kind': _kind, @@ -86,7 +89,13 @@ class SendPollDialogState extends State { }; try { - await widget.room.sendEvent(pollContent, type: 'org.matrix.msc3381.poll.start'); + await widget.room.sendEvent( + pollContent, + type: 'org.matrix.msc3381.poll.start', + threadLastEventId: widget.thread?.lastEvent?.eventId ?? + widget.thread?.rootEvent.eventId, + threadRootEventId: widget.thread?.rootEvent.eventId, + ); // ignore: use_build_context_synchronously Navigator.of(context).pop(); } catch (e) { @@ -154,7 +163,7 @@ class SendPollDialogState extends State { ), const SizedBox(height: 16), DropdownButtonFormField( - value: _maxSelections, + initialValue: _maxSelections, decoration: InputDecoration( labelText: L10n.of(context).maxSelections, border: const OutlineInputBorder(), @@ -170,7 +179,7 @@ class SendPollDialogState extends State { ), const SizedBox(height: 16), DropdownButtonFormField( - value: _kind, + initialValue: _kind, decoration: InputDecoration( labelText: L10n.of(context).pollType, border: const OutlineInputBorder(), @@ -202,4 +211,4 @@ class SendPollDialogState extends State { ], ); } -} \ No newline at end of file +} diff --git a/lib/pages/thread/thread.dart b/lib/pages/thread/thread.dart new file mode 100644 index 0000000..b1f0bf4 --- /dev/null +++ b/lib/pages/thread/thread.dart @@ -0,0 +1,47 @@ +import 'package:extera_next/generated/l10n/l10n.dart'; +import 'package:extera_next/pages/chat/chat.dart'; +import 'package:extera_next/widgets/matrix.dart'; +import 'package:extera_next/widgets/share_scaffold_dialog.dart'; +import 'package:flutter/material.dart'; + +class ThreadPage extends StatelessWidget { + final String roomId; + final List? shareItems; + final String? threadRootEventId; + final String? eventId; + + const ThreadPage({ + super.key, + required this.roomId, + required this.threadRootEventId, + this.eventId, + this.shareItems, + }); + + @override + Widget build(BuildContext context) { + final client = Matrix.of(context).client; + final room = client.getRoomById(roomId); + if (room == null) { + return Scaffold( + appBar: AppBar(title: Text(L10n.of(context).oopsSomethingWentWrong)), + body: Center( + child: Padding( + padding: const EdgeInsets.all(16), + child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), + ), + ), + ); + } + + final thread = room.threads[threadRootEventId]; + + return ChatPageWithRoom( + key: Key('chat_page_${roomId}_${threadRootEventId}_$eventId'), + room: room, + thread: thread, + shareItems: shareItems, + eventId: eventId, + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 06fcbca..431f0cc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2171,7 +2171,7 @@ packages: source: hosted version: "3.1.4" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff diff --git a/pubspec.yaml b/pubspec.yaml index 5a88b0c..2f81498 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,7 @@ dependencies: wakelock_plus: ^1.2.2 webrtc_interface: ^1.0.13 dio: ^5.9.0 + uuid: ^4.5.1 dev_dependencies: flutter_lints: ^3.0.0 From 9d8f1e3656e1cb9f559e8b1042afa798b053d4a2 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sat, 25 Oct 2025 19:15:36 +0500 Subject: [PATCH 06/22] add "discuss" button --- assets/l10n/intl_en.arb | 1 + assets/l10n/intl_ru.arb | 1 + lib/generated/l10n/l10n.dart | 6 ++++ lib/generated/l10n/l10n_ar.dart | 3 ++ lib/generated/l10n/l10n_be.dart | 3 ++ lib/generated/l10n/l10n_bn.dart | 3 ++ lib/generated/l10n/l10n_bo.dart | 3 ++ lib/generated/l10n/l10n_ca.dart | 3 ++ lib/generated/l10n/l10n_cs.dart | 3 ++ lib/generated/l10n/l10n_de.dart | 3 ++ lib/generated/l10n/l10n_el.dart | 3 ++ lib/generated/l10n/l10n_en.dart | 3 ++ lib/generated/l10n/l10n_eo.dart | 3 ++ lib/generated/l10n/l10n_es.dart | 3 ++ lib/generated/l10n/l10n_et.dart | 3 ++ lib/generated/l10n/l10n_eu.dart | 3 ++ lib/generated/l10n/l10n_fa.dart | 3 ++ lib/generated/l10n/l10n_fi.dart | 3 ++ lib/generated/l10n/l10n_fil.dart | 3 ++ lib/generated/l10n/l10n_fr.dart | 3 ++ lib/generated/l10n/l10n_ga.dart | 3 ++ lib/generated/l10n/l10n_gl.dart | 3 ++ lib/generated/l10n/l10n_he.dart | 3 ++ lib/generated/l10n/l10n_hi.dart | 3 ++ lib/generated/l10n/l10n_hr.dart | 3 ++ lib/generated/l10n/l10n_hu.dart | 3 ++ lib/generated/l10n/l10n_ia.dart | 3 ++ lib/generated/l10n/l10n_id.dart | 3 ++ lib/generated/l10n/l10n_ie.dart | 3 ++ lib/generated/l10n/l10n_it.dart | 3 ++ lib/generated/l10n/l10n_ja.dart | 3 ++ lib/generated/l10n/l10n_ka.dart | 3 ++ lib/generated/l10n/l10n_ko.dart | 3 ++ lib/generated/l10n/l10n_lt.dart | 3 ++ lib/generated/l10n/l10n_lv.dart | 3 ++ lib/generated/l10n/l10n_nb.dart | 3 ++ lib/generated/l10n/l10n_nl.dart | 3 ++ lib/generated/l10n/l10n_pl.dart | 3 ++ lib/generated/l10n/l10n_pt.dart | 3 ++ lib/generated/l10n/l10n_ro.dart | 3 ++ lib/generated/l10n/l10n_ru.dart | 3 ++ lib/generated/l10n/l10n_sk.dart | 3 ++ lib/generated/l10n/l10n_sl.dart | 3 ++ lib/generated/l10n/l10n_sr.dart | 3 ++ lib/generated/l10n/l10n_sv.dart | 3 ++ lib/generated/l10n/l10n_ta.dart | 3 ++ lib/generated/l10n/l10n_te.dart | 3 ++ lib/generated/l10n/l10n_th.dart | 3 ++ lib/generated/l10n/l10n_tr.dart | 3 ++ lib/generated/l10n/l10n_uk.dart | 3 ++ lib/generated/l10n/l10n_vi.dart | 3 ++ lib/generated/l10n/l10n_zh.dart | 3 ++ lib/pages/chat/chat.dart | 16 +++++++++ lib/pages/chat/chat_app_bar_title.dart | 47 +++++++++++++++++++------- lib/pages/chat/chat_view.dart | 26 +++++++++----- lib/pages/chat/events/message.dart | 4 +-- lib/pages/thread/thread.dart | 1 + 57 files changed, 226 insertions(+), 23 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index a0db7d0..e1e6aa1 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -16,6 +16,7 @@ "vote": "Vote", "changeVote": "Re-vote", "choicesSelected": "{selected} of {max} selected", + "discuss": "Discuss", "@choicesSelected": { "type": "String", "placeholders": { diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index 052efcb..8e50924 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -16,6 +16,7 @@ "vote": "Голосовать", "changeVote": "Изменить ответ", "choicesSelected": "Выбрано {selected} из {max}", + "discuss": "Обсудить", "@choicesSelected": { "type": "String", "placeholders": { diff --git a/lib/generated/l10n/l10n.dart b/lib/generated/l10n/l10n.dart index 0e28925..0142cb1 100644 --- a/lib/generated/l10n/l10n.dart +++ b/lib/generated/l10n/l10n.dart @@ -283,6 +283,12 @@ abstract class L10n { /// **'{selected} of {max} selected'** String choicesSelected(int selected, int max); + /// No description provided for @discuss. + /// + /// In en, this message translates to: + /// **'Discuss'** + String get discuss; + /// No description provided for @pollType. /// /// In en, this message translates to: diff --git a/lib/generated/l10n/l10n_ar.dart b/lib/generated/l10n/l10n_ar.dart index 6080152..1dca7be 100644 --- a/lib/generated/l10n/l10n_ar.dart +++ b/lib/generated/l10n/l10n_ar.dart @@ -56,6 +56,9 @@ class L10nAr extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_be.dart b/lib/generated/l10n/l10n_be.dart index a58e901..e56739a 100644 --- a/lib/generated/l10n/l10n_be.dart +++ b/lib/generated/l10n/l10n_be.dart @@ -56,6 +56,9 @@ class L10nBe extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_bn.dart b/lib/generated/l10n/l10n_bn.dart index 28883f5..9999e47 100644 --- a/lib/generated/l10n/l10n_bn.dart +++ b/lib/generated/l10n/l10n_bn.dart @@ -56,6 +56,9 @@ class L10nBn extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_bo.dart b/lib/generated/l10n/l10n_bo.dart index df8ca51..218b960 100644 --- a/lib/generated/l10n/l10n_bo.dart +++ b/lib/generated/l10n/l10n_bo.dart @@ -56,6 +56,9 @@ class L10nBo extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ca.dart b/lib/generated/l10n/l10n_ca.dart index 7a202db..ceadb89 100644 --- a/lib/generated/l10n/l10n_ca.dart +++ b/lib/generated/l10n/l10n_ca.dart @@ -56,6 +56,9 @@ class L10nCa extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_cs.dart b/lib/generated/l10n/l10n_cs.dart index 3fc13e6..1c440b6 100644 --- a/lib/generated/l10n/l10n_cs.dart +++ b/lib/generated/l10n/l10n_cs.dart @@ -56,6 +56,9 @@ class L10nCs extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_de.dart b/lib/generated/l10n/l10n_de.dart index 0dfe099..08b344f 100644 --- a/lib/generated/l10n/l10n_de.dart +++ b/lib/generated/l10n/l10n_de.dart @@ -56,6 +56,9 @@ class L10nDe extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_el.dart b/lib/generated/l10n/l10n_el.dart index e62f767..c01c3a3 100644 --- a/lib/generated/l10n/l10n_el.dart +++ b/lib/generated/l10n/l10n_el.dart @@ -56,6 +56,9 @@ class L10nEl extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_en.dart b/lib/generated/l10n/l10n_en.dart index f6b2d1f..bfc5779 100644 --- a/lib/generated/l10n/l10n_en.dart +++ b/lib/generated/l10n/l10n_en.dart @@ -56,6 +56,9 @@ class L10nEn extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_eo.dart b/lib/generated/l10n/l10n_eo.dart index f9194f8..d1be07f 100644 --- a/lib/generated/l10n/l10n_eo.dart +++ b/lib/generated/l10n/l10n_eo.dart @@ -56,6 +56,9 @@ class L10nEo extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_es.dart b/lib/generated/l10n/l10n_es.dart index 330336c..45b37ea 100644 --- a/lib/generated/l10n/l10n_es.dart +++ b/lib/generated/l10n/l10n_es.dart @@ -56,6 +56,9 @@ class L10nEs extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_et.dart b/lib/generated/l10n/l10n_et.dart index 1f69070..108e526 100644 --- a/lib/generated/l10n/l10n_et.dart +++ b/lib/generated/l10n/l10n_et.dart @@ -56,6 +56,9 @@ class L10nEt extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_eu.dart b/lib/generated/l10n/l10n_eu.dart index 52e8d1d..5ed11ca 100644 --- a/lib/generated/l10n/l10n_eu.dart +++ b/lib/generated/l10n/l10n_eu.dart @@ -56,6 +56,9 @@ class L10nEu extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_fa.dart b/lib/generated/l10n/l10n_fa.dart index afb64a9..e41eaf6 100644 --- a/lib/generated/l10n/l10n_fa.dart +++ b/lib/generated/l10n/l10n_fa.dart @@ -56,6 +56,9 @@ class L10nFa extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_fi.dart b/lib/generated/l10n/l10n_fi.dart index c71cbe5..c786c02 100644 --- a/lib/generated/l10n/l10n_fi.dart +++ b/lib/generated/l10n/l10n_fi.dart @@ -56,6 +56,9 @@ class L10nFi extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_fil.dart b/lib/generated/l10n/l10n_fil.dart index 1429a9c..72e0637 100644 --- a/lib/generated/l10n/l10n_fil.dart +++ b/lib/generated/l10n/l10n_fil.dart @@ -56,6 +56,9 @@ class L10nFil extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_fr.dart b/lib/generated/l10n/l10n_fr.dart index 5602fbf..eb38cff 100644 --- a/lib/generated/l10n/l10n_fr.dart +++ b/lib/generated/l10n/l10n_fr.dart @@ -56,6 +56,9 @@ class L10nFr extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ga.dart b/lib/generated/l10n/l10n_ga.dart index 131fcd2..6c55850 100644 --- a/lib/generated/l10n/l10n_ga.dart +++ b/lib/generated/l10n/l10n_ga.dart @@ -56,6 +56,9 @@ class L10nGa extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_gl.dart b/lib/generated/l10n/l10n_gl.dart index 01ae489..5d1caec 100644 --- a/lib/generated/l10n/l10n_gl.dart +++ b/lib/generated/l10n/l10n_gl.dart @@ -56,6 +56,9 @@ class L10nGl extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_he.dart b/lib/generated/l10n/l10n_he.dart index 7db68b8..9bae162 100644 --- a/lib/generated/l10n/l10n_he.dart +++ b/lib/generated/l10n/l10n_he.dart @@ -56,6 +56,9 @@ class L10nHe extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_hi.dart b/lib/generated/l10n/l10n_hi.dart index bf68016..fc80631 100644 --- a/lib/generated/l10n/l10n_hi.dart +++ b/lib/generated/l10n/l10n_hi.dart @@ -56,6 +56,9 @@ class L10nHi extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_hr.dart b/lib/generated/l10n/l10n_hr.dart index 1e8145d..9187316 100644 --- a/lib/generated/l10n/l10n_hr.dart +++ b/lib/generated/l10n/l10n_hr.dart @@ -56,6 +56,9 @@ class L10nHr extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_hu.dart b/lib/generated/l10n/l10n_hu.dart index 410e9e0..0485832 100644 --- a/lib/generated/l10n/l10n_hu.dart +++ b/lib/generated/l10n/l10n_hu.dart @@ -56,6 +56,9 @@ class L10nHu extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ia.dart b/lib/generated/l10n/l10n_ia.dart index 5be8d93..8ea9d92 100644 --- a/lib/generated/l10n/l10n_ia.dart +++ b/lib/generated/l10n/l10n_ia.dart @@ -56,6 +56,9 @@ class L10nIa extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_id.dart b/lib/generated/l10n/l10n_id.dart index 7075f65..e0c5964 100644 --- a/lib/generated/l10n/l10n_id.dart +++ b/lib/generated/l10n/l10n_id.dart @@ -56,6 +56,9 @@ class L10nId extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ie.dart b/lib/generated/l10n/l10n_ie.dart index e455c13..1074ddb 100644 --- a/lib/generated/l10n/l10n_ie.dart +++ b/lib/generated/l10n/l10n_ie.dart @@ -56,6 +56,9 @@ class L10nIe extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_it.dart b/lib/generated/l10n/l10n_it.dart index 3edf595..eb05dec 100644 --- a/lib/generated/l10n/l10n_it.dart +++ b/lib/generated/l10n/l10n_it.dart @@ -56,6 +56,9 @@ class L10nIt extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ja.dart b/lib/generated/l10n/l10n_ja.dart index 5c0b0b9..04a1d22 100644 --- a/lib/generated/l10n/l10n_ja.dart +++ b/lib/generated/l10n/l10n_ja.dart @@ -56,6 +56,9 @@ class L10nJa extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ka.dart b/lib/generated/l10n/l10n_ka.dart index b14cf51..53c423f 100644 --- a/lib/generated/l10n/l10n_ka.dart +++ b/lib/generated/l10n/l10n_ka.dart @@ -56,6 +56,9 @@ class L10nKa extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ko.dart b/lib/generated/l10n/l10n_ko.dart index 987d15b..92b8286 100644 --- a/lib/generated/l10n/l10n_ko.dart +++ b/lib/generated/l10n/l10n_ko.dart @@ -56,6 +56,9 @@ class L10nKo extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_lt.dart b/lib/generated/l10n/l10n_lt.dart index ac83bb7..28362b4 100644 --- a/lib/generated/l10n/l10n_lt.dart +++ b/lib/generated/l10n/l10n_lt.dart @@ -56,6 +56,9 @@ class L10nLt extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_lv.dart b/lib/generated/l10n/l10n_lv.dart index 1c846be..b3417a4 100644 --- a/lib/generated/l10n/l10n_lv.dart +++ b/lib/generated/l10n/l10n_lv.dart @@ -56,6 +56,9 @@ class L10nLv extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_nb.dart b/lib/generated/l10n/l10n_nb.dart index 46d562b..d6fc856 100644 --- a/lib/generated/l10n/l10n_nb.dart +++ b/lib/generated/l10n/l10n_nb.dart @@ -56,6 +56,9 @@ class L10nNb extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_nl.dart b/lib/generated/l10n/l10n_nl.dart index 515d851..af0eceb 100644 --- a/lib/generated/l10n/l10n_nl.dart +++ b/lib/generated/l10n/l10n_nl.dart @@ -56,6 +56,9 @@ class L10nNl extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_pl.dart b/lib/generated/l10n/l10n_pl.dart index bd7c434..a8791df 100644 --- a/lib/generated/l10n/l10n_pl.dart +++ b/lib/generated/l10n/l10n_pl.dart @@ -56,6 +56,9 @@ class L10nPl extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_pt.dart b/lib/generated/l10n/l10n_pt.dart index dec34b1..aae1a03 100644 --- a/lib/generated/l10n/l10n_pt.dart +++ b/lib/generated/l10n/l10n_pt.dart @@ -56,6 +56,9 @@ class L10nPt extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ro.dart b/lib/generated/l10n/l10n_ro.dart index bee47b5..589e285 100644 --- a/lib/generated/l10n/l10n_ro.dart +++ b/lib/generated/l10n/l10n_ro.dart @@ -56,6 +56,9 @@ class L10nRo extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ru.dart b/lib/generated/l10n/l10n_ru.dart index 6185935..6ee67f0 100644 --- a/lib/generated/l10n/l10n_ru.dart +++ b/lib/generated/l10n/l10n_ru.dart @@ -56,6 +56,9 @@ class L10nRu extends L10n { return 'Выбрано $selected из $max'; } + @override + String get discuss => 'Обсудить'; + @override String get pollType => 'Тип опроса'; diff --git a/lib/generated/l10n/l10n_sk.dart b/lib/generated/l10n/l10n_sk.dart index ccf70fd..33f6390 100644 --- a/lib/generated/l10n/l10n_sk.dart +++ b/lib/generated/l10n/l10n_sk.dart @@ -56,6 +56,9 @@ class L10nSk extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_sl.dart b/lib/generated/l10n/l10n_sl.dart index 77b0bb6..e12d910 100644 --- a/lib/generated/l10n/l10n_sl.dart +++ b/lib/generated/l10n/l10n_sl.dart @@ -56,6 +56,9 @@ class L10nSl extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_sr.dart b/lib/generated/l10n/l10n_sr.dart index 9dcd3ed..9928fe1 100644 --- a/lib/generated/l10n/l10n_sr.dart +++ b/lib/generated/l10n/l10n_sr.dart @@ -56,6 +56,9 @@ class L10nSr extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_sv.dart b/lib/generated/l10n/l10n_sv.dart index 114a14c..6b67f5c 100644 --- a/lib/generated/l10n/l10n_sv.dart +++ b/lib/generated/l10n/l10n_sv.dart @@ -56,6 +56,9 @@ class L10nSv extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_ta.dart b/lib/generated/l10n/l10n_ta.dart index 23b7602..34d65dd 100644 --- a/lib/generated/l10n/l10n_ta.dart +++ b/lib/generated/l10n/l10n_ta.dart @@ -56,6 +56,9 @@ class L10nTa extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_te.dart b/lib/generated/l10n/l10n_te.dart index 394e118..5822fb2 100644 --- a/lib/generated/l10n/l10n_te.dart +++ b/lib/generated/l10n/l10n_te.dart @@ -56,6 +56,9 @@ class L10nTe extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_th.dart b/lib/generated/l10n/l10n_th.dart index 60b3fb7..6361640 100644 --- a/lib/generated/l10n/l10n_th.dart +++ b/lib/generated/l10n/l10n_th.dart @@ -56,6 +56,9 @@ class L10nTh extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_tr.dart b/lib/generated/l10n/l10n_tr.dart index b3672ae..f5c32a3 100644 --- a/lib/generated/l10n/l10n_tr.dart +++ b/lib/generated/l10n/l10n_tr.dart @@ -56,6 +56,9 @@ class L10nTr extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_uk.dart b/lib/generated/l10n/l10n_uk.dart index 94f5d9d..60aa8ac 100644 --- a/lib/generated/l10n/l10n_uk.dart +++ b/lib/generated/l10n/l10n_uk.dart @@ -56,6 +56,9 @@ class L10nUk extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_vi.dart b/lib/generated/l10n/l10n_vi.dart index be90270..1ac0e9d 100644 --- a/lib/generated/l10n/l10n_vi.dart +++ b/lib/generated/l10n/l10n_vi.dart @@ -56,6 +56,9 @@ class L10nVi extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/generated/l10n/l10n_zh.dart b/lib/generated/l10n/l10n_zh.dart index 3bdf92f..ef91fd3 100644 --- a/lib/generated/l10n/l10n_zh.dart +++ b/lib/generated/l10n/l10n_zh.dart @@ -56,6 +56,9 @@ class L10nZh extends L10n { return '$selected of $max selected'; } + @override + String get discuss => 'Discuss'; + @override String get pollType => 'Poll type'; diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 048c4d5..9934dcd 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -948,6 +948,22 @@ class ChatController extends State } } + void discussAction() async { + final event = selectedEvents.first; + if (!room.threads.containsKey(event.eventId)) { + room.threads[event.eventId] = Thread( + room: room, + rootEvent: event, + client: room.client, + currentUserParticipated: false, + count: 0, + ); + } + + context.go('/rooms/$roomId/${event.eventId}'); + selectedEvents.clear(); + } + void endPollAction() async { final event = selectedEvents.first; final client = currentRoomBundle.firstWhere( diff --git a/lib/pages/chat/chat_app_bar_title.dart b/lib/pages/chat/chat_app_bar_title.dart index 1b11bd6..2a02699 100644 --- a/lib/pages/chat/chat_app_bar_title.dart +++ b/lib/pages/chat/chat_app_bar_title.dart @@ -31,22 +31,40 @@ class ChatAppBarTitle extends StatelessWidget { hoverColor: Colors.transparent, splashColor: Colors.transparent, highlightColor: Colors.transparent, - onTap: controller.isArchived - ? null - : () => FluffyThemes.isThreeColumnMode(context) - ? controller.toggleDisplayChatDetailsColumn() - : context.go('/rooms/${room.id}/details'), + onTap: () { + if (controller.thread != null) { + if (context.canPop()) { + context.pop(); + } else { + context.go('/rooms/${room.id}'); + } + return; + } + if (!controller.isArchived) { + if (FluffyThemes.isThreeColumnMode(context)) { + controller.toggleDisplayChatDetailsColumn(); + } else { + context.go('/rooms/${room.id}/details'); + } + } + }, child: Row( children: [ Hero( tag: 'content_banner', - child: Avatar( - mxContent: room.avatar, - name: room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), - ), - size: 32, - ), + child: controller.thread == null + ? Avatar( + mxContent: room.avatar, + name: room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)), + ), + size: 32, + ) + : Icon( + Icons.chat_bubble_outline, + color: Colors.grey[200], + size: 20, + ), ), const SizedBox(width: 12), Expanded( @@ -54,7 +72,10 @@ class ChatAppBarTitle extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))), + controller.thread == null + ? room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context))) + : '${controller.thread!.rootEvent.senderFromMemoryOrFallback.displayName ?? controller.thread!.rootEvent.senderId}: ${controller.thread!.rootEvent.text}', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index f518e02..7cda110 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -48,12 +48,20 @@ class ChatView extends StatelessWidget { tooltip: L10n.of(context).copy, onPressed: controller.copyEventsAction, ), - if (controller.selectedEvents.length == 1 && controller.selectedEvents.single.content['xyz.extera.translated'] == null) - IconButton( - icon: const Icon(Icons.translate_outlined), - tooltip: L10n.of(context).translateMessage, - onPressed: controller.translateEventAction, - ), + if (controller.selectedEvents.length == 1) + IconButton( + onPressed: controller.discussAction, + icon: const Icon(Icons.chat_bubble_outline), + tooltip: L10n.of(context).discuss, + ), + if (controller.selectedEvents.length == 1 && + controller.selectedEvents.single.content['xyz.extera.translated'] == + null) + IconButton( + icon: const Icon(Icons.translate_outlined), + tooltip: L10n.of(context).translateMessage, + onPressed: controller.translateEventAction, + ), if (controller.canSaveSelectedEvent) // Use builder context to correctly position the share dialog on iPad Builder( @@ -118,8 +126,10 @@ class ChatView extends StatelessWidget { Text(L10n.of(context).recoverMessage), ]), ), - if (controller.selectedEvents.single.type == 'org.matrix.msc3381.poll.start' && controller.selectedEvents.single.senderId == Matrix.of(context).client.userID) - + if (controller.selectedEvents.single.type == + 'org.matrix.msc3381.poll.start' && + controller.selectedEvents.single.senderId == + Matrix.of(context).client.userID) PopupMenuItem( value: _EventContextAction.endPoll, child: Row( diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 9ba3ef3..faefe7d 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -3,7 +3,6 @@ import 'dart:ui' as ui; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:extera_next/utils/adaptive_bottom_sheet.dart'; import 'package:extera_next/utils/poll_events.dart'; -import 'package:extera_next/widgets/mxc_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -733,7 +732,8 @@ class Message extends StatelessWidget { const SizedBox(width: 6), thread!.lastEvent != null ? Text( - thread!.lastEvent!.text) + thread!.lastEvent!.text, + ) : const Text('Thread'), ], ), diff --git a/lib/pages/thread/thread.dart b/lib/pages/thread/thread.dart index b1f0bf4..4c032bf 100644 --- a/lib/pages/thread/thread.dart +++ b/lib/pages/thread/thread.dart @@ -3,6 +3,7 @@ import 'package:extera_next/pages/chat/chat.dart'; import 'package:extera_next/widgets/matrix.dart'; import 'package:extera_next/widgets/share_scaffold_dialog.dart'; import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; class ThreadPage extends StatelessWidget { final String roomId; From ae3c2d84d4a7d3822724e68651bc0e9c5dd58556 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sat, 25 Oct 2025 22:09:58 +0500 Subject: [PATCH 07/22] remove proxy usage --- lib/utils/custom_http_client.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/utils/custom_http_client.dart b/lib/utils/custom_http_client.dart index 96b17fc..c73b4a3 100644 --- a/lib/utils/custom_http_client.dart +++ b/lib/utils/custom_http_client.dart @@ -29,9 +29,9 @@ class CustomHttpClient { // Use Nekoray mixed proxy // Made it for myself, remove later final httpClient = HttpClient(context: context); - httpClient.findProxy = (uri) { - return 'PROXY localhost:2080;'; - }; + // httpClient.findProxy = (uri) { + // return 'PROXY localhost:2080;'; + // }; return httpClient; } From 22209b03f2c591207c02c6a87a5ecd4ac511abb0 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 26 Oct 2025 15:00:42 +0500 Subject: [PATCH 08/22] fix: chat settings not opening feat: unread marker on thread cards --- .../reports/problems/problems-report.html | 2 +- lib/config/routes.dart | 51 ++++++++++++------- lib/pages/chat/chat.dart | 4 +- lib/pages/chat/events/message.dart | 4 +- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html index e923c4a..2e4e670 100644 --- a/android/build/reports/problems/problems-report.html +++ b/android/build/reports/problems/problems-report.html @@ -650,7 +650,7 @@ code + .copy-button { diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 4ba6ab2..ed84b31 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -151,17 +151,23 @@ abstract class AppRoutes { redirect: loggedOutRedirect, routes: [ GoRoute( - path: ':threadroot', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - ThreadPage( - roomId: state.pathParameters['roomid']!, - threadRootEventId: - state.pathParameters['threadroot']!, - eventId: state.uri.queryParameters['event'], + path: 'threads', + redirect: loggedOutRedirect, + routes: [ + GoRoute( + path: ':threadroot', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ThreadPage( + roomId: state.pathParameters['roomid']!, + threadRootEventId: + state.pathParameters['threadroot']!, + eventId: state.uri.queryParameters['event'], + ), + ), ), - ), + ], ), ], ), @@ -369,16 +375,23 @@ abstract class AppRoutes { redirect: loggedOutRedirect, routes: [ GoRoute( - path: ':threadroot', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - ThreadPage( - roomId: state.pathParameters['roomid']!, - threadRootEventId: state.pathParameters['threadroot']!, - eventId: state.uri.queryParameters['event'], + path: 'threads', + redirect: loggedOutRedirect, + routes: [ + GoRoute( + path: ':threadroot', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ThreadPage( + roomId: state.pathParameters['roomid']!, + threadRootEventId: + state.pathParameters['threadroot']!, + eventId: state.uri.queryParameters['event'], + ), + ), ), - ), + ], ), GoRoute( path: 'search', diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 9934dcd..96d7fc3 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -957,10 +957,12 @@ class ChatController extends State client: room.client, currentUserParticipated: false, count: 0, + highlightCount: 0, + notificationCount: 0, ); } - context.go('/rooms/$roomId/${event.eventId}'); + context.go('/rooms/$roomId/threads/${event.eventId}'); selectedEvents.clear(); } diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index faefe7d..06d2c17 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -703,7 +703,7 @@ class Message extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Icon( - Icons.chat_bubble_outline, + (thread?.hasNewMessages ?? false) ? Icons.mark_chat_unread_outlined : Icons.chat_bubble_outline, color: Colors.grey[200], size: 20, ), @@ -738,7 +738,7 @@ class Message extends StatelessWidget { ], ), onTap: () => context.go( - '/rooms/${event.roomId}/${event.eventId}', + '/rooms/${event.roomId}/threads/${event.eventId}', ), ), ), From 1c5a632d2a6b87d6462edb5d3c6b26f61d935e8c Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 26 Oct 2025 20:58:25 +0500 Subject: [PATCH 09/22] update thread card --- .../reports/problems/problems-report.html | 2 +- lib/pages/chat/chat.dart | 97 ++++++++++++------- lib/pages/chat/chat_event_list.dart | 5 +- lib/pages/chat/events/poll_content.dart | 43 +++++--- 4 files changed, 96 insertions(+), 51 deletions(-) diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html index 2e4e670..f85479d 100644 --- a/android/build/reports/problems/problems-report.html +++ b/android/build/reports/problems/problems-report.html @@ -650,7 +650,7 @@ code + .copy-button { diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 96d7fc3..b6eef99 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -380,14 +380,32 @@ class ChatController extends State scrollUpBannerEventId = eventId; }); - void updateView() { + Future updateView() async { if (!mounted) return; setReadMarker(); + await updateThreads(); setState(() {}); } + Future updateThreads() async { + if (timeline?.events == null) return; + final lastEvent = timeline?.events[timeline!.events.length - 1]; + + if (lastEvent == null) return; + if (lastEvent.relationshipType == RelationshipTypes.thread && + lastEvent.relationshipEventId != null) { + final thread = await room.client.database + .getThread(room.id, lastEvent.relationshipEventId!, room.client); + if (thread != null) { + setState(() { + threads?[lastEvent.eventId] = thread; + }); + } + } + } + Future? loadTimelineFuture; - Map? threads; + Map? threads = {}; int? animateInEventIndex; @@ -437,9 +455,10 @@ class ChatController extends State Logs().v("Thread timeline loaded"); } catch (e, s) { Logs().w( - 'Unable to load timeline on event ID $eventContextId (in thread)', - e, - s); + 'Unable to load timeline on event ID $eventContextId (in thread)', + e, + s, + ); if (!mounted) return; timeline = await thread!.getTimeline( onUpdate: updateView, @@ -595,13 +614,15 @@ class ChatController extends State } // ignore: unawaited_futures - room.sendTextEvent(sendController.text, - inReplyTo: replyEvent, - editEventId: editEvent?.eventId, - parseCommands: parseCommands, - threadRootEventId: thread?.rootEvent.eventId, - threadLastEventId: - thread?.lastEvent?.eventId ?? thread?.rootEvent.eventId); + room.sendTextEvent( + sendController.text, + inReplyTo: replyEvent, + editEventId: editEvent?.eventId, + parseCommands: parseCommands, + threadRootEventId: thread?.rootEvent.eventId, + threadLastEventId: + thread?.lastEvent?.eventId ?? thread?.rootEvent.eventId, + ); sendController.value = TextEditingValue( text: pendingText, selection: const TextSelection.collapsed(offset: 0), @@ -821,7 +842,8 @@ class ChatController extends State final reports = await mx.client.getEventReports(); final report = reports.firstWhere( - (rep) => rep['room_id'] == roomId && rep['event_id'] == event.eventId); + (rep) => rep['room_id'] == roomId && rep['event_id'] == event.eventId, + ); final recoveredEvent = await mx.client.getReportedEvent(report['id']); if (recoveredEvent == null) { @@ -831,15 +853,17 @@ class ChatController extends State return; } - Navigator.of(context).push(MaterialPageRoute( - builder: (BuildContext ctx) { - return RecoveredEventDialog( - event: recoveredEvent, - timeline: timeline!, - ); - }, - fullscreenDialog: true, - )); + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext ctx) { + return RecoveredEventDialog( + event: recoveredEvent, + timeline: timeline!, + ); + }, + fullscreenDialog: true, + ), + ); } void translateEventAction() async { @@ -854,7 +878,9 @@ class ChatController extends State final content = {...event.content}; try { text = await Translator.translate( - text, PlatformDispatcher.instance.locale.languageCode); + text, + PlatformDispatcher.instance.locale.languageCode, + ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(L10n.of(context).errorTranslatingMessage)), @@ -867,19 +893,24 @@ class ChatController extends State content['body'] = text; } content['xyz.extera.translated'] = true; - Navigator.of(context).push(new MaterialPageRoute( + Navigator.of(context).push( + new MaterialPageRoute( builder: (BuildContext ctx) { return TranslatedEventDialog( - event: new Event( - content: content, - type: 'm.room.message', - eventId: event.eventId, - senderId: event.senderId, - originServerTs: event.originServerTs, - room: room), - timeline: timeline!); + event: new Event( + content: content, + type: 'm.room.message', + eventId: event.eventId, + senderId: event.senderId, + originServerTs: event.originServerTs, + room: room, + ), + timeline: timeline!, + ); }, - fullscreenDialog: true)); + fullscreenDialog: true, + ), + ); } void reportEventAction() async { diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 178dc72..f807fd4 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -39,6 +39,7 @@ class ChatEventList extends StatelessWidget { final events = timeline.events.filterByVisibleInGui().filterByThreaded(controller.thread != null); final animateInEventIndex = controller.animateInEventIndex; + final threads = controller.room.threads; // create a map of eventId --> index to greatly improve performance of // ListView's findChildIndexCallback @@ -121,8 +122,8 @@ class ChatEventList extends StatelessWidget { timeline.events.length > animateInEventIndex && event == timeline.events[animateInEventIndex]; - final thread = (controller.threads?.containsKey(event.eventId) ?? false) - ? controller.threads![event.eventId] + final thread = threads.containsKey(event.eventId) + ? threads[event.eventId] : null; return AutoScrollTag( diff --git a/lib/pages/chat/events/poll_content.dart b/lib/pages/chat/events/poll_content.dart index 579f63e..33fb26b 100644 --- a/lib/pages/chat/events/poll_content.dart +++ b/lib/pages/chat/events/poll_content.dart @@ -58,8 +58,12 @@ class PollWidgetState extends State { final rel = await Matrix.of(context) .client - .getRelatingEventsWithRelTypeAndEventType(room.id, widget.event.eventId, - "m.reference", "org.matrix.msc3381.poll.response"); + .getRelatingEventsWithRelTypeAndEventType( + room.id, + widget.event.eventId, + "m.reference", + "org.matrix.msc3381.poll.response", + ); // Get all poll response events for this poll final responses = rel.chunk; @@ -90,8 +94,12 @@ class PollWidgetState extends State { final rel = await Matrix.of(context) .client - .getRelatingEventsWithRelTypeAndEventType(room.id, pollEventId, - "m.reference", "org.matrix.msc3381.poll.response"); + .getRelatingEventsWithRelTypeAndEventType( + room.id, + pollEventId, + "m.reference", + "org.matrix.msc3381.poll.response", + ); // Get all poll response events for this poll final responses = rel.chunk; @@ -124,15 +132,18 @@ class PollWidgetState extends State { final room = widget.event.room; // Send poll response event - await room.sendEvent({ - 'm.relates_to': { - 'rel_type': 'm.reference', - 'event_id': widget.event.eventId, + await room.sendEvent( + { + 'm.relates_to': { + 'rel_type': 'm.reference', + 'event_id': widget.event.eventId, + }, + 'org.matrix.msc3381.poll.response': { + 'answers': answers, + }, }, - 'org.matrix.msc3381.poll.response': { - 'answers': answers, - }, - }, type: 'org.matrix.msc3381.poll.response'); + type: 'org.matrix.msc3381.poll.response', + ); setState(() { selectedAnswers = answers; @@ -369,9 +380,11 @@ class PollWidgetState extends State { height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) - : Text(hasVoted - ? L10n.of(context).changeVote - : L10n.of(context).vote), + : Text( + hasVoted + ? L10n.of(context).changeVote + : L10n.of(context).vote, + ), ), const Spacer(), From 1757a7a4afff67d87a3d9a7045b2aee997fa079d Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 11:06:06 +0500 Subject: [PATCH 10/22] forgot --- .../reports/problems/problems-report.html | 2 +- assets/l10n/intl_en.arb | 3 + assets/l10n/intl_ru.arb | 5 +- lib/config/routes.dart | 10 +- lib/generated/l10n/l10n.dart | 12 ++ lib/generated/l10n/l10n_ar.dart | 6 + lib/generated/l10n/l10n_be.dart | 6 + lib/generated/l10n/l10n_bn.dart | 6 + lib/generated/l10n/l10n_bo.dart | 6 + lib/generated/l10n/l10n_ca.dart | 6 + lib/generated/l10n/l10n_cs.dart | 6 + lib/generated/l10n/l10n_de.dart | 6 + lib/generated/l10n/l10n_el.dart | 6 + lib/generated/l10n/l10n_en.dart | 6 + lib/generated/l10n/l10n_eo.dart | 6 + lib/generated/l10n/l10n_es.dart | 6 + lib/generated/l10n/l10n_et.dart | 6 + lib/generated/l10n/l10n_eu.dart | 6 + lib/generated/l10n/l10n_fa.dart | 6 + lib/generated/l10n/l10n_fi.dart | 6 + lib/generated/l10n/l10n_fil.dart | 6 + lib/generated/l10n/l10n_fr.dart | 6 + lib/generated/l10n/l10n_ga.dart | 6 + lib/generated/l10n/l10n_gl.dart | 6 + lib/generated/l10n/l10n_he.dart | 6 + lib/generated/l10n/l10n_hi.dart | 6 + lib/generated/l10n/l10n_hr.dart | 6 + lib/generated/l10n/l10n_hu.dart | 6 + lib/generated/l10n/l10n_ia.dart | 6 + lib/generated/l10n/l10n_id.dart | 6 + lib/generated/l10n/l10n_ie.dart | 6 + lib/generated/l10n/l10n_it.dart | 6 + lib/generated/l10n/l10n_ja.dart | 6 + lib/generated/l10n/l10n_ka.dart | 6 + lib/generated/l10n/l10n_ko.dart | 6 + lib/generated/l10n/l10n_lt.dart | 6 + lib/generated/l10n/l10n_lv.dart | 6 + lib/generated/l10n/l10n_nb.dart | 6 + lib/generated/l10n/l10n_nl.dart | 6 + lib/generated/l10n/l10n_pl.dart | 6 + lib/generated/l10n/l10n_pt.dart | 6 + lib/generated/l10n/l10n_ro.dart | 6 + lib/generated/l10n/l10n_ru.dart | 8 +- lib/generated/l10n/l10n_sk.dart | 6 + lib/generated/l10n/l10n_sl.dart | 6 + lib/generated/l10n/l10n_sr.dart | 6 + lib/generated/l10n/l10n_sv.dart | 6 + lib/generated/l10n/l10n_ta.dart | 6 + lib/generated/l10n/l10n_te.dart | 6 + lib/generated/l10n/l10n_th.dart | 6 + lib/generated/l10n/l10n_tr.dart | 6 + lib/generated/l10n/l10n_uk.dart | 6 + lib/generated/l10n/l10n_vi.dart | 6 + lib/generated/l10n/l10n_zh.dart | 6 + lib/pages/chat/chat_event_list.dart | 2 +- lib/pages/chat/chat_view.dart | 4 +- lib/pages/chat/translated_event_dialog.dart | 61 +++++----- lib/pages/chat_details/chat_details.dart | 1 + lib/pages/chat_details/chat_details_view.dart | 17 ++- lib/pages/chat_thread/chat_threads.dart | 53 +++++++++ lib/pages/chat_thread/chat_threads_view.dart | 110 ++++++++++++++++++ lib/pages/{thread => chat_thread}/thread.dart | 1 - .../filtered_timeline_extension.dart | 4 + 63 files changed, 540 insertions(+), 41 deletions(-) create mode 100644 lib/pages/chat_thread/chat_threads.dart create mode 100644 lib/pages/chat_thread/chat_threads_view.dart rename lib/pages/{thread => chat_thread}/thread.dart (97%) diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html index f85479d..e923c4a 100644 --- a/android/build/reports/problems/problems-report.html +++ b/android/build/reports/problems/problems-report.html @@ -650,7 +650,7 @@ code + .copy-button { diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index e1e6aa1..c14f47d 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -894,6 +894,9 @@ }, "chatPermissions": "Chat permissions", "@chatPermissions": {}, + "chatThreads": "Threads", + "@chatThreads": {}, + "chatThreadsDescription": "See all threads in this room", "editDisplayname": "Edit displayname", "@editDisplayname": { "type": "String", diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index 8e50924..af8a974 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -893,6 +893,9 @@ }, "chatPermissions": "Права в чате", "@chatPermissions": {}, + "chatThreads": "Обсуждения", + "@chatThreads": {}, + "chatThreadsDescription": "Список всех обсуждений в этой комнате", "editDisplayname": "Отображаемое имя", "@editDisplayname": { "type": "String", @@ -937,7 +940,7 @@ "@globalChatId": {}, "accessAndVisibility": "Доступность и видимость", "@accessAndVisibility": {}, - "accessAndVisibilityDescription": "Кто может зайти и как найти этот чат.", + "accessAndVisibilityDescription": "Кто может зайти и как найти этот чат", "@accessAndVisibilityDescription": {}, "calls": "Звонки", "@calls": {}, diff --git a/lib/config/routes.dart b/lib/config/routes.dart index ed84b31..2841c06 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,6 +1,7 @@ import 'dart:async'; -import 'package:extera_next/pages/thread/thread.dart'; +import 'package:extera_next/pages/chat_thread/chat_threads.dart'; +import 'package:extera_next/pages/chat_thread/thread.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -152,6 +153,13 @@ abstract class AppRoutes { routes: [ GoRoute( path: 'threads', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + ChatThreads( + roomId: state.pathParameters['roomid']!, + ), + ), redirect: loggedOutRedirect, routes: [ GoRoute( diff --git a/lib/generated/l10n/l10n.dart b/lib/generated/l10n/l10n.dart index 0142cb1..7d1a5ea 100644 --- a/lib/generated/l10n/l10n.dart +++ b/lib/generated/l10n/l10n.dart @@ -1317,6 +1317,18 @@ abstract class L10n { /// **'Chat permissions'** String get chatPermissions; + /// No description provided for @chatThreads. + /// + /// In en, this message translates to: + /// **'Threads'** + String get chatThreads; + + /// No description provided for @chatThreadsDescription. + /// + /// In en, this message translates to: + /// **'See all threads in this room'** + String get chatThreadsDescription; + /// No description provided for @editDisplayname. /// /// In en, this message translates to: diff --git a/lib/generated/l10n/l10n_ar.dart b/lib/generated/l10n/l10n_ar.dart index 1dca7be..129d998 100644 --- a/lib/generated/l10n/l10n_ar.dart +++ b/lib/generated/l10n/l10n_ar.dart @@ -659,6 +659,12 @@ class L10nAr extends L10n { @override String get chatPermissions => 'صلاحيات المحادثة'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'حرر الاسم العلني'; diff --git a/lib/generated/l10n/l10n_be.dart b/lib/generated/l10n/l10n_be.dart index e56739a..47ea2b6 100644 --- a/lib/generated/l10n/l10n_be.dart +++ b/lib/generated/l10n/l10n_be.dart @@ -663,6 +663,12 @@ class L10nBe extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_bn.dart b/lib/generated/l10n/l10n_bn.dart index 9999e47..bbd7340 100644 --- a/lib/generated/l10n/l10n_bn.dart +++ b/lib/generated/l10n/l10n_bn.dart @@ -663,6 +663,12 @@ class L10nBn extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_bo.dart b/lib/generated/l10n/l10n_bo.dart index 218b960..5ca3f14 100644 --- a/lib/generated/l10n/l10n_bo.dart +++ b/lib/generated/l10n/l10n_bo.dart @@ -663,6 +663,12 @@ class L10nBo extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_ca.dart b/lib/generated/l10n/l10n_ca.dart index ceadb89..16bb4b1 100644 --- a/lib/generated/l10n/l10n_ca.dart +++ b/lib/generated/l10n/l10n_ca.dart @@ -668,6 +668,12 @@ class L10nCa extends L10n { @override String get chatPermissions => 'Permisos del xat'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edita l\'àlies'; diff --git a/lib/generated/l10n/l10n_cs.dart b/lib/generated/l10n/l10n_cs.dart index 1c440b6..bd2a35c 100644 --- a/lib/generated/l10n/l10n_cs.dart +++ b/lib/generated/l10n/l10n_cs.dart @@ -667,6 +667,12 @@ class L10nCs extends L10n { @override String get chatPermissions => 'Oprávnění konverzace'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Změnit přezdívku'; diff --git a/lib/generated/l10n/l10n_de.dart b/lib/generated/l10n/l10n_de.dart index 08b344f..4a353ff 100644 --- a/lib/generated/l10n/l10n_de.dart +++ b/lib/generated/l10n/l10n_de.dart @@ -669,6 +669,12 @@ class L10nDe extends L10n { @override String get chatPermissions => 'Chatberechtigungen'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Anzeigename ändern'; diff --git a/lib/generated/l10n/l10n_el.dart b/lib/generated/l10n/l10n_el.dart index c01c3a3..18d28c0 100644 --- a/lib/generated/l10n/l10n_el.dart +++ b/lib/generated/l10n/l10n_el.dart @@ -664,6 +664,12 @@ class L10nEl extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_en.dart b/lib/generated/l10n/l10n_en.dart index bfc5779..04c491b 100644 --- a/lib/generated/l10n/l10n_en.dart +++ b/lib/generated/l10n/l10n_en.dart @@ -663,6 +663,12 @@ class L10nEn extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_eo.dart b/lib/generated/l10n/l10n_eo.dart index d1be07f..62a3a8a 100644 --- a/lib/generated/l10n/l10n_eo.dart +++ b/lib/generated/l10n/l10n_eo.dart @@ -666,6 +666,12 @@ class L10nEo extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Redakti prezentan nomon'; diff --git a/lib/generated/l10n/l10n_es.dart b/lib/generated/l10n/l10n_es.dart index 45b37ea..e80a010 100644 --- a/lib/generated/l10n/l10n_es.dart +++ b/lib/generated/l10n/l10n_es.dart @@ -667,6 +667,12 @@ class L10nEs extends L10n { @override String get chatPermissions => 'Permisos del chat'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Editar nombre visible'; diff --git a/lib/generated/l10n/l10n_et.dart b/lib/generated/l10n/l10n_et.dart index 108e526..afda2ec 100644 --- a/lib/generated/l10n/l10n_et.dart +++ b/lib/generated/l10n/l10n_et.dart @@ -665,6 +665,12 @@ class L10nEt extends L10n { @override String get chatPermissions => 'Vestluse õigused'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Muuda kuvatavat nime'; diff --git a/lib/generated/l10n/l10n_eu.dart b/lib/generated/l10n/l10n_eu.dart index 5ed11ca..a4c5eb4 100644 --- a/lib/generated/l10n/l10n_eu.dart +++ b/lib/generated/l10n/l10n_eu.dart @@ -666,6 +666,12 @@ class L10nEu extends L10n { @override String get chatPermissions => 'Txataren baimenak'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Editatu ezizena'; diff --git a/lib/generated/l10n/l10n_fa.dart b/lib/generated/l10n/l10n_fa.dart index e41eaf6..a796961 100644 --- a/lib/generated/l10n/l10n_fa.dart +++ b/lib/generated/l10n/l10n_fa.dart @@ -661,6 +661,12 @@ class L10nFa extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'ویرایش نام نمایشی'; diff --git a/lib/generated/l10n/l10n_fi.dart b/lib/generated/l10n/l10n_fi.dart index c786c02..0accfcc 100644 --- a/lib/generated/l10n/l10n_fi.dart +++ b/lib/generated/l10n/l10n_fi.dart @@ -668,6 +668,12 @@ class L10nFi extends L10n { @override String get chatPermissions => 'Keskustelun oikeudet'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Muokkaa näyttönimeä'; diff --git a/lib/generated/l10n/l10n_fil.dart b/lib/generated/l10n/l10n_fil.dart index 72e0637..20010ed 100644 --- a/lib/generated/l10n/l10n_fil.dart +++ b/lib/generated/l10n/l10n_fil.dart @@ -669,6 +669,12 @@ class L10nFil extends L10n { @override String get chatPermissions => 'Mga pahintulot ng chat'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'I-edit ang display name'; diff --git a/lib/generated/l10n/l10n_fr.dart b/lib/generated/l10n/l10n_fr.dart index eb38cff..33e4d5a 100644 --- a/lib/generated/l10n/l10n_fr.dart +++ b/lib/generated/l10n/l10n_fr.dart @@ -672,6 +672,12 @@ class L10nFr extends L10n { @override String get chatPermissions => 'Permissions du salon'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Changer de nom d\'affichage'; diff --git a/lib/generated/l10n/l10n_ga.dart b/lib/generated/l10n/l10n_ga.dart index 6c55850..94b2051 100644 --- a/lib/generated/l10n/l10n_ga.dart +++ b/lib/generated/l10n/l10n_ga.dart @@ -671,6 +671,12 @@ class L10nGa extends L10n { @override String get chatPermissions => 'Ceadanna comhrá'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Cuir ainm taispeána in eagar'; diff --git a/lib/generated/l10n/l10n_gl.dart b/lib/generated/l10n/l10n_gl.dart index 5d1caec..1f4e199 100644 --- a/lib/generated/l10n/l10n_gl.dart +++ b/lib/generated/l10n/l10n_gl.dart @@ -666,6 +666,12 @@ class L10nGl extends L10n { @override String get chatPermissions => 'Permisos da conversa'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Editar nome público'; diff --git a/lib/generated/l10n/l10n_he.dart b/lib/generated/l10n/l10n_he.dart index 9bae162..3d32ec7 100644 --- a/lib/generated/l10n/l10n_he.dart +++ b/lib/generated/l10n/l10n_he.dart @@ -661,6 +661,12 @@ class L10nHe extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'ערוך את שם התצוגה'; diff --git a/lib/generated/l10n/l10n_hi.dart b/lib/generated/l10n/l10n_hi.dart index fc80631..a12d291 100644 --- a/lib/generated/l10n/l10n_hi.dart +++ b/lib/generated/l10n/l10n_hi.dart @@ -663,6 +663,12 @@ class L10nHi extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_hr.dart b/lib/generated/l10n/l10n_hr.dart index 9187316..02d5df4 100644 --- a/lib/generated/l10n/l10n_hr.dart +++ b/lib/generated/l10n/l10n_hr.dart @@ -664,6 +664,12 @@ class L10nHr extends L10n { @override String get chatPermissions => 'Dozvole za razgovor'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Uredi prikazano ime'; diff --git a/lib/generated/l10n/l10n_hu.dart b/lib/generated/l10n/l10n_hu.dart index 0485832..bb01c3e 100644 --- a/lib/generated/l10n/l10n_hu.dart +++ b/lib/generated/l10n/l10n_hu.dart @@ -666,6 +666,12 @@ class L10nHu extends L10n { @override String get chatPermissions => 'Csevegés engedélyek'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Megjelenítési név szerkesztése'; diff --git a/lib/generated/l10n/l10n_ia.dart b/lib/generated/l10n/l10n_ia.dart index 8ea9d92..c3fed88 100644 --- a/lib/generated/l10n/l10n_ia.dart +++ b/lib/generated/l10n/l10n_ia.dart @@ -663,6 +663,12 @@ class L10nIa extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_id.dart b/lib/generated/l10n/l10n_id.dart index e0c5964..12c4c3b 100644 --- a/lib/generated/l10n/l10n_id.dart +++ b/lib/generated/l10n/l10n_id.dart @@ -670,6 +670,12 @@ class L10nId extends L10n { @override String get chatPermissions => 'Perizinan obrolan'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit nama tampilan'; diff --git a/lib/generated/l10n/l10n_ie.dart b/lib/generated/l10n/l10n_ie.dart index 1074ddb..7d3b20a 100644 --- a/lib/generated/l10n/l10n_ie.dart +++ b/lib/generated/l10n/l10n_ie.dart @@ -662,6 +662,12 @@ class L10nIe extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Redacter li visibil nómine'; diff --git a/lib/generated/l10n/l10n_it.dart b/lib/generated/l10n/l10n_it.dart index eb05dec..2be51c0 100644 --- a/lib/generated/l10n/l10n_it.dart +++ b/lib/generated/l10n/l10n_it.dart @@ -667,6 +667,12 @@ class L10nIt extends L10n { @override String get chatPermissions => 'Permessi della chat'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Modifica il nominativo'; diff --git a/lib/generated/l10n/l10n_ja.dart b/lib/generated/l10n/l10n_ja.dart index 04a1d22..e3fe6bb 100644 --- a/lib/generated/l10n/l10n_ja.dart +++ b/lib/generated/l10n/l10n_ja.dart @@ -654,6 +654,12 @@ class L10nJa extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => '表示名を編集'; diff --git a/lib/generated/l10n/l10n_ka.dart b/lib/generated/l10n/l10n_ka.dart index 53c423f..3010bdd 100644 --- a/lib/generated/l10n/l10n_ka.dart +++ b/lib/generated/l10n/l10n_ka.dart @@ -665,6 +665,12 @@ class L10nKa extends L10n { @override String get chatPermissions => 'ჩატის უფლებები'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'ნაჩვენები სახელის შეცვლა'; diff --git a/lib/generated/l10n/l10n_ko.dart b/lib/generated/l10n/l10n_ko.dart index 92b8286..7b80318 100644 --- a/lib/generated/l10n/l10n_ko.dart +++ b/lib/generated/l10n/l10n_ko.dart @@ -652,6 +652,12 @@ class L10nKo extends L10n { @override String get chatPermissions => '채팅 권한'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => '표시 이름 수정'; diff --git a/lib/generated/l10n/l10n_lt.dart b/lib/generated/l10n/l10n_lt.dart index 28362b4..0a81214 100644 --- a/lib/generated/l10n/l10n_lt.dart +++ b/lib/generated/l10n/l10n_lt.dart @@ -665,6 +665,12 @@ class L10nLt extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Redaguoti rodomą vardą'; diff --git a/lib/generated/l10n/l10n_lv.dart b/lib/generated/l10n/l10n_lv.dart index b3417a4..5b32b8a 100644 --- a/lib/generated/l10n/l10n_lv.dart +++ b/lib/generated/l10n/l10n_lv.dart @@ -669,6 +669,12 @@ class L10nLv extends L10n { @override String get chatPermissions => 'Tērzēšanas atļaujas'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Labot attēlojamo vārdu'; diff --git a/lib/generated/l10n/l10n_nb.dart b/lib/generated/l10n/l10n_nb.dart index d6fc856..e4df751 100644 --- a/lib/generated/l10n/l10n_nb.dart +++ b/lib/generated/l10n/l10n_nb.dart @@ -663,6 +663,12 @@ class L10nNb extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Rediger visningsnavn'; diff --git a/lib/generated/l10n/l10n_nl.dart b/lib/generated/l10n/l10n_nl.dart index af0eceb..e4be809 100644 --- a/lib/generated/l10n/l10n_nl.dart +++ b/lib/generated/l10n/l10n_nl.dart @@ -665,6 +665,12 @@ class L10nNl extends L10n { @override String get chatPermissions => 'Chat toestemmingen'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Naam wijzigen'; diff --git a/lib/generated/l10n/l10n_pl.dart b/lib/generated/l10n/l10n_pl.dart index a8791df..0d1522d 100644 --- a/lib/generated/l10n/l10n_pl.dart +++ b/lib/generated/l10n/l10n_pl.dart @@ -666,6 +666,12 @@ class L10nPl extends L10n { @override String get chatPermissions => 'Uprawnienia w czacie'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edytuj nazwę wyświetlaną'; diff --git a/lib/generated/l10n/l10n_pt.dart b/lib/generated/l10n/l10n_pt.dart index aae1a03..9ac3270 100644 --- a/lib/generated/l10n/l10n_pt.dart +++ b/lib/generated/l10n/l10n_pt.dart @@ -663,6 +663,12 @@ class L10nPt extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_ro.dart b/lib/generated/l10n/l10n_ro.dart index 589e285..27570ba 100644 --- a/lib/generated/l10n/l10n_ro.dart +++ b/lib/generated/l10n/l10n_ro.dart @@ -668,6 +668,12 @@ class L10nRo extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Schimbați displayname'; diff --git a/lib/generated/l10n/l10n_ru.dart b/lib/generated/l10n/l10n_ru.dart index 6ee67f0..b3cab54 100644 --- a/lib/generated/l10n/l10n_ru.dart +++ b/lib/generated/l10n/l10n_ru.dart @@ -665,6 +665,12 @@ class L10nRu extends L10n { @override String get chatPermissions => 'Права в чате'; + @override + String get chatThreads => 'Обсуждения'; + + @override + String get chatThreadsDescription => 'Список всех обсуждений в этой комнате'; + @override String get editDisplayname => 'Отображаемое имя'; @@ -698,7 +704,7 @@ class L10nRu extends L10n { @override String get accessAndVisibilityDescription => - 'Кто может зайти и как найти этот чат.'; + 'Кто может зайти и как найти этот чат'; @override String get calls => 'Звонки'; diff --git a/lib/generated/l10n/l10n_sk.dart b/lib/generated/l10n/l10n_sk.dart index 33f6390..eaf5a91 100644 --- a/lib/generated/l10n/l10n_sk.dart +++ b/lib/generated/l10n/l10n_sk.dart @@ -665,6 +665,12 @@ class L10nSk extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Zmeniť prezývku'; diff --git a/lib/generated/l10n/l10n_sl.dart b/lib/generated/l10n/l10n_sl.dart index e12d910..36b0412 100644 --- a/lib/generated/l10n/l10n_sl.dart +++ b/lib/generated/l10n/l10n_sl.dart @@ -667,6 +667,12 @@ class L10nSl extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_sr.dart b/lib/generated/l10n/l10n_sr.dart index 9928fe1..ed06706 100644 --- a/lib/generated/l10n/l10n_sr.dart +++ b/lib/generated/l10n/l10n_sr.dart @@ -663,6 +663,12 @@ class L10nSr extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Уреди име за приказ'; diff --git a/lib/generated/l10n/l10n_sv.dart b/lib/generated/l10n/l10n_sv.dart index 6b67f5c..72be4e4 100644 --- a/lib/generated/l10n/l10n_sv.dart +++ b/lib/generated/l10n/l10n_sv.dart @@ -664,6 +664,12 @@ class L10nSv extends L10n { @override String get chatPermissions => 'Chatt-behörigheter'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Ändra visningsnamn'; diff --git a/lib/generated/l10n/l10n_ta.dart b/lib/generated/l10n/l10n_ta.dart index 34d65dd..2406198 100644 --- a/lib/generated/l10n/l10n_ta.dart +++ b/lib/generated/l10n/l10n_ta.dart @@ -673,6 +673,12 @@ class L10nTa extends L10n { @override String get chatPermissions => 'அரட்டை அனுமதிகள்'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'காட்சி பெயர் திருத்து'; diff --git a/lib/generated/l10n/l10n_te.dart b/lib/generated/l10n/l10n_te.dart index 5822fb2..64c73fb 100644 --- a/lib/generated/l10n/l10n_te.dart +++ b/lib/generated/l10n/l10n_te.dart @@ -663,6 +663,12 @@ class L10nTe extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_th.dart b/lib/generated/l10n/l10n_th.dart index 6361640..ea57c24 100644 --- a/lib/generated/l10n/l10n_th.dart +++ b/lib/generated/l10n/l10n_th.dart @@ -665,6 +665,12 @@ class L10nTh extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Edit displayname'; diff --git a/lib/generated/l10n/l10n_tr.dart b/lib/generated/l10n/l10n_tr.dart index f5c32a3..91dbd9d 100644 --- a/lib/generated/l10n/l10n_tr.dart +++ b/lib/generated/l10n/l10n_tr.dart @@ -665,6 +665,12 @@ class L10nTr extends L10n { @override String get chatPermissions => 'Sohbet izinleri'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Görünen adı düzenle'; diff --git a/lib/generated/l10n/l10n_uk.dart b/lib/generated/l10n/l10n_uk.dart index 60aa8ac..4964ed8 100644 --- a/lib/generated/l10n/l10n_uk.dart +++ b/lib/generated/l10n/l10n_uk.dart @@ -667,6 +667,12 @@ class L10nUk extends L10n { @override String get chatPermissions => 'Дозволи бесіди'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Змінити показуване ім\'я'; diff --git a/lib/generated/l10n/l10n_vi.dart b/lib/generated/l10n/l10n_vi.dart index 1ac0e9d..1bcb6d4 100644 --- a/lib/generated/l10n/l10n_vi.dart +++ b/lib/generated/l10n/l10n_vi.dart @@ -663,6 +663,12 @@ class L10nVi extends L10n { @override String get chatPermissions => 'Chat permissions'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => 'Sửa tên hiển thị'; diff --git a/lib/generated/l10n/l10n_zh.dart b/lib/generated/l10n/l10n_zh.dart index ef91fd3..0550d1a 100644 --- a/lib/generated/l10n/l10n_zh.dart +++ b/lib/generated/l10n/l10n_zh.dart @@ -647,6 +647,12 @@ class L10nZh extends L10n { @override String get chatPermissions => '聊天权限'; + @override + String get chatThreads => 'Threads'; + + @override + String get chatThreadsDescription => 'See all threads in this room'; + @override String get editDisplayname => '编辑昵称'; diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index f807fd4..54f2a14 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -15,7 +15,7 @@ import 'package:extera_next/utils/platform_infos.dart'; class ChatEventList extends StatelessWidget { final ChatController controller; - + const ChatEventList({ super.key, required this.controller, diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 7cda110..76cab5d 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -328,7 +328,9 @@ class ChatView extends StatelessWidget { Expanded( child: GestureDetector( onTap: controller.clearSingleSelectedEvent, - child: ChatEventList(controller: controller), + child: ChatEventList( + controller: controller, + ), ), ), if (controller.showScrollDownButton) diff --git a/lib/pages/chat/translated_event_dialog.dart b/lib/pages/chat/translated_event_dialog.dart index 337701f..7b9f399 100644 --- a/lib/pages/chat/translated_event_dialog.dart +++ b/lib/pages/chat/translated_event_dialog.dart @@ -16,14 +16,13 @@ class TranslatedEventDialog extends StatefulWidget { }); @override - TranslatedEventDialogState createState() => - TranslatedEventDialogState(event, timeline); + TranslatedEventDialogState createState() => TranslatedEventDialogState(); } class TranslatedEventDialogState extends State { - final Event event; - final Timeline timeline; - TranslatedEventDialogState(this.event, this.timeline); + Event get event => widget.event; + Timeline get timeline => widget.timeline; + TranslatedEventDialogState(); @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -33,35 +32,31 @@ class TranslatedEventDialogState extends State { theme.bubbleColor, ]; - final message = Message( - event, - colors: colors, - onInfoTab: (Event ev) => {}, - onMention: () => {}, - onSelect: (Event ev) => {}, - onSwipe: () => {}, - scrollToEventId: (String p0) => {}, - timeline: timeline, - animateIn: false, - displayReadMarker: false, - highlightMarker: false, - longPressSelect: false, - selected: false, - wallpaperMode: false, - gradient: false - ); + final message = Message( + event, + colors: colors, + onInfoTab: (Event ev) => {}, + onMention: () => {}, + onSelect: (Event ev) => {}, + onSwipe: () => {}, + scrollToEventId: (String p0) => {}, + timeline: timeline, + animateIn: false, + displayReadMarker: false, + highlightMarker: false, + longPressSelect: false, + selected: false, + wallpaperMode: false, + gradient: false, + ); return Scaffold( - appBar: AppBar( - title: Text(L10n.of(context).translatedMessage) - ), - body: Container( - child: Column( - children: [ - message, - ], - ), - ), - ); + appBar: AppBar(title: Text(L10n.of(context).translatedMessage)), + body: Column( + children: [ + message, + ], + ), + ); } } diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index a0efa8c..c3cc32c 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -1,3 +1,4 @@ +import 'package:extera_next/pages/chat_thread/chat_threads_view.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index f5f4ccc..e990f40 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -259,6 +259,21 @@ class ChatDetailsView extends StatelessWidget { const SizedBox(height: 16), ], Divider(color: theme.dividerColor), + ListTile( + leading: CircleAvatar( + backgroundColor: theme.scaffoldBackgroundColor, + foregroundColor: iconColor, + child: const Icon( + Icons.chat_bubble_outline, + ), + ), + title: Text(L10n.of(context).chatThreads), + subtitle: + Text(L10n.of(context).chatThreadsDescription), + onTap: () => + context.push('/rooms/${room.id}/threads'), + trailing: const Icon(Icons.chevron_right_outlined), + ), ListTile( leading: CircleAvatar( backgroundColor: theme.scaffoldBackgroundColor, @@ -360,4 +375,4 @@ class ChatDetailsView extends StatelessWidget { }, ); } -} \ No newline at end of file +} diff --git a/lib/pages/chat_thread/chat_threads.dart b/lib/pages/chat_thread/chat_threads.dart new file mode 100644 index 0000000..ff0d001 --- /dev/null +++ b/lib/pages/chat_thread/chat_threads.dart @@ -0,0 +1,53 @@ +import 'package:extera_next/pages/chat_thread/chat_threads_view.dart'; +import 'package:extera_next/widgets/matrix.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:matrix/matrix.dart'; +import 'package:scroll_to_index/scroll_to_index.dart'; + +class ChatThreads extends StatefulWidget { + final String roomId; + + const ChatThreads({ + super.key, + required this.roomId, + }); + + @override + ChatThreadsController createState() => ChatThreadsController(); +} + +class ChatThreadsController extends State { + String get roomId => widget.roomId; + Room? get room => Matrix.of(context).client.getRoomById(roomId); + + bool isLoadingThreads = false; + + final AutoScrollController scrollController = AutoScrollController(); + + @override + Widget build(BuildContext context) => ChatThreadsView(this); + + void loadThreads([dynamic _]) async { + final room = Matrix.of(context).client.getRoomById(roomId); + + if (room == null) { + return; + } + + isLoadingThreads = true; + + await room.loadThreadsFromServer(); + + isLoadingThreads = false; + } + + List? get threads => room?.threads.values.toList(); + + Stream get onChanged => Matrix.of(context).client.onSync.stream.where( + (e) => + (e.rooms?.join?.containsKey(roomId) ?? false) && + (e.rooms!.join![roomId]?.timeline?.events + ?.any((s) => s.type == EventTypes.Message && s.content['m.relates_to'] != null) ?? + false), + ); +} \ No newline at end of file diff --git a/lib/pages/chat_thread/chat_threads_view.dart b/lib/pages/chat_thread/chat_threads_view.dart new file mode 100644 index 0000000..15ef1d2 --- /dev/null +++ b/lib/pages/chat_thread/chat_threads_view.dart @@ -0,0 +1,110 @@ +import 'package:extera_next/config/themes.dart'; +import 'package:extera_next/generated/l10n/l10n.dart'; +import 'package:extera_next/pages/chat_thread/chat_threads.dart'; +import 'package:extera_next/utils/platform_infos.dart'; +import 'package:extera_next/widgets/avatar.dart'; +import 'package:extera_next/widgets/layouts/max_width_body.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; +import 'package:scroll_to_index/scroll_to_index.dart'; + +class ChatThreadsView extends StatelessWidget { + final ChatThreadsController controller; + + const ChatThreadsView(this.controller, {super.key}); + + @override + Widget build(BuildContext context) { + final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; + + return Scaffold( + appBar: AppBar( + leading: const Center(child: BackButton()), + title: Text(L10n.of(context).chatThreads), + ), + body: MaxWidthBody( + child: ListView.custom( + padding: EdgeInsets.only( + top: 16, + bottom: 8, + left: horizontalPadding, + right: horizontalPadding, + ), + reverse: true, + controller: controller.scrollController, + keyboardDismissBehavior: PlatformInfos.isIOS + ? ScrollViewKeyboardDismissBehavior.onDrag + : ScrollViewKeyboardDismissBehavior.manual, + childrenDelegate: SliverChildBuilderDelegate( + (BuildContext context, int i) { + if (i == (controller.threads?.length ?? 0) + 1) { + if (controller.isLoadingThreads) { + return const Center( + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ); + } else if (!(controller.room?.loadedAllThreads ?? false)) { + return Builder( + builder: (context) { + WidgetsBinding.instance + .addPostFrameCallback(controller.loadThreads); + return Center( + child: IconButton( + onPressed: controller.loadThreads, + icon: const Icon(Icons.refresh_outlined), + ), + ); + }, + ); + } + i--; + + final thread = controller.threads![i]; + + return AutoScrollTag( + key: ValueKey(thread.rootEvent.eventId), + index: i, + controller: controller.scrollController, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FutureBuilder( + future: thread.rootEvent.fetchSenderUser(), + builder: (context, snapshot) { + final user = snapshot.data ?? + thread.rootEvent.senderFromMemoryOrFallback; + + return Avatar( + mxContent: user.avatarUrl, + name: user.calcDisplayname(), + size: 48, + ); + }, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(thread.rootEvent.senderFromMemoryOrFallback.calcDisplayname()), + ], + ), + ), + const SizedBox(height: 4.0), + Text(thread.rootEvent.text), + ], + ), + ), + ); + } + }, + childCount: (controller.threads?.length ?? 0) + 1, + ), + ), + ), + ); + } +} diff --git a/lib/pages/thread/thread.dart b/lib/pages/chat_thread/thread.dart similarity index 97% rename from lib/pages/thread/thread.dart rename to lib/pages/chat_thread/thread.dart index 4c032bf..b1f0bf4 100644 --- a/lib/pages/thread/thread.dart +++ b/lib/pages/chat_thread/thread.dart @@ -3,7 +3,6 @@ import 'package:extera_next/pages/chat/chat.dart'; import 'package:extera_next/widgets/matrix.dart'; import 'package:extera_next/widgets/share_scaffold_dialog.dart'; import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; class ThreadPage extends StatelessWidget { final String roomId; diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index 4732c56..b7340cf 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -8,6 +8,10 @@ extension VisibleInGuiExtension on List { return where((e) => e.isThreaded == threaded).toList(); } + List filterThreadRoots() { + return where((e) => e.room.threads.containsKey(e.eventId)).toList(); + } + List filterByVisibleInGui({String? exceptionEventId}) { final visibleEvents = where((e) => e.isVisibleInGui || e.eventId == exceptionEventId) From 0f5c19b0ce35c8c054018c10b3ae091957fa2c73 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 12:33:34 +0500 Subject: [PATCH 11/22] horrible implementation of thread list in room --- lib/config/routes.dart | 9 +- lib/pages/chat/chat.dart | 6 + lib/pages/chat/chat_event_list.dart | 11 +- lib/pages/chat/chat_view.dart | 4 +- lib/pages/chat_details/chat_details.dart | 1 - lib/pages/chat_details/chat_details_view.dart | 2 +- lib/pages/chat_thread/chat_threads.dart | 53 --------- lib/pages/chat_thread/chat_threads_view.dart | 110 ------------------ 8 files changed, 21 insertions(+), 175 deletions(-) delete mode 100644 lib/pages/chat_thread/chat_threads.dart delete mode 100644 lib/pages/chat_thread/chat_threads_view.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 2841c06..fa32d04 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:extera_next/pages/chat_thread/chat_threads.dart'; import 'package:extera_next/pages/chat_thread/thread.dart'; import 'package:flutter/material.dart'; @@ -153,13 +152,6 @@ abstract class AppRoutes { routes: [ GoRoute( path: 'threads', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - ChatThreads( - roomId: state.pathParameters['roomid']!, - ), - ), redirect: loggedOutRedirect, routes: [ GoRoute( @@ -377,6 +369,7 @@ abstract class AppRoutes { roomId: state.pathParameters['roomid']!, shareItems: shareItems, eventId: state.uri.queryParameters['event'], + showThreadRoots: state.uri.queryParameters['threads'] == 'true', ), ); }, diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index b6eef99..91e914a 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -51,12 +51,14 @@ class ChatPage extends StatelessWidget { final String roomId; final List? shareItems; final String? eventId; + final bool? showThreadRoots; const ChatPage({ super.key, required this.roomId, this.eventId, this.shareItems, + this.showThreadRoots, }); @override @@ -79,6 +81,7 @@ class ChatPage extends StatelessWidget { room: room, shareItems: shareItems, eventId: eventId, + showThreadRoots: showThreadRoots, ); } } @@ -88,6 +91,7 @@ class ChatPageWithRoom extends StatefulWidget { final Thread? thread; final List? shareItems; final String? eventId; + final bool? showThreadRoots; const ChatPageWithRoom({ super.key, @@ -95,6 +99,7 @@ class ChatPageWithRoom extends StatefulWidget { this.thread, this.shareItems, this.eventId, + this.showThreadRoots, }); @override @@ -104,6 +109,7 @@ class ChatPageWithRoom extends StatefulWidget { class ChatController extends State with WidgetsBindingObserver { Room get room => sendingClient.getRoomById(roomId) ?? widget.room; + bool get showThreadRoots => (widget.showThreadRoots ?? false); Thread? get thread => sendingClient.getRoomById(roomId)?.threads[threadRootEventId] ?? widget.room.threads[threadRootEventId]; diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 54f2a14..9fd9539 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -15,10 +15,12 @@ import 'package:extera_next/utils/platform_infos.dart'; class ChatEventList extends StatelessWidget { final ChatController controller; + final bool showThreadRoots; const ChatEventList({ super.key, required this.controller, + this.showThreadRoots = false, }); @override @@ -37,7 +39,14 @@ class ChatEventList extends StatelessWidget { final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - final events = timeline.events.filterByVisibleInGui().filterByThreaded(controller.thread != null); + var events = timeline.events.filterByVisibleInGui(); + + if (showThreadRoots) { + events = events.filterThreadRoots(); + } else { + events = events.filterByThreaded(controller.thread != null); + } + final animateInEventIndex = controller.animateInEventIndex; final threads = controller.room.threads; diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 76cab5d..f9d7c71 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -330,6 +330,7 @@ class ChatView extends StatelessWidget { onTap: controller.clearSingleSelectedEvent, child: ChatEventList( controller: controller, + showThreadRoots: controller.showThreadRoots, ), ), ), @@ -349,7 +350,8 @@ class ChatView extends StatelessWidget { ), ) else if (controller.room.canSendDefaultMessages && - controller.room.membership == Membership.join) + controller.room.membership == Membership.join && + !controller.showThreadRoots) Container( margin: EdgeInsets.all(bottomSheetPadding), constraints: const BoxConstraints( diff --git a/lib/pages/chat_details/chat_details.dart b/lib/pages/chat_details/chat_details.dart index c3cc32c..a0efa8c 100644 --- a/lib/pages/chat_details/chat_details.dart +++ b/lib/pages/chat_details/chat_details.dart @@ -1,4 +1,3 @@ -import 'package:extera_next/pages/chat_thread/chat_threads_view.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index e990f40..21df5fc 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -271,7 +271,7 @@ class ChatDetailsView extends StatelessWidget { subtitle: Text(L10n.of(context).chatThreadsDescription), onTap: () => - context.push('/rooms/${room.id}/threads'), + context.push('/rooms/${room.id}?threads=true'), trailing: const Icon(Icons.chevron_right_outlined), ), ListTile( diff --git a/lib/pages/chat_thread/chat_threads.dart b/lib/pages/chat_thread/chat_threads.dart deleted file mode 100644 index ff0d001..0000000 --- a/lib/pages/chat_thread/chat_threads.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:extera_next/pages/chat_thread/chat_threads_view.dart'; -import 'package:extera_next/widgets/matrix.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:matrix/matrix.dart'; -import 'package:scroll_to_index/scroll_to_index.dart'; - -class ChatThreads extends StatefulWidget { - final String roomId; - - const ChatThreads({ - super.key, - required this.roomId, - }); - - @override - ChatThreadsController createState() => ChatThreadsController(); -} - -class ChatThreadsController extends State { - String get roomId => widget.roomId; - Room? get room => Matrix.of(context).client.getRoomById(roomId); - - bool isLoadingThreads = false; - - final AutoScrollController scrollController = AutoScrollController(); - - @override - Widget build(BuildContext context) => ChatThreadsView(this); - - void loadThreads([dynamic _]) async { - final room = Matrix.of(context).client.getRoomById(roomId); - - if (room == null) { - return; - } - - isLoadingThreads = true; - - await room.loadThreadsFromServer(); - - isLoadingThreads = false; - } - - List? get threads => room?.threads.values.toList(); - - Stream get onChanged => Matrix.of(context).client.onSync.stream.where( - (e) => - (e.rooms?.join?.containsKey(roomId) ?? false) && - (e.rooms!.join![roomId]?.timeline?.events - ?.any((s) => s.type == EventTypes.Message && s.content['m.relates_to'] != null) ?? - false), - ); -} \ No newline at end of file diff --git a/lib/pages/chat_thread/chat_threads_view.dart b/lib/pages/chat_thread/chat_threads_view.dart deleted file mode 100644 index 15ef1d2..0000000 --- a/lib/pages/chat_thread/chat_threads_view.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:extera_next/config/themes.dart'; -import 'package:extera_next/generated/l10n/l10n.dart'; -import 'package:extera_next/pages/chat_thread/chat_threads.dart'; -import 'package:extera_next/utils/platform_infos.dart'; -import 'package:extera_next/widgets/avatar.dart'; -import 'package:extera_next/widgets/layouts/max_width_body.dart'; -import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; -import 'package:scroll_to_index/scroll_to_index.dart'; - -class ChatThreadsView extends StatelessWidget { - final ChatThreadsController controller; - - const ChatThreadsView(this.controller, {super.key}); - - @override - Widget build(BuildContext context) { - final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - - return Scaffold( - appBar: AppBar( - leading: const Center(child: BackButton()), - title: Text(L10n.of(context).chatThreads), - ), - body: MaxWidthBody( - child: ListView.custom( - padding: EdgeInsets.only( - top: 16, - bottom: 8, - left: horizontalPadding, - right: horizontalPadding, - ), - reverse: true, - controller: controller.scrollController, - keyboardDismissBehavior: PlatformInfos.isIOS - ? ScrollViewKeyboardDismissBehavior.onDrag - : ScrollViewKeyboardDismissBehavior.manual, - childrenDelegate: SliverChildBuilderDelegate( - (BuildContext context, int i) { - if (i == (controller.threads?.length ?? 0) + 1) { - if (controller.isLoadingThreads) { - return const Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ); - } else if (!(controller.room?.loadedAllThreads ?? false)) { - return Builder( - builder: (context) { - WidgetsBinding.instance - .addPostFrameCallback(controller.loadThreads); - return Center( - child: IconButton( - onPressed: controller.loadThreads, - icon: const Icon(Icons.refresh_outlined), - ), - ); - }, - ); - } - i--; - - final thread = controller.threads![i]; - - return AutoScrollTag( - key: ValueKey(thread.rootEvent.eventId), - index: i, - controller: controller.scrollController, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 8.0, - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FutureBuilder( - future: thread.rootEvent.fetchSenderUser(), - builder: (context, snapshot) { - final user = snapshot.data ?? - thread.rootEvent.senderFromMemoryOrFallback; - - return Avatar( - mxContent: user.avatarUrl, - name: user.calcDisplayname(), - size: 48, - ); - }, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(thread.rootEvent.senderFromMemoryOrFallback.calcDisplayname()), - ], - ), - ), - const SizedBox(height: 4.0), - Text(thread.rootEvent.text), - ], - ), - ), - ); - } - }, - childCount: (controller.threads?.length ?? 0) + 1, - ), - ), - ), - ); - } -} From 0a80312278f57d2c3688ca6cd4baaf1606843519 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 14:11:19 +0500 Subject: [PATCH 12/22] edits and reactions in threads, sendlocationdialog use thread --- lib/pages/chat/chat.dart | 23 ++++++++++++------- lib/pages/chat/chat_event_list.dart | 4 +++- lib/pages/chat/send_location_dialog.dart | 10 +++++++- .../filtered_timeline_extension.dart | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 91e914a..b6b09eb 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -455,9 +455,6 @@ class ChatController extends State eventContextId: eventContextId, onInsert: onInsert, ); - if (timeline is ThreadTimeline) { - (timeline as ThreadTimeline).getThreadEvents(); - } Logs().v("Thread timeline loaded"); } catch (e, s) { Logs().w( @@ -475,6 +472,9 @@ class ChatController extends State _showScrollUpMaterialBanner(eventContextId!); } } + if (timeline is ThreadTimeline) { + (timeline as ThreadTimeline).getThreadEvents(); + } } Future _getTimeline({ @@ -749,7 +749,8 @@ class ChatController extends State name: fileName ?? audioFile.path, ); - await room.sendFileEvent( + await room + .sendFileEvent( file, inReplyTo: replyEvent, extraContent: { @@ -763,7 +764,10 @@ class ChatController extends State 'waveform': waveform, }, }, - ).catchError((e) { + threadLastEventId: thread?.lastEvent?.eventId, + threadRootEventId: thread?.rootEvent.eventId, + ) + .catchError((e) { scaffoldMessenger.showSnackBar( SnackBar( content: Text( @@ -802,7 +806,10 @@ class ChatController extends State void sendLocationAction() async { await showAdaptiveDialog( context: context, - builder: (c) => SendLocationDialog(room: room), + builder: (c) => SendLocationDialog( + room: room, + thread: thread, + ), ); } @@ -900,10 +907,10 @@ class ChatController extends State } content['xyz.extera.translated'] = true; Navigator.of(context).push( - new MaterialPageRoute( + MaterialPageRoute( builder: (BuildContext ctx) { return TranslatedEventDialog( - event: new Event( + event: Event( content: content, type: 'm.room.message', eventId: event.eventId, diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 9fd9539..85b562e 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -39,7 +39,7 @@ class ChatEventList extends StatelessWidget { final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - var events = timeline.events.filterByVisibleInGui(); + var events = timeline.events; if (showThreadRoots) { events = events.filterThreadRoots(); @@ -47,6 +47,8 @@ class ChatEventList extends StatelessWidget { events = events.filterByThreaded(controller.thread != null); } + events = events.filterByVisibleInGui(); + final animateInEventIndex = controller.animateInEventIndex; final threads = controller.room.threads; diff --git a/lib/pages/chat/send_location_dialog.dart b/lib/pages/chat/send_location_dialog.dart index 228a482..66515d5 100644 --- a/lib/pages/chat/send_location_dialog.dart +++ b/lib/pages/chat/send_location_dialog.dart @@ -13,9 +13,11 @@ import 'package:extera_next/widgets/future_loading_dialog.dart'; class SendLocationDialog extends StatefulWidget { final Room room; + final Thread? thread; const SendLocationDialog({ required this.room, + required this.thread, super.key, }); @@ -85,7 +87,13 @@ class SendLocationDialogState extends State { 'geo:${position!.latitude},${position!.longitude};u=${position!.accuracy}'; await showFutureLoadingDialog( context: context, - future: () => widget.room.sendLocation(body, uri), + future: () { + if (widget.thread != null) { + return widget.thread!.sendLocation(body, uri); + } else { + return widget.room.sendLocation(body, uri); + } + }, ); Navigator.of(context, rootNavigator: false).pop(); } diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index b7340cf..dce194e 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -5,7 +5,7 @@ import '../../config/app_config.dart'; extension VisibleInGuiExtension on List { List filterByThreaded(bool threaded) { - return where((e) => e.isThreaded == threaded).toList(); + return where((e) => e.isThreaded == threaded || e.relationshipType == RelationshipTypes.edit || e.relationshipType == RelationshipTypes.reaction).toList(); } List filterThreadRoots() { From f327927fdd4265869b82bfb982a7aeaec8f53b06 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 17:53:24 +0500 Subject: [PATCH 13/22] merge --- lib/utils/custom_http_client.dart | 34 +++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/utils/custom_http_client.dart b/lib/utils/custom_http_client.dart index 31a48ae..c6c5060 100644 --- a/lib/utils/custom_http_client.dart +++ b/lib/utils/custom_http_client.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:io'; +import 'package:extera_next/config/app_config.dart'; +import 'package:extera_next/utils/platform_infos.dart'; import 'package:http/http.dart' as http; import 'package:http/io_client.dart'; @@ -12,20 +14,30 @@ class CustomHttpClient { final context = SecurityContext.defaultContext; - try { - if (cert != null) { - final bytes = utf8.encode(cert); - context.setTrustedCertificatesBytes(bytes); - } - } on TlsException catch (e) { - if (e.osError != null && - e.osError!.message.contains('CERT_ALREADY_IN_HASH_TABLE')) { - } else { - rethrow; + if (PlatformInfos.isAndroid) { + try { + if (cert != null) { + final bytes = utf8.encode(cert); + context.setTrustedCertificatesBytes(bytes); + } + } on TlsException catch (e) { + if (e.osError != null && + e.osError!.message.contains('CERT_ALREADY_IN_HASH_TABLE')) { + } else { + rethrow; + } } } - return HttpClient(context: context); + final client = HttpClient(context: context); + + if (AppConfig.httpProxy != null) { + client.findProxy = (uri) { + return "PROXY ${AppConfig.httpProxy};"; + }; + } + + return client; } static http.Client createHTTPClient() => IOClient(customHttpClient(ISRG_X1)); From 66ad301045fc519ace80fcaa7412f49dc4e64980 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 17:57:55 +0500 Subject: [PATCH 14/22] go back to using matrix-dart-sdk from git --- pubspec.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 2f81498..3b37331 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,10 +65,9 @@ dependencies: linkify: ^5.0.0 material: ^1.0.0+2 matrix: - path: /home/officialdakari/repos/matrix-dart-sdk - # git: - # url: https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git - # ref: feature/threads + git: + url: https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git + ref: main mime: ^1.0.6 native_imaging: ^0.2.0 opus_caf_converter_dart: ^1.0.1 From 0f65f3e7f572c78f017d6b3227547e39df10c12e Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:13:45 +0500 Subject: [PATCH 15/22] Improved UX for spaces --- assets/l10n/intl_en.arb | 5 + assets/l10n/intl_ru.arb | 5 + lib/generated/l10n/l10n.dart | 30 ++ lib/generated/l10n/l10n_ar.dart | 16 + lib/generated/l10n/l10n_be.dart | 16 + lib/generated/l10n/l10n_bn.dart | 16 + lib/generated/l10n/l10n_bo.dart | 16 + lib/generated/l10n/l10n_ca.dart | 16 + lib/generated/l10n/l10n_cs.dart | 16 + lib/generated/l10n/l10n_de.dart | 16 + lib/generated/l10n/l10n_el.dart | 16 + lib/generated/l10n/l10n_en.dart | 16 + lib/generated/l10n/l10n_eo.dart | 16 + lib/generated/l10n/l10n_es.dart | 16 + lib/generated/l10n/l10n_et.dart | 16 + lib/generated/l10n/l10n_eu.dart | 16 + lib/generated/l10n/l10n_fa.dart | 16 + lib/generated/l10n/l10n_fi.dart | 16 + lib/generated/l10n/l10n_fil.dart | 16 + lib/generated/l10n/l10n_fr.dart | 16 + lib/generated/l10n/l10n_ga.dart | 16 + lib/generated/l10n/l10n_gl.dart | 16 + lib/generated/l10n/l10n_he.dart | 16 + lib/generated/l10n/l10n_hi.dart | 16 + lib/generated/l10n/l10n_hr.dart | 16 + lib/generated/l10n/l10n_hu.dart | 16 + lib/generated/l10n/l10n_ia.dart | 16 + lib/generated/l10n/l10n_id.dart | 16 + lib/generated/l10n/l10n_ie.dart | 16 + lib/generated/l10n/l10n_it.dart | 16 + lib/generated/l10n/l10n_ja.dart | 16 + lib/generated/l10n/l10n_ka.dart | 16 + lib/generated/l10n/l10n_ko.dart | 16 + lib/generated/l10n/l10n_lt.dart | 16 + lib/generated/l10n/l10n_lv.dart | 16 + lib/generated/l10n/l10n_nb.dart | 16 + lib/generated/l10n/l10n_nl.dart | 16 + lib/generated/l10n/l10n_pl.dart | 16 + lib/generated/l10n/l10n_pt.dart | 16 + lib/generated/l10n/l10n_ro.dart | 16 + lib/generated/l10n/l10n_ru.dart | 18 +- lib/generated/l10n/l10n_sk.dart | 16 + lib/generated/l10n/l10n_sl.dart | 16 + lib/generated/l10n/l10n_sr.dart | 16 + lib/generated/l10n/l10n_sv.dart | 16 + lib/generated/l10n/l10n_ta.dart | 16 + lib/generated/l10n/l10n_te.dart | 16 + lib/generated/l10n/l10n_th.dart | 16 + lib/generated/l10n/l10n_tr.dart | 16 + lib/generated/l10n/l10n_uk.dart | 16 + lib/generated/l10n/l10n_vi.dart | 16 + lib/generated/l10n/l10n_zh.dart | 16 + lib/pages/chat_list/chat_list_item.dart | 120 ++--- lib/pages/chat_list/space_view.dart | 493 ++++++++++++------ lib/pages/chat_list/unread_bubble.dart | 56 ++ .../adaptive_dialogs/public_room_dialog.dart | 4 +- lib/widgets/avatar.dart | 10 +- pubspec.lock | 8 +- 58 files changed, 1272 insertions(+), 245 deletions(-) create mode 100644 lib/pages/chat_list/unread_bubble.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index c14f47d..3776fd5 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -6,6 +6,11 @@ "longPressToRecordVoiceMessage": "Long press to record voice message.", "pause": "Pause", "resume": "Resume", + "newSubSpace": "New sub space", + "moveToDifferentSpace": "Move to different space", + "moveUp": "Move up", + "moveDown": "Move down", + "removeFromSpaceDescription": "The chat will be removed from the space but still appear in your chat list.", "endPoll": "End poll", "anonymousPoll": "Anonymous", "publicPoll": "Public", diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index 58b3139..01996ee 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -6,6 +6,11 @@ "noMessagesYet": "Нет сообщений", "longPressToRecordVoiceMessage": "Зажмите, чтобы записать голосовое сообщение.", "pause": "Пауза", + "newSubSpace": "Новое подпространство", + "moveToDifferentSpace": "Перенести в другое пространство", + "moveUp": "Вверх", + "moveDown": "Вниз", + "removeFromSpaceDescription": "Эта комната будет удалена из пространства, но останется в Вашем списке чатов.", "resume": "Продолжить", "anonymousPoll": "Анонимный", "publicPoll": "Открытый", diff --git a/lib/generated/l10n/l10n.dart b/lib/generated/l10n/l10n.dart index 7d1a5ea..10cda94 100644 --- a/lib/generated/l10n/l10n.dart +++ b/lib/generated/l10n/l10n.dart @@ -223,6 +223,36 @@ abstract class L10n { /// **'Resume'** String get resume; + /// No description provided for @newSubSpace. + /// + /// In en, this message translates to: + /// **'New sub space'** + String get newSubSpace; + + /// No description provided for @moveToDifferentSpace. + /// + /// In en, this message translates to: + /// **'Move to different space'** + String get moveToDifferentSpace; + + /// No description provided for @moveUp. + /// + /// In en, this message translates to: + /// **'Move up'** + String get moveUp; + + /// No description provided for @moveDown. + /// + /// In en, this message translates to: + /// **'Move down'** + String get moveDown; + + /// No description provided for @removeFromSpaceDescription. + /// + /// In en, this message translates to: + /// **'The chat will be removed from the space but still appear in your chat list.'** + String get removeFromSpaceDescription; + /// No description provided for @endPoll. /// /// In en, this message translates to: diff --git a/lib/generated/l10n/l10n_ar.dart b/lib/generated/l10n/l10n_ar.dart index 129d998..7dbad83 100644 --- a/lib/generated/l10n/l10n_ar.dart +++ b/lib/generated/l10n/l10n_ar.dart @@ -24,6 +24,22 @@ class L10nAr extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_be.dart b/lib/generated/l10n/l10n_be.dart index 47ea2b6..fbc64d4 100644 --- a/lib/generated/l10n/l10n_be.dart +++ b/lib/generated/l10n/l10n_be.dart @@ -24,6 +24,22 @@ class L10nBe extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_bn.dart b/lib/generated/l10n/l10n_bn.dart index bbd7340..81ad17d 100644 --- a/lib/generated/l10n/l10n_bn.dart +++ b/lib/generated/l10n/l10n_bn.dart @@ -24,6 +24,22 @@ class L10nBn extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_bo.dart b/lib/generated/l10n/l10n_bo.dart index 5ca3f14..b4af5c3 100644 --- a/lib/generated/l10n/l10n_bo.dart +++ b/lib/generated/l10n/l10n_bo.dart @@ -24,6 +24,22 @@ class L10nBo extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ca.dart b/lib/generated/l10n/l10n_ca.dart index 16bb4b1..d1fb8a6 100644 --- a/lib/generated/l10n/l10n_ca.dart +++ b/lib/generated/l10n/l10n_ca.dart @@ -24,6 +24,22 @@ class L10nCa extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_cs.dart b/lib/generated/l10n/l10n_cs.dart index bd2a35c..c0cdcea 100644 --- a/lib/generated/l10n/l10n_cs.dart +++ b/lib/generated/l10n/l10n_cs.dart @@ -24,6 +24,22 @@ class L10nCs extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_de.dart b/lib/generated/l10n/l10n_de.dart index 4a353ff..51ab151 100644 --- a/lib/generated/l10n/l10n_de.dart +++ b/lib/generated/l10n/l10n_de.dart @@ -24,6 +24,22 @@ class L10nDe extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_el.dart b/lib/generated/l10n/l10n_el.dart index 18d28c0..f649d09 100644 --- a/lib/generated/l10n/l10n_el.dart +++ b/lib/generated/l10n/l10n_el.dart @@ -24,6 +24,22 @@ class L10nEl extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_en.dart b/lib/generated/l10n/l10n_en.dart index 04c491b..4a5fa02 100644 --- a/lib/generated/l10n/l10n_en.dart +++ b/lib/generated/l10n/l10n_en.dart @@ -24,6 +24,22 @@ class L10nEn extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_eo.dart b/lib/generated/l10n/l10n_eo.dart index 62a3a8a..d9e1f12 100644 --- a/lib/generated/l10n/l10n_eo.dart +++ b/lib/generated/l10n/l10n_eo.dart @@ -24,6 +24,22 @@ class L10nEo extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_es.dart b/lib/generated/l10n/l10n_es.dart index e80a010..533be37 100644 --- a/lib/generated/l10n/l10n_es.dart +++ b/lib/generated/l10n/l10n_es.dart @@ -24,6 +24,22 @@ class L10nEs extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_et.dart b/lib/generated/l10n/l10n_et.dart index afda2ec..da47eea 100644 --- a/lib/generated/l10n/l10n_et.dart +++ b/lib/generated/l10n/l10n_et.dart @@ -24,6 +24,22 @@ class L10nEt extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_eu.dart b/lib/generated/l10n/l10n_eu.dart index a4c5eb4..1bde741 100644 --- a/lib/generated/l10n/l10n_eu.dart +++ b/lib/generated/l10n/l10n_eu.dart @@ -24,6 +24,22 @@ class L10nEu extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_fa.dart b/lib/generated/l10n/l10n_fa.dart index a796961..43464c6 100644 --- a/lib/generated/l10n/l10n_fa.dart +++ b/lib/generated/l10n/l10n_fa.dart @@ -24,6 +24,22 @@ class L10nFa extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_fi.dart b/lib/generated/l10n/l10n_fi.dart index 0accfcc..aae290d 100644 --- a/lib/generated/l10n/l10n_fi.dart +++ b/lib/generated/l10n/l10n_fi.dart @@ -24,6 +24,22 @@ class L10nFi extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_fil.dart b/lib/generated/l10n/l10n_fil.dart index 20010ed..222201e 100644 --- a/lib/generated/l10n/l10n_fil.dart +++ b/lib/generated/l10n/l10n_fil.dart @@ -24,6 +24,22 @@ class L10nFil extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_fr.dart b/lib/generated/l10n/l10n_fr.dart index 33e4d5a..283460f 100644 --- a/lib/generated/l10n/l10n_fr.dart +++ b/lib/generated/l10n/l10n_fr.dart @@ -24,6 +24,22 @@ class L10nFr extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ga.dart b/lib/generated/l10n/l10n_ga.dart index 94b2051..9febce3 100644 --- a/lib/generated/l10n/l10n_ga.dart +++ b/lib/generated/l10n/l10n_ga.dart @@ -24,6 +24,22 @@ class L10nGa extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_gl.dart b/lib/generated/l10n/l10n_gl.dart index 1f4e199..268efdb 100644 --- a/lib/generated/l10n/l10n_gl.dart +++ b/lib/generated/l10n/l10n_gl.dart @@ -24,6 +24,22 @@ class L10nGl extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_he.dart b/lib/generated/l10n/l10n_he.dart index 3d32ec7..aaab0d9 100644 --- a/lib/generated/l10n/l10n_he.dart +++ b/lib/generated/l10n/l10n_he.dart @@ -24,6 +24,22 @@ class L10nHe extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_hi.dart b/lib/generated/l10n/l10n_hi.dart index a12d291..1528cac 100644 --- a/lib/generated/l10n/l10n_hi.dart +++ b/lib/generated/l10n/l10n_hi.dart @@ -24,6 +24,22 @@ class L10nHi extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_hr.dart b/lib/generated/l10n/l10n_hr.dart index 02d5df4..9f01e8b 100644 --- a/lib/generated/l10n/l10n_hr.dart +++ b/lib/generated/l10n/l10n_hr.dart @@ -24,6 +24,22 @@ class L10nHr extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_hu.dart b/lib/generated/l10n/l10n_hu.dart index bb01c3e..c2dc2ba 100644 --- a/lib/generated/l10n/l10n_hu.dart +++ b/lib/generated/l10n/l10n_hu.dart @@ -24,6 +24,22 @@ class L10nHu extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ia.dart b/lib/generated/l10n/l10n_ia.dart index c3fed88..6a83aaa 100644 --- a/lib/generated/l10n/l10n_ia.dart +++ b/lib/generated/l10n/l10n_ia.dart @@ -24,6 +24,22 @@ class L10nIa extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_id.dart b/lib/generated/l10n/l10n_id.dart index 12c4c3b..b162b23 100644 --- a/lib/generated/l10n/l10n_id.dart +++ b/lib/generated/l10n/l10n_id.dart @@ -24,6 +24,22 @@ class L10nId extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ie.dart b/lib/generated/l10n/l10n_ie.dart index 7d3b20a..7ee3897 100644 --- a/lib/generated/l10n/l10n_ie.dart +++ b/lib/generated/l10n/l10n_ie.dart @@ -24,6 +24,22 @@ class L10nIe extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_it.dart b/lib/generated/l10n/l10n_it.dart index 2be51c0..4c6a77f 100644 --- a/lib/generated/l10n/l10n_it.dart +++ b/lib/generated/l10n/l10n_it.dart @@ -24,6 +24,22 @@ class L10nIt extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ja.dart b/lib/generated/l10n/l10n_ja.dart index e3fe6bb..e3c855f 100644 --- a/lib/generated/l10n/l10n_ja.dart +++ b/lib/generated/l10n/l10n_ja.dart @@ -24,6 +24,22 @@ class L10nJa extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ka.dart b/lib/generated/l10n/l10n_ka.dart index 3010bdd..eb09a4d 100644 --- a/lib/generated/l10n/l10n_ka.dart +++ b/lib/generated/l10n/l10n_ka.dart @@ -24,6 +24,22 @@ class L10nKa extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ko.dart b/lib/generated/l10n/l10n_ko.dart index 7b80318..bad2623 100644 --- a/lib/generated/l10n/l10n_ko.dart +++ b/lib/generated/l10n/l10n_ko.dart @@ -24,6 +24,22 @@ class L10nKo extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_lt.dart b/lib/generated/l10n/l10n_lt.dart index 0a81214..3f437b6 100644 --- a/lib/generated/l10n/l10n_lt.dart +++ b/lib/generated/l10n/l10n_lt.dart @@ -24,6 +24,22 @@ class L10nLt extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_lv.dart b/lib/generated/l10n/l10n_lv.dart index 5b32b8a..572f204 100644 --- a/lib/generated/l10n/l10n_lv.dart +++ b/lib/generated/l10n/l10n_lv.dart @@ -24,6 +24,22 @@ class L10nLv extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_nb.dart b/lib/generated/l10n/l10n_nb.dart index e4df751..56fb2a8 100644 --- a/lib/generated/l10n/l10n_nb.dart +++ b/lib/generated/l10n/l10n_nb.dart @@ -24,6 +24,22 @@ class L10nNb extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_nl.dart b/lib/generated/l10n/l10n_nl.dart index e4be809..893028d 100644 --- a/lib/generated/l10n/l10n_nl.dart +++ b/lib/generated/l10n/l10n_nl.dart @@ -24,6 +24,22 @@ class L10nNl extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_pl.dart b/lib/generated/l10n/l10n_pl.dart index 0d1522d..0df608c 100644 --- a/lib/generated/l10n/l10n_pl.dart +++ b/lib/generated/l10n/l10n_pl.dart @@ -24,6 +24,22 @@ class L10nPl extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_pt.dart b/lib/generated/l10n/l10n_pt.dart index 9ac3270..681ce6e 100644 --- a/lib/generated/l10n/l10n_pt.dart +++ b/lib/generated/l10n/l10n_pt.dart @@ -24,6 +24,22 @@ class L10nPt extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ro.dart b/lib/generated/l10n/l10n_ro.dart index 27570ba..30fe5af 100644 --- a/lib/generated/l10n/l10n_ro.dart +++ b/lib/generated/l10n/l10n_ro.dart @@ -24,6 +24,22 @@ class L10nRo extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ru.dart b/lib/generated/l10n/l10n_ru.dart index b3cab54..95528bf 100644 --- a/lib/generated/l10n/l10n_ru.dart +++ b/lib/generated/l10n/l10n_ru.dart @@ -24,6 +24,22 @@ class L10nRu extends L10n { @override String get resume => 'Продолжить'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'Завершить опрос'; @@ -91,7 +107,7 @@ class L10nRu extends L10n { @override String get cleanExifDescription => - 'Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений еред отправкой.'; + 'Удалять метаданные EXIF (модель камеры, геолокация, время) из изображений перед отправкой.'; @override String get repeatPassword => 'Повторите пароль'; diff --git a/lib/generated/l10n/l10n_sk.dart b/lib/generated/l10n/l10n_sk.dart index eaf5a91..db59bd9 100644 --- a/lib/generated/l10n/l10n_sk.dart +++ b/lib/generated/l10n/l10n_sk.dart @@ -24,6 +24,22 @@ class L10nSk extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_sl.dart b/lib/generated/l10n/l10n_sl.dart index 36b0412..3ed0849 100644 --- a/lib/generated/l10n/l10n_sl.dart +++ b/lib/generated/l10n/l10n_sl.dart @@ -24,6 +24,22 @@ class L10nSl extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_sr.dart b/lib/generated/l10n/l10n_sr.dart index ed06706..e3955ed 100644 --- a/lib/generated/l10n/l10n_sr.dart +++ b/lib/generated/l10n/l10n_sr.dart @@ -24,6 +24,22 @@ class L10nSr extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_sv.dart b/lib/generated/l10n/l10n_sv.dart index 72be4e4..8c80e17 100644 --- a/lib/generated/l10n/l10n_sv.dart +++ b/lib/generated/l10n/l10n_sv.dart @@ -24,6 +24,22 @@ class L10nSv extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_ta.dart b/lib/generated/l10n/l10n_ta.dart index 2406198..8981c25 100644 --- a/lib/generated/l10n/l10n_ta.dart +++ b/lib/generated/l10n/l10n_ta.dart @@ -24,6 +24,22 @@ class L10nTa extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_te.dart b/lib/generated/l10n/l10n_te.dart index 64c73fb..6b5ece6 100644 --- a/lib/generated/l10n/l10n_te.dart +++ b/lib/generated/l10n/l10n_te.dart @@ -24,6 +24,22 @@ class L10nTe extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_th.dart b/lib/generated/l10n/l10n_th.dart index ea57c24..dd42ebb 100644 --- a/lib/generated/l10n/l10n_th.dart +++ b/lib/generated/l10n/l10n_th.dart @@ -24,6 +24,22 @@ class L10nTh extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_tr.dart b/lib/generated/l10n/l10n_tr.dart index 91dbd9d..75e2758 100644 --- a/lib/generated/l10n/l10n_tr.dart +++ b/lib/generated/l10n/l10n_tr.dart @@ -24,6 +24,22 @@ class L10nTr extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_uk.dart b/lib/generated/l10n/l10n_uk.dart index 4964ed8..a692a7b 100644 --- a/lib/generated/l10n/l10n_uk.dart +++ b/lib/generated/l10n/l10n_uk.dart @@ -24,6 +24,22 @@ class L10nUk extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_vi.dart b/lib/generated/l10n/l10n_vi.dart index 1bcb6d4..330b6a5 100644 --- a/lib/generated/l10n/l10n_vi.dart +++ b/lib/generated/l10n/l10n_vi.dart @@ -24,6 +24,22 @@ class L10nVi extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/generated/l10n/l10n_zh.dart b/lib/generated/l10n/l10n_zh.dart index 0550d1a..2a9aece 100644 --- a/lib/generated/l10n/l10n_zh.dart +++ b/lib/generated/l10n/l10n_zh.dart @@ -24,6 +24,22 @@ class L10nZh extends L10n { @override String get resume => 'Resume'; + @override + String get newSubSpace => 'New sub space'; + + @override + String get moveToDifferentSpace => 'Move to different space'; + + @override + String get moveUp => 'Move up'; + + @override + String get moveDown => 'Move down'; + + @override + String get removeFromSpaceDescription => + 'The chat will be removed from the space but still appear in your chat list.'; + @override String get endPoll => 'End poll'; diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index ffa49a4..5c64552 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:extera_next/generated/l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:extera_next/config/app_config.dart'; +import 'package:extera_next/generated/l10n/l10n.dart'; +import 'package:extera_next/pages/chat_list/unread_bubble.dart'; import 'package:extera_next/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:extera_next/utils/room_status_extension.dart'; import 'package:extera_next/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; @@ -35,32 +36,6 @@ class ChatListItem extends StatelessWidget { super.key, }); - Future archiveAction(BuildContext context) async { - { - if ([Membership.leave, Membership.ban].contains(room.membership)) { - final forgetResult = await showFutureLoadingDialog( - context: context, - future: () => room.forget(), - ); - return forgetResult.isValue; - } - final confirmed = await showOkCancelAlertDialog( - context: context, - title: L10n.of(context).areYouSure, - okLabel: L10n.of(context).leave, - cancelLabel: L10n.of(context).cancel, - message: L10n.of(context).archiveRoomDescription, - isDestructive: true, - ); - if (confirmed != OkCancelResult.ok) return false; - final leaveResult = await showFutureLoadingDialog( - context: context, - future: () => room.leave(), - ); - return leaveResult.isValue; - } - } - @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -69,14 +44,9 @@ class ChatListItem extends StatelessWidget { final typingText = room.getLocalizedTypingText(context); final lastEvent = room.lastEvent; final ownMessage = lastEvent?.senderId == room.client.userID; - final unread = room.isUnread || room.membership == Membership.invite; + final unread = room.isUnread; final directChatMatrixId = room.directChatMatrixID; final isDirectChat = directChatMatrixId != null; - final unreadBubbleSize = unread || room.hasNewMessages - ? room.notificationCount > 0 - ? 20.0 - : 14.0 - : 0.0; final hasNotifications = room.notificationCount > 0; final backgroundColor = activeChat ? theme.colorScheme.secondaryContainer : null; @@ -93,8 +63,6 @@ class ChatListItem extends StatelessWidget { : room.getState(EventTypes.RoomMember, lastEvent.senderId) == null; final space = this.space; - final inviterMxId = room.getState(EventTypes.RoomMember, room.client.userID!)?.senderId; - return Padding( padding: const EdgeInsets.symmetric( horizontal: 8, @@ -116,9 +84,7 @@ class ChatListItem extends StatelessWidget { duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, scale: hovered ? 1.1 : 1.0, - child: - (!AppConfig.hideAvatarsInInvites || room.membership != Membership.invite) - ? SizedBox( + child: SizedBox( width: Avatar.defaultSize, height: Avatar.defaultSize, child: Stack( @@ -195,7 +161,7 @@ class ChatListItem extends StatelessWidget { ), ], ), - ) : null, + ), ), ), title: Row( @@ -232,13 +198,12 @@ class ChatListItem extends StatelessWidget { color: theme.colorScheme.primary, ), ), - if (!room.isSpace && - lastEvent != null && - room.membership != Membership.invite) + if (!room.isSpace && room.membership != Membership.invite) Padding( padding: const EdgeInsets.only(left: 4.0), child: Text( - lastEvent.originServerTs.localizedTimeShort(context), + room.latestEventReceivedTime + .localizedTimeShort(context), style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, @@ -253,7 +218,7 @@ class ChatListItem extends StatelessWidget { children: [ if (typingText.isEmpty && ownMessage && - (room.lastEvent?.status.isSending ?? false)) ...[ + room.lastEvent?.status.isSending == true) ...[ const SizedBox( width: 16, height: 16, @@ -321,7 +286,7 @@ class ChatListItem extends StatelessWidget { ), builder: (context, snapshot) => Text( room.membership == Membership.invite - ? "${room + ? room .getState( EventTypes.RoomMember, room.client.userID!, @@ -331,9 +296,9 @@ class ChatListItem extends StatelessWidget { (isDirectChat ? L10n.of(context).newChatRequest : L10n.of(context) - .inviteGroupChat)}${inviterMxId != null ? " ($inviterMxId)" : ""}" + .inviteGroupChat) : snapshot.data ?? - L10n.of(context).emptyChat, + L10n.of(context).noMessagesYet, softWrap: false, maxLines: room.notificationCount >= 1 ? 2 : 1, overflow: TextOverflow.ellipsis, @@ -349,48 +314,33 @@ class ChatListItem extends StatelessWidget { ), ), const SizedBox(width: 8), - AnimatedContainer( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 7), - height: unreadBubbleSize, - width: !hasNotifications && !unread && !room.hasNewMessages - ? 0 - : (unreadBubbleSize - 9) * - room.notificationCount.toString().length + - 9, - decoration: BoxDecoration( - color: room.highlightCount > 0 || - room.membership == Membership.invite - ? theme.colorScheme.error - : hasNotifications || room.markedUnread - ? theme.colorScheme.primary - : theme.colorScheme.primaryContainer, - borderRadius: BorderRadius.circular(7), - ), - child: hasNotifications - ? Text( - room.notificationCount.toString(), - style: TextStyle( - color: room.highlightCount > 0 || - room.membership == Membership.invite - ? theme.colorScheme.onError - : hasNotifications - ? theme.colorScheme.onPrimary - : theme.colorScheme.onPrimaryContainer, - fontSize: 13, - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.center, - ) - : const SizedBox.shrink(), - ), + UnreadBubble(room: room), ], ), onTap: onTap, trailing: onForget == null - ? null + ? room.membership == Membership.invite + ? IconButton( + tooltip: L10n.of(context).decline, + icon: const Icon(Icons.delete_forever_outlined), + color: theme.colorScheme.error, + onPressed: () async { + final consent = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).decline, + message: L10n.of(context).areYouSure, + okLabel: L10n.of(context).yes, + isDestructive: true, + ); + if (consent != OkCancelResult.ok) return; + if (!context.mounted) return; + await showFutureLoadingDialog( + context: context, + future: room.leave, + ); + }, + ) + : null : IconButton( icon: const Icon(Icons.delete_outlined), onPressed: onForget, diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 10698d5..fc5e330 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -1,27 +1,40 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; -import 'package:extera_next/generated/l10n/l10n.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; import 'package:extera_next/config/app_config.dart'; import 'package:extera_next/config/themes.dart'; -import 'package:extera_next/pages/chat_list/chat_list_item.dart'; -import 'package:extera_next/pages/chat_list/search_title.dart'; +import 'package:extera_next/generated/l10n/l10n.dart'; +import 'package:extera_next/pages/chat_list/unread_bubble.dart'; import 'package:extera_next/utils/localized_exception_extension.dart'; +import 'package:extera_next/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:extera_next/utils/stream_extension.dart'; +import 'package:extera_next/utils/string_color.dart'; import 'package:extera_next/widgets/adaptive_dialogs/public_room_dialog.dart'; import 'package:extera_next/widgets/adaptive_dialogs/show_modal_action_popup.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/avatar.dart'; import 'package:extera_next/widgets/future_loading_dialog.dart'; +import 'package:extera_next/widgets/hover_builder.dart'; import 'package:extera_next/widgets/matrix.dart'; enum AddRoomType { chat, subspace } +enum SpaceChildAction { edit, moveToSpace, removeFromSpace } + +enum SpaceActions { + settings, + invite, + members, + leave, +} + class SpaceView extends StatefulWidget { final String spaceId; final void Function() onBack; @@ -45,7 +58,7 @@ class SpaceView extends StatefulWidget { } class _SpaceViewState extends State { - final List _discoveredChildren = []; + final List _discoveredChildren = []; final TextEditingController _filterController = TextEditingController(); String? _nextBatch; bool _noMoreRooms = false; @@ -58,9 +71,28 @@ class _SpaceViewState extends State { } void _loadHierarchy() async { - final room = Matrix.of(context).client.getRoomById(widget.spaceId); + final matrix = Matrix.of(context); + final room = matrix.client.getRoomById(widget.spaceId); if (room == null) return; + final cacheKey = 'spaces_history_cache${room.id}'; + if (_discoveredChildren.isEmpty) { + final cachedChildren = matrix.store.getStringList(cacheKey); + if (cachedChildren != null) { + try { + _discoveredChildren.addAll( + cachedChildren.map( + (jsonString) => + SpaceRoomsChunk$2.fromJson(jsonDecode(jsonString)), + ), + ); + } catch (e, s) { + Logs().e('Unable to json decode spaces hierarchy cache!', e, s); + matrix.store.remove(cacheKey); + } + } + } + setState(() { _isLoading = true; }); @@ -74,16 +106,25 @@ class _SpaceViewState extends State { ); if (!mounted) return; setState(() { + if (_nextBatch == null) _discoveredChildren.clear(); _nextBatch = hierarchy.nextBatch; if (hierarchy.nextBatch == null) { _noMoreRooms = true; } _discoveredChildren.addAll( - hierarchy.rooms - .where((c) => room.client.getRoomById(c.roomId) == null), + hierarchy.rooms.where((room) => room.roomId != widget.spaceId), ); _isLoading = false; }); + + if (_nextBatch == null) { + matrix.store.setStringList( + cacheKey, + _discoveredChildren + .map((child) => jsonEncode(child.toJson())) + .toList(), + ); + } } catch (e, s) { Logs().w('Unable to load hierarchy', e, s); if (!mounted) return; @@ -95,7 +136,7 @@ class _SpaceViewState extends State { } } - void _joinChildRoom(SpaceRoomsChunk item) async { + void _joinChildRoom(SpaceRoomsChunk$2 item) async { final client = Matrix.of(context).client; final space = client.getRoomById(widget.spaceId); @@ -111,9 +152,7 @@ class _SpaceViewState extends State { ), ); if (mounted && joined == true) { - setState(() { - _discoveredChildren.remove(item); - }); + setState(() {}); } } @@ -129,6 +168,10 @@ class _SpaceViewState extends State { await space?.postLoad(); context.push('/rooms/${widget.spaceId}/invite'); break; + case SpaceActions.members: + await space?.postLoad(); + context.push('/rooms/${widget.spaceId}/details/members'); + break; case SpaceActions.leave: final confirmed = await showOkCancelAlertDialog( context: context, @@ -151,27 +194,11 @@ class _SpaceViewState extends State { } } - void _addChatOrSubspace() async { - final roomType = await showModalActionPopup( - context: context, - title: L10n.of(context).addChatOrSubSpace, - actions: [ - AdaptiveModalAction( - value: AddRoomType.subspace, - label: L10n.of(context).createNewSpace, - ), - AdaptiveModalAction( - value: AddRoomType.chat, - label: L10n.of(context).createGroup, - ), - ], - ); - if (roomType == null) return; - + void _addChatOrSubspace(AddRoomType roomType) async { final names = await showTextInputDialog( context: context, title: roomType == AddRoomType.subspace - ? L10n.of(context).createNewSpace + ? L10n.of(context).newSubSpace : L10n.of(context).createGroup, hintText: roomType == AddRoomType.subspace ? L10n.of(context).spaceName @@ -196,29 +223,169 @@ class _SpaceViewState extends State { late final String roomId; final activeSpace = client.getRoomById(widget.spaceId)!; await activeSpace.postLoad(); + final isPublicSpace = activeSpace.joinRules == JoinRules.public; if (roomType == AddRoomType.subspace) { roomId = await client.createSpace( name: names, - visibility: activeSpace.joinRules == JoinRules.public - ? sdk.Visibility.public - : sdk.Visibility.private, + visibility: + isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private, ); } else { roomId = await client.createGroupChat( + enableEncryption: !isPublicSpace, groupName: names, - preset: activeSpace.joinRules == JoinRules.public + preset: isPublicSpace ? CreateRoomPreset.publicChat : CreateRoomPreset.privateChat, - visibility: activeSpace.joinRules == JoinRules.public - ? sdk.Visibility.public - : sdk.Visibility.private, + visibility: + isPublicSpace ? sdk.Visibility.public : sdk.Visibility.private, + initialState: isPublicSpace + ? null + : [ + StateEvent( + content: { + 'join_rule': 'restricted', + 'allow': [ + { + 'room_id': widget.spaceId, + 'type': 'm.room_membership', + }, + ], + }, + type: EventTypes.RoomJoinRules, + ), + ], ); } await activeSpace.setSpaceChild(roomId); }, ); if (result.error != null) return; + setState(() { + _nextBatch = null; + _discoveredChildren.clear(); + }); + _loadHierarchy(); + } + + void _showSpaceChildEditMenu(BuildContext posContext, String roomId) async { + final overlay = + Overlay.of(posContext).context.findRenderObject() as RenderBox; + + final button = posContext.findRenderObject() as RenderBox; + + final position = RelativeRect.fromRect( + Rect.fromPoints( + button.localToGlobal(const Offset(0, -65), ancestor: overlay), + button.localToGlobal( + button.size.bottomRight(Offset.zero) + const Offset(-50, 0), + ancestor: overlay, + ), + ), + Offset.zero & overlay.size, + ); + + final action = await showMenu( + context: posContext, + position: position, + items: [ + PopupMenuItem( + value: SpaceChildAction.moveToSpace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.move_down_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).moveToDifferentSpace), + ], + ), + ), + PopupMenuItem( + value: SpaceChildAction.edit, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.edit_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).edit), + ], + ), + ), + PopupMenuItem( + value: SpaceChildAction.removeFromSpace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group_remove_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).removeFromSpace), + ], + ), + ), + ], + ); + if (action == null) return; + if (!mounted) return; + final space = Matrix.of(context).client.getRoomById(widget.spaceId); + if (space == null) return; + switch (action) { + case SpaceChildAction.edit: + context.push('/rooms/${widget.spaceId}/details'); + case SpaceChildAction.moveToSpace: + final spacesWithPowerLevels = space.client.rooms + .where( + (room) => + room.isSpace && + room.canChangeStateEvent(EventTypes.SpaceChild) && + room.id != widget.spaceId, + ) + .toList(); + final newSpace = await showModalActionPopup( + context: context, + title: L10n.of(context).space, + actions: spacesWithPowerLevels + .map( + (space) => AdaptiveModalAction( + value: space, + label: space + .getLocalizedDisplayname(MatrixLocals(L10n.of(context))), + ), + ) + .toList(), + ); + if (newSpace == null) return; + final result = await showFutureLoadingDialog( + context: context, + future: () async { + await newSpace.setSpaceChild(newSpace.id); + await space.removeSpaceChild(roomId); + }, + ); + if (result.isError) return; + if (!mounted) return; + _nextBatch = null; + _loadHierarchy(); + return; + + case SpaceChildAction.removeFromSpace: + final consent = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).removeFromSpace, + message: L10n.of(context).removeFromSpaceDescription, + ); + if (consent != OkCancelResult.ok) return; + if (!mounted) return; + final result = await showFutureLoadingDialog( + context: context, + future: () => space.removeSpaceChild(roomId), + ); + if (result.isError) return; + if (!mounted) return; + _nextBatch = null; + _loadHierarchy(); + return; + } } @override @@ -228,6 +395,11 @@ class _SpaceViewState extends State { final room = Matrix.of(context).client.getRoomById(widget.spaceId); final displayname = room?.getLocalizedDisplayname() ?? L10n.of(context).nothingFound; + const avatarSize = Avatar.defaultSize / 1.5; + final isAdmin = room?.canChangeStateEvent( + EventTypes.SpaceChild, + ) == + true; return Scaffold( appBar: AppBar( leading: FluffyThemes.isColumnMode(context) @@ -242,6 +414,7 @@ class _SpaceViewState extends State { title: ListTile( contentPadding: EdgeInsets.zero, leading: Avatar( + size: avatarSize, mxContent: room?.avatar, name: displayname, border: BorderSide(width: 1, color: theme.dividerColor), @@ -252,19 +425,40 @@ class _SpaceViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - subtitle: room == null - ? null - : Text( - L10n.of(context).countChatsAndCountParticipants( - room.spaceChildren.length, - room.summary.mJoinedMemberCount ?? 1, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), ), actions: [ + if (isAdmin) + PopupMenuButton( + icon: const Icon(Icons.add_outlined), + onSelected: _addChatOrSubspace, + tooltip: L10n.of(context).addChatOrSubSpace, + itemBuilder: (context) => [ + PopupMenuItem( + value: AddRoomType.chat, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group_add_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).newGroup), + ], + ), + ), + PopupMenuItem( + value: AddRoomType.subspace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.workspaces_outlined), + const SizedBox(width: 12), + Text(L10n.of(context).newSubSpace), + ], + ), + ), + ], + ), PopupMenuButton( + useRootNavigator: true, onSelected: _onSpaceAction, itemBuilder: (context) => [ PopupMenuItem( @@ -289,6 +483,21 @@ class _SpaceViewState extends State { ], ), ), + PopupMenuItem( + value: SpaceActions.members, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.group_outlined), + const SizedBox(width: 12), + Text( + L10n.of(context).countParticipants( + room?.summary.mJoinedMemberCount ?? 1, + ), + ), + ], + ), + ), PopupMenuItem( value: SpaceActions.leave, child: Row( @@ -304,16 +513,6 @@ class _SpaceViewState extends State { ), ], ), - floatingActionButton: room?.canChangeStateEvent( - EventTypes.SpaceChild, - ) == - true - ? FloatingActionButton.extended( - onPressed: _addChatOrSubspace, - label: Text(L10n.of(context).group), - icon: const Icon(Icons.group_add_outlined), - ) - : null, body: room == null ? const Center( child: Icon( @@ -331,9 +530,11 @@ class _SpaceViewState extends State { .whereType() .toSet(); - final joinedRooms = room.client.rooms - .where((room) => childrenIds.remove(room.id)) - .toList(); + final joinedRooms = Map.fromEntries( + room.client.rooms + .where((room) => childrenIds.remove(room.id)) + .map((room) => MapEntry(room.id, room)), + ); final joinedParents = room.spaceParents .map((parent) { @@ -348,7 +549,6 @@ class _SpaceViewState extends State { slivers: [ SliverAppBar( floating: true, - toolbarHeight: 72, scrolledUnderElevation: 0, backgroundColor: Colors.transparent, automaticallyImplyLeading: false, @@ -358,11 +558,6 @@ class _SpaceViewState extends State { textInputAction: TextInputAction.search, decoration: InputDecoration( filled: true, - fillColor: theme.colorScheme.secondaryContainer, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(99), - ), contentPadding: EdgeInsets.zero, hintText: L10n.of(context).search, hintStyle: TextStyle( @@ -422,42 +617,11 @@ class _SpaceViewState extends State { }, ), SliverList.builder( - itemCount: joinedRooms.length, + itemCount: _discoveredChildren.length + 1, itemBuilder: (context, i) { - final joinedRoom = joinedRooms[i]; - return ChatListItem( - joinedRoom, - filter: filter, - onTap: () => widget.onChatTab(joinedRoom), - onLongPress: (context) => widget.onChatContext( - joinedRoom, - context, - ), - activeChat: widget.activeChat == joinedRoom.id, - ); - }, - ), - SliverList.builder( - itemCount: _discoveredChildren.length + 2, - itemBuilder: (context, i) { - if (i == 0) { - return SearchTitle( - title: L10n.of(context).discover, - icon: const Icon(Icons.explore_outlined), - ); - } - i--; if (i == _discoveredChildren.length) { if (_noMoreRooms) { - return Padding( - padding: const EdgeInsets.all(12.0), - child: Center( - child: Text( - L10n.of(context).noMoreChatsFound, - style: const TextStyle(fontSize: 13), - ), - ), - ); + return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric( @@ -467,11 +631,7 @@ class _SpaceViewState extends State { child: TextButton( onPressed: _isLoading ? null : _loadHierarchy, child: _isLoading - ? LinearProgressIndicator( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ) + ? const CircularProgressIndicator.adaptive() : Text(L10n.of(context).loadMore), ), ); @@ -483,6 +643,7 @@ class _SpaceViewState extends State { if (!displayname.toLowerCase().contains(filter)) { return const SizedBox.shrink(); } + final joinedRoom = joinedRooms[item.roomId]; return Padding( padding: const EdgeInsets.symmetric( horizontal: 8, @@ -492,51 +653,83 @@ class _SpaceViewState extends State { borderRadius: BorderRadius.circular(AppConfig.borderRadius), clipBehavior: Clip.hardEdge, - child: ListTile( - visualDensity: - const VisualDensity(vertical: -0.5), - contentPadding: - const EdgeInsets.symmetric(horizontal: 8), - onTap: () => _joinChildRoom(item), - leading: Avatar( - mxContent: item.avatarUrl, - name: displayname, - borderRadius: item.roomType == 'm.space' - ? BorderRadius.circular( - AppConfig.borderRadius / 2, - ) + color: joinedRoom != null && + widget.activeChat == joinedRoom.id + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + child: HoverBuilder( + builder: (context, hovered) => ListTile( + visualDensity: + const VisualDensity(vertical: -0.5), + contentPadding: + const EdgeInsets.symmetric(horizontal: 8), + onTap: joinedRoom != null + ? () => widget.onChatTab(joinedRoom) + : () => _joinChildRoom(item), + onLongPress: isAdmin + ? () => _showSpaceChildEditMenu( + context, + item.roomId, + ) : null, - ), - title: Row( - children: [ - Expanded( - child: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, + leading: hovered && isAdmin + ? SizedBox.square( + dimension: avatarSize, + child: IconButton( + splashRadius: avatarSize, + iconSize: 14, + style: IconButton.styleFrom( + foregroundColor: theme.colorScheme + .onTertiaryContainer, + backgroundColor: theme + .colorScheme.tertiaryContainer, + ), + onPressed: () => + _showSpaceChildEditMenu( + context, + item.roomId, + ), + icon: const Icon(Icons.edit_outlined), + ), + ) + : Avatar( + size: avatarSize, + mxContent: item.avatarUrl, + name: '#', + backgroundColor: + theme.colorScheme.surfaceContainer, + textColor: item.name?.darkColor ?? + theme.colorScheme.onSurface, + border: item.roomType == 'm.space' + ? BorderSide( + color: theme.colorScheme + .surfaceContainerHighest, + ) + : null, + borderRadius: item.roomType == 'm.space' + ? BorderRadius.circular( + AppConfig.borderRadius / 4, + ) + : null, + ), + title: Row( + children: [ + Expanded( + child: Opacity( + opacity: joinedRoom == null ? 0.5 : 1, + child: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), ), - ), - Text( - item.numJoinedMembers.toString(), - style: TextStyle( - fontSize: 13, - color: theme.textTheme.bodyMedium!.color, - ), - ), - const SizedBox(width: 4), - const Icon( - Icons.people_outlined, - size: 14, - ), - ], - ), - subtitle: Text( - item.topic ?? - L10n.of(context).countParticipants( - item.numJoinedMembers, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, + if (joinedRoom != null) + UnreadBubble(room: joinedRoom) + else + const Icon(Icons.chevron_right_outlined), + ], + ), ), ), ), @@ -550,10 +743,4 @@ class _SpaceViewState extends State { ), ); } -} - -enum SpaceActions { - settings, - invite, - leave, -} +} \ No newline at end of file diff --git a/lib/pages/chat_list/unread_bubble.dart b/lib/pages/chat_list/unread_bubble.dart new file mode 100644 index 0000000..b244e6e --- /dev/null +++ b/lib/pages/chat_list/unread_bubble.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +import 'package:matrix/matrix.dart'; + +import 'package:extera_next/config/themes.dart'; + +class UnreadBubble extends StatelessWidget { + final Room room; + const UnreadBubble({required this.room, super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final unread = room.isUnread; + final hasNotifications = room.notificationCount > 0; + final unreadBubbleSize = unread || room.hasNewMessages + ? room.notificationCount > 0 + ? 20.0 + : 14.0 + : 0.0; + return AnimatedContainer( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(horizontal: 7), + height: unreadBubbleSize, + width: !hasNotifications && !unread && !room.hasNewMessages + ? 0 + : (unreadBubbleSize - 9) * room.notificationCount.toString().length + + 9, + decoration: BoxDecoration( + color: room.highlightCount > 0 + ? theme.colorScheme.error + : hasNotifications || room.markedUnread + ? theme.colorScheme.primary + : theme.colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(7), + ), + child: hasNotifications + ? Text( + room.notificationCount.toString(), + style: TextStyle( + color: room.highlightCount > 0 + ? theme.colorScheme.onError + : hasNotifications + ? theme.colorScheme.onPrimary + : theme.colorScheme.onPrimaryContainer, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ) + : const SizedBox.shrink(), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/adaptive_dialogs/public_room_dialog.dart b/lib/widgets/adaptive_dialogs/public_room_dialog.dart index 2add953..c604248 100644 --- a/lib/widgets/adaptive_dialogs/public_room_dialog.dart +++ b/lib/widgets/adaptive_dialogs/public_room_dialog.dart @@ -30,7 +30,9 @@ class PublicRoomDialog extends StatelessWidget { final result = await showFutureLoadingDialog( context: context, future: () async { - if (chunk != null && client.getRoomById(chunk.roomId) != null) { + if (chunk != null && + client.getRoomById(chunk.roomId) != null && + client.getRoomById(chunk.roomId)?.membership != Membership.leave) { return chunk.roomId; } final roomId = chunk != null && knock diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index 293b557..c2a0895 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -18,6 +18,8 @@ class Avatar extends StatelessWidget { final BorderRadius? borderRadius; final IconData? icon; final BorderSide? border; + final Color? backgroundColor; + final Color? textColor; const Avatar({ this.mxContent, @@ -30,6 +32,8 @@ class Avatar extends StatelessWidget { this.borderRadius, this.border, this.icon, + this.backgroundColor, + this.textColor, super.key, }); @@ -71,14 +75,16 @@ class Avatar extends StatelessWidget { height: size, placeholder: (_) => noPic ? Container( - decoration: BoxDecoration(color: name?.lightColorAvatar), + decoration: BoxDecoration( + color: backgroundColor ?? name?.lightColorAvatar, + ), alignment: Alignment.center, child: Text( fallbackLetters, textAlign: TextAlign.center, style: TextStyle( fontFamily: 'RobotoMono', - color: Colors.white, + color: textColor ?? Colors.white, fontWeight: FontWeight.bold, fontSize: (size / 2.5).roundToDouble(), ), diff --git a/pubspec.lock b/pubspec.lock index 431f0cc..20451ab 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1201,9 +1201,11 @@ packages: matrix: dependency: "direct main" description: - path: "/home/officialdakari/repos/matrix-dart-sdk" - relative: false - source: path + path: "." + ref: main + resolved-ref: f678376e3a4825a6e1a00d89585d6bae0843950c + url: "https://git.extera.xyz/OfficialDakari/matrix-dart-sdk.git" + source: git version: "3.0.1" meta: dependency: transitive From 7cab2cba7c3e5fcd5098160d1dbd733b1f3f6089 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:17:08 +0500 Subject: [PATCH 16/22] Add support for restricted join rule --- assets/l10n/intl_en.arb | 18 +++ assets/l10n/intl_ru.arb | 18 +++ .../chat_access_settings_controller.dart | 29 ++++- .../chat_access_settings_page.dart | 115 +++++++++++------- 4 files changed, 133 insertions(+), 47 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 3776fd5..cdb000e 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3154,6 +3154,24 @@ }, "restricted": "Restricted", "@restricted": {}, + }, + "spaceMemberOf": "Space member of {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Space member of {spaces} can knock", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + }, "knockRestricted": "Knock restricted", "@knockRestricted": {}, "goToSpace": "Go to space: {space}", diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index 01996ee..980f7b9 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -3154,6 +3154,24 @@ "space": {} }, "markAsUnread": "Отметить как непрочитанное", + + "spaceMemberOf": "Участники пространства {spaces}", + "@spaceMemberOf": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + } + }, + "spaceMemberOfCanKnock": "Участники пространства {spaces} (по запросу)", + "@spaceMemberOfCanKnock": { + "type": "String", + "placeholders": { + "spaces": { + "type": "String" + } + }, "userLevel": "{level} - Пользователь", "@userLevel": { "type": "String", diff --git a/lib/pages/chat_access_settings/chat_access_settings_controller.dart b/lib/pages/chat_access_settings/chat_access_settings_controller.dart index 3840ebb..c4b075a 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_controller.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_controller.dart @@ -26,6 +26,16 @@ class ChatAccessSettingsController extends State { bool historyVisibilityLoading = false; bool guestAccessLoading = false; Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!; + Set get knownSpaceParents => { + ...room.client.rooms.where( + (space) => + space.isSpace && + space.spaceChildren.any((child) => child.roomId == room.id), + ), + ...room.spaceParents + .map((parent) => room.client.getRoomById(parent.roomId ?? '')) + .whereType(), + }; String get roomVersion => room @@ -46,9 +56,12 @@ class ChatAccessSettingsController extends State { joinRules.remove(JoinRules.knock); } - // Not yet supported in FluffyChat: - joinRules.remove(JoinRules.restricted); - joinRules.remove(JoinRules.knockRestricted); + if (knownSpaceParents.isEmpty) { + joinRules.remove(JoinRules.restricted); + if (roomVersionInt != null && roomVersionInt <= 6) { + joinRules.remove(JoinRules.knockRestricted); + } + } // If an unsupported join rule is the current join rule, display it: final currentJoinRule = room.joinRules; @@ -64,7 +77,13 @@ class ChatAccessSettingsController extends State { }); try { - await room.setJoinRules(newJoinRules); + await room.setJoinRules( + newJoinRules, + allowConditionRoomId: {JoinRules.restricted, JoinRules.knockRestricted} + .contains(newJoinRules) + ? knownSpaceParents.first.id + : null, + ); } catch (e, s) { Logs().w('Unable to change join rules', e, s); if (mounted) { @@ -297,4 +316,4 @@ class ChatAccessSettingsController extends State { Widget build(BuildContext context) { return ChatAccessSettingsPageView(this); } -} +} \ No newline at end of file diff --git a/lib/pages/chat_access_settings/chat_access_settings_page.dart b/lib/pages/chat_access_settings/chat_access_settings_page.dart index dc11925..d6ad0de 100644 --- a/lib/pages/chat_access_settings/chat_access_settings_page.dart +++ b/lib/pages/chat_access_settings/chat_access_settings_page.dart @@ -45,19 +45,27 @@ class ChatAccessSettingsPageView extends StatelessWidget { ), ), ), - for (final historyVisibility in HistoryVisibility.values) - RadioListTile.adaptive( - title: Text( - historyVisibility - .getLocalizedString(MatrixLocals(L10n.of(context))), - ), - value: historyVisibility, - groupValue: room.historyVisibility, - onChanged: controller.historyVisibilityLoading || - !room.canChangeHistoryVisibility - ? null - : controller.setHistoryVisibility, + RadioGroup( + groupValue: room.historyVisibility, + onChanged: controller.historyVisibilityLoading || + !room.canChangeHistoryVisibility + ? (_) {} + : controller.setHistoryVisibility, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final historyVisibility in HistoryVisibility.values) + RadioListTile.adaptive( + title: Text( + historyVisibility.getLocalizedString( + MatrixLocals(L10n.of(context)), + ), + ), + value: historyVisibility, + ), + ], ), + ), Divider(color: theme.dividerColor), ListTile( title: Text( @@ -68,19 +76,28 @@ class ChatAccessSettingsPageView extends StatelessWidget { ), ), ), - for (final joinRule in controller.availableJoinRules) - if (joinRule != JoinRules.private) - RadioListTile.adaptive( - title: Text( - joinRule.localizedString(L10n.of(context)), - ), - value: joinRule, - groupValue: room.joinRules, - onChanged: controller.joinRulesLoading || - !room.canChangeJoinRules - ? null - : controller.setJoinRule, - ), + RadioGroup( + groupValue: room.joinRules, + onChanged: controller.setJoinRule, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final joinRule in controller.availableJoinRules) + if (joinRule != JoinRules.private) + RadioListTile.adaptive( + enabled: !controller.joinRulesLoading && + room.canChangeJoinRules, + title: Text( + joinRule.localizedString( + L10n.of(context), + controller.knownSpaceParents, + ), + ), + value: joinRule, + ), + ], + ), + ), Divider(color: theme.dividerColor), if ({JoinRules.public, JoinRules.knock} .contains(room.joinRules)) ...[ @@ -93,20 +110,26 @@ class ChatAccessSettingsPageView extends StatelessWidget { ), ), ), - for (final guestAccess in GuestAccess.values) - RadioListTile.adaptive( - title: Text( - guestAccess.getLocalizedString( - MatrixLocals(L10n.of(context)), - ), - ), - value: guestAccess, - groupValue: room.guestAccess, - onChanged: controller.guestAccessLoading || - !room.canChangeGuestAccess - ? null - : controller.setGuestAccess, + RadioGroup( + groupValue: room.guestAccess, + onChanged: controller.setGuestAccess, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final guestAccess in GuestAccess.values) + RadioListTile.adaptive( + enabled: !controller.guestAccessLoading && + room.canChangeGuestAccess, + title: Text( + guestAccess.getLocalizedString( + MatrixLocals(L10n.of(context)), + ), + ), + value: guestAccess, + ), + ], ), + ), Divider(color: theme.dividerColor), ListTile( title: Text( @@ -260,7 +283,7 @@ class _AliasListTile extends StatelessWidget { } extension JoinRulesDisplayString on JoinRules { - String localizedString(L10n l10n) { + String localizedString(L10n l10n, Set spaceParents) { switch (this) { case JoinRules.public: return l10n.anyoneCanJoin; @@ -271,9 +294,17 @@ extension JoinRulesDisplayString on JoinRules { case JoinRules.private: return l10n.noOneCanJoin; case JoinRules.restricted: - return l10n.restricted; + return l10n.spaceMemberOf( + spaceParents + .map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n))) + .join(', '), + ); case JoinRules.knockRestricted: - return l10n.knockRestricted; + return l10n.spaceMemberOfCanKnock( + spaceParents + .map((space) => space.getLocalizedDisplayname(MatrixLocals(l10n))) + .join(', '), + ); } } -} +} \ No newline at end of file From 277bc549909af2ad32ea3a6d4bac2fa19180dea8 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:19:14 +0500 Subject: [PATCH 17/22] chore: remove unused import --- .../flutter_matrix_dart_sdk_database/builder.dart | 1 - 1 file changed, 1 deletion(-) 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 f432bbe..d5c9918 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 @@ -10,7 +10,6 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:universal_html/html.dart' as html; import 'package:extera_next/config/app_config.dart'; -import 'package:extera_next/generated/l10n/l10n.dart'; import 'package:extera_next/utils/client_manager.dart'; import 'package:extera_next/utils/platform_infos.dart'; import 'cipher.dart'; From 287c5f05bb75bf7db27e9ef4af75e91a685cfc7c Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:19:46 +0500 Subject: [PATCH 18/22] fix: Create a subdirectory in the tmp directory --- .../builder.dart | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) 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 d5c9918..351339a 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 @@ -68,7 +68,12 @@ Future _constructDatabase(String clientName) async { Directory? fileStorageLocation; try { - fileStorageLocation = await getTemporaryDirectory(); + final tmpDir = await getTemporaryDirectory(); + final appTmpDir = Directory(join(tmpDir.path, clientName)); + if (!await appTmpDir.exists()) { + await appTmpDir.create(recursive: true); + } + fileStorageLocation = appTmpDir; } on MissingPlatformDirectoryException catch (_) { Logs().w( 'No temporary directory for file cache available on this platform.', @@ -105,11 +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, + singleInstance: false), ); return await MatrixSdkDatabase.init( @@ -148,4 +152,4 @@ Future _migrateLegacyLocation( await maybeOldFile.copy(sqlFilePath); await maybeOldFile.delete(); } -} \ No newline at end of file +} From 02573fa64523ac62f5751b91b1447ad1c7d5c864 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:26:17 +0500 Subject: [PATCH 19/22] attempt to fix video playing in background --- lib/pages/chat/events/video_player.dart | 15 --------------- lib/pages/image_viewer/image_viewer_view.dart | 2 +- lib/pages/image_viewer/video_player.dart | 10 +++++++++- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index 920db06..d6628e3 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -1,28 +1,17 @@ -import 'dart:io'; - import 'package:extera_next/pages/chat/events/html_message.dart'; import 'package:extera_next/pages/image_viewer/image_viewer.dart'; import 'package:extera_next/widgets/mxc_image.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:chewie/chewie.dart'; -import 'package:extera_next/generated/l10n/l10n.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:matrix/matrix.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:universal_html/html.dart' as html; -import 'package:video_player/video_player.dart'; import 'package:extera_next/config/app_config.dart'; -import 'package:extera_next/pages/chat/events/image_bubble.dart'; import 'package:extera_next/utils/file_description.dart'; -import 'package:extera_next/utils/localized_exception_extension.dart'; import 'package:extera_next/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:extera_next/utils/platform_infos.dart'; import 'package:extera_next/utils/url_launcher.dart'; import 'package:extera_next/widgets/blur_hash.dart'; -import '../../../utils/error_reporter.dart'; class EventVideoPlayer extends StatelessWidget { final Event event; @@ -128,8 +117,6 @@ class EventVideoPlayer extends StatelessWidget { ), ), if (fileDescription != null && - textColor != null && - linkColor != null && !event.isRichFileDescription) SizedBox( width: width, @@ -159,8 +146,6 @@ class EventVideoPlayer extends StatelessWidget { ), ), if (fileDescription != null && - textColor != null && - linkColor != null && event.isRichFileDescription) SizedBox( width: width, diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index 9adb2c7..6c70779 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -88,7 +88,7 @@ class ImageViewerView extends StatelessWidget { child: GestureDetector( // Ignore taps to not go back here: onTap: () {}, - child: EventVideoPlayer(event), + child: EventVideoPlayer(event, controller), ), ), ); diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart index 5d91dea..16d3b6e 100644 --- a/lib/pages/image_viewer/video_player.dart +++ b/lib/pages/image_viewer/video_player.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:extera_next/pages/image_viewer/image_viewer.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -18,9 +19,11 @@ import '../../widgets/mxc_image.dart'; class EventVideoPlayer extends StatefulWidget { final Event event; + final ImageViewerController ivController; const EventVideoPlayer( - this.event, { + this.event, + this.ivController, { super.key, }); @@ -69,6 +72,11 @@ class EventVideoPlayerState extends State { await videoPlayerController.initialize(); + if (widget.ivController.currentEvent.eventId != widget.event.eventId) { + dispose(); + return; + } + // Create a ChewieController on top. setState(() { _chewieController = ChewieController( From 6d330a18d51f028e155dbfff30ef3ca73c4c4ca6 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:28:27 +0500 Subject: [PATCH 20/22] remove "synthetic-package" from l10n.yaml --- assets/l10n/intl_en.arb | 1 - l10n.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index cdb000e..675f83d 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3154,7 +3154,6 @@ }, "restricted": "Restricted", "@restricted": {}, - }, "spaceMemberOf": "Space member of {spaces}", "@spaceMemberOf": { "type": "String", diff --git a/l10n.yaml b/l10n.yaml index 8cba8e1..2323963 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -5,5 +5,4 @@ output-class: L10n preferred-supported-locales: ["en"] use-deferred-loading: true nullable-getter: false -synthetic-package: false output-dir: lib/generated/l10n \ No newline at end of file From 9ff857d0d0a679559f7c002aa03c02f39dc71d49 Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:32:49 +0500 Subject: [PATCH 21/22] fix formatting in l10n files --- assets/l10n/intl_en.arb | 3 ++- assets/l10n/intl_ru.arb | 3 ++- lib/generated/l10n/l10n.dart | 12 ++++++++++++ lib/generated/l10n/l10n_ar.dart | 10 ++++++++++ lib/generated/l10n/l10n_be.dart | 10 ++++++++++ lib/generated/l10n/l10n_bn.dart | 10 ++++++++++ lib/generated/l10n/l10n_bo.dart | 10 ++++++++++ lib/generated/l10n/l10n_ca.dart | 10 ++++++++++ lib/generated/l10n/l10n_cs.dart | 10 ++++++++++ lib/generated/l10n/l10n_de.dart | 10 ++++++++++ lib/generated/l10n/l10n_el.dart | 10 ++++++++++ lib/generated/l10n/l10n_en.dart | 10 ++++++++++ lib/generated/l10n/l10n_eo.dart | 10 ++++++++++ lib/generated/l10n/l10n_es.dart | 10 ++++++++++ lib/generated/l10n/l10n_et.dart | 10 ++++++++++ lib/generated/l10n/l10n_eu.dart | 10 ++++++++++ lib/generated/l10n/l10n_fa.dart | 10 ++++++++++ lib/generated/l10n/l10n_fi.dart | 10 ++++++++++ lib/generated/l10n/l10n_fil.dart | 10 ++++++++++ lib/generated/l10n/l10n_fr.dart | 10 ++++++++++ lib/generated/l10n/l10n_ga.dart | 10 ++++++++++ lib/generated/l10n/l10n_gl.dart | 10 ++++++++++ lib/generated/l10n/l10n_he.dart | 10 ++++++++++ lib/generated/l10n/l10n_hi.dart | 10 ++++++++++ lib/generated/l10n/l10n_hr.dart | 10 ++++++++++ lib/generated/l10n/l10n_hu.dart | 10 ++++++++++ lib/generated/l10n/l10n_ia.dart | 10 ++++++++++ lib/generated/l10n/l10n_id.dart | 10 ++++++++++ lib/generated/l10n/l10n_ie.dart | 10 ++++++++++ lib/generated/l10n/l10n_it.dart | 10 ++++++++++ lib/generated/l10n/l10n_ja.dart | 10 ++++++++++ lib/generated/l10n/l10n_ka.dart | 10 ++++++++++ lib/generated/l10n/l10n_ko.dart | 10 ++++++++++ lib/generated/l10n/l10n_lt.dart | 10 ++++++++++ lib/generated/l10n/l10n_lv.dart | 10 ++++++++++ lib/generated/l10n/l10n_nb.dart | 10 ++++++++++ lib/generated/l10n/l10n_nl.dart | 10 ++++++++++ lib/generated/l10n/l10n_pl.dart | 10 ++++++++++ lib/generated/l10n/l10n_pt.dart | 10 ++++++++++ lib/generated/l10n/l10n_ro.dart | 10 ++++++++++ lib/generated/l10n/l10n_ru.dart | 20 +++++++++++++++----- lib/generated/l10n/l10n_sk.dart | 10 ++++++++++ lib/generated/l10n/l10n_sl.dart | 10 ++++++++++ lib/generated/l10n/l10n_sr.dart | 10 ++++++++++ lib/generated/l10n/l10n_sv.dart | 10 ++++++++++ lib/generated/l10n/l10n_ta.dart | 10 ++++++++++ lib/generated/l10n/l10n_te.dart | 10 ++++++++++ lib/generated/l10n/l10n_th.dart | 10 ++++++++++ lib/generated/l10n/l10n_tr.dart | 10 ++++++++++ lib/generated/l10n/l10n_uk.dart | 10 ++++++++++ lib/generated/l10n/l10n_vi.dart | 10 ++++++++++ lib/generated/l10n/l10n_zh.dart | 10 ++++++++++ 52 files changed, 511 insertions(+), 7 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 675f83d..9e2c7ca 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3170,7 +3170,8 @@ "spaces": { "type": "String" } - }, + } + }, "knockRestricted": "Knock restricted", "@knockRestricted": {}, "goToSpace": "Go to space: {space}", diff --git a/assets/l10n/intl_ru.arb b/assets/l10n/intl_ru.arb index 980f7b9..57b22f5 100644 --- a/assets/l10n/intl_ru.arb +++ b/assets/l10n/intl_ru.arb @@ -3171,7 +3171,8 @@ "spaces": { "type": "String" } - }, + } + }, "userLevel": "{level} - Пользователь", "@userLevel": { "type": "String", diff --git a/lib/generated/l10n/l10n.dart b/lib/generated/l10n/l10n.dart index 10cda94..338dba3 100644 --- a/lib/generated/l10n/l10n.dart +++ b/lib/generated/l10n/l10n.dart @@ -4401,6 +4401,18 @@ abstract class L10n { /// **'Restricted'** String get restricted; + /// No description provided for @spaceMemberOf. + /// + /// In en, this message translates to: + /// **'Space member of {spaces}'** + String spaceMemberOf(String spaces); + + /// No description provided for @spaceMemberOfCanKnock. + /// + /// In en, this message translates to: + /// **'Space member of {spaces} can knock'** + String spaceMemberOfCanKnock(String spaces); + /// No description provided for @knockRestricted. /// /// In en, this message translates to: diff --git a/lib/generated/l10n/l10n_ar.dart b/lib/generated/l10n/l10n_ar.dart index 7dbad83..0efcb6d 100644 --- a/lib/generated/l10n/l10n_ar.dart +++ b/lib/generated/l10n/l10n_ar.dart @@ -2431,6 +2431,16 @@ class L10nAr extends L10n { @override String get restricted => 'مقيد'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'قيود النقر'; diff --git a/lib/generated/l10n/l10n_be.dart b/lib/generated/l10n/l10n_be.dart index fbc64d4..9235913 100644 --- a/lib/generated/l10n/l10n_be.dart +++ b/lib/generated/l10n/l10n_be.dart @@ -2446,6 +2446,16 @@ class L10nBe extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_bn.dart b/lib/generated/l10n/l10n_bn.dart index 81ad17d..f5bfc69 100644 --- a/lib/generated/l10n/l10n_bn.dart +++ b/lib/generated/l10n/l10n_bn.dart @@ -2446,6 +2446,16 @@ class L10nBn extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_bo.dart b/lib/generated/l10n/l10n_bo.dart index b4af5c3..badf080 100644 --- a/lib/generated/l10n/l10n_bo.dart +++ b/lib/generated/l10n/l10n_bo.dart @@ -2446,6 +2446,16 @@ class L10nBo extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_ca.dart b/lib/generated/l10n/l10n_ca.dart index d1fb8a6..6969bc9 100644 --- a/lib/generated/l10n/l10n_ca.dart +++ b/lib/generated/l10n/l10n_ca.dart @@ -2470,6 +2470,16 @@ class L10nCa extends L10n { @override String get restricted => 'Restringit'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'No es pot picar a la porta'; diff --git a/lib/generated/l10n/l10n_cs.dart b/lib/generated/l10n/l10n_cs.dart index c0cdcea..68d9c23 100644 --- a/lib/generated/l10n/l10n_cs.dart +++ b/lib/generated/l10n/l10n_cs.dart @@ -2450,6 +2450,16 @@ class L10nCs extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_de.dart b/lib/generated/l10n/l10n_de.dart index 51ab151..5fc9004 100644 --- a/lib/generated/l10n/l10n_de.dart +++ b/lib/generated/l10n/l10n_de.dart @@ -2465,6 +2465,16 @@ class L10nDe extends L10n { @override String get restricted => 'Beschränkt'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Anklopfen beschränkt'; diff --git a/lib/generated/l10n/l10n_el.dart b/lib/generated/l10n/l10n_el.dart index f649d09..2c784d1 100644 --- a/lib/generated/l10n/l10n_el.dart +++ b/lib/generated/l10n/l10n_el.dart @@ -2447,6 +2447,16 @@ class L10nEl extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_en.dart b/lib/generated/l10n/l10n_en.dart index 4a5fa02..b1f3b69 100644 --- a/lib/generated/l10n/l10n_en.dart +++ b/lib/generated/l10n/l10n_en.dart @@ -2446,6 +2446,16 @@ class L10nEn extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_eo.dart b/lib/generated/l10n/l10n_eo.dart index d9e1f12..30939c7 100644 --- a/lib/generated/l10n/l10n_eo.dart +++ b/lib/generated/l10n/l10n_eo.dart @@ -2453,6 +2453,16 @@ class L10nEo extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_es.dart b/lib/generated/l10n/l10n_es.dart index 533be37..9aaa079 100644 --- a/lib/generated/l10n/l10n_es.dart +++ b/lib/generated/l10n/l10n_es.dart @@ -2469,6 +2469,16 @@ class L10nEs extends L10n { @override String get restricted => 'Restringido'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Aviso restringido'; diff --git a/lib/generated/l10n/l10n_et.dart b/lib/generated/l10n/l10n_et.dart index da47eea..3176483 100644 --- a/lib/generated/l10n/l10n_et.dart +++ b/lib/generated/l10n/l10n_et.dart @@ -2464,6 +2464,16 @@ class L10nEt extends L10n { @override String get restricted => 'Piiratud'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Koputa piiratud ligipääsuga jututoa uksele'; diff --git a/lib/generated/l10n/l10n_eu.dart b/lib/generated/l10n/l10n_eu.dart index 1bde741..1f359ea 100644 --- a/lib/generated/l10n/l10n_eu.dart +++ b/lib/generated/l10n/l10n_eu.dart @@ -2456,6 +2456,16 @@ class L10nEu extends L10n { @override String get restricted => 'Mugatuta'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Eskatu baimena sarrera mugatua duen txatean'; diff --git a/lib/generated/l10n/l10n_fa.dart b/lib/generated/l10n/l10n_fa.dart index 43464c6..d8474a0 100644 --- a/lib/generated/l10n/l10n_fa.dart +++ b/lib/generated/l10n/l10n_fa.dart @@ -2449,6 +2449,16 @@ class L10nFa extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_fi.dart b/lib/generated/l10n/l10n_fi.dart index aae290d..b2e0e15 100644 --- a/lib/generated/l10n/l10n_fi.dart +++ b/lib/generated/l10n/l10n_fi.dart @@ -2455,6 +2455,16 @@ class L10nFi extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_fil.dart b/lib/generated/l10n/l10n_fil.dart index 222201e..04872fd 100644 --- a/lib/generated/l10n/l10n_fil.dart +++ b/lib/generated/l10n/l10n_fil.dart @@ -2452,6 +2452,16 @@ class L10nFil extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_fr.dart b/lib/generated/l10n/l10n_fr.dart index 283460f..32308c7 100644 --- a/lib/generated/l10n/l10n_fr.dart +++ b/lib/generated/l10n/l10n_fr.dart @@ -2490,6 +2490,16 @@ class L10nFr extends L10n { @override String get restricted => 'Limité'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Frapper à la porte limité'; diff --git a/lib/generated/l10n/l10n_ga.dart b/lib/generated/l10n/l10n_ga.dart index 9febce3..7ed95c9 100644 --- a/lib/generated/l10n/l10n_ga.dart +++ b/lib/generated/l10n/l10n_ga.dart @@ -2475,6 +2475,16 @@ class L10nGa extends L10n { @override String get restricted => 'Srianta'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Cnoc Mhuire srianta'; diff --git a/lib/generated/l10n/l10n_gl.dart b/lib/generated/l10n/l10n_gl.dart index 268efdb..5f5eed9 100644 --- a/lib/generated/l10n/l10n_gl.dart +++ b/lib/generated/l10n/l10n_gl.dart @@ -2458,6 +2458,16 @@ class L10nGl extends L10n { @override String get restricted => 'Non accesible'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Peta á porta'; diff --git a/lib/generated/l10n/l10n_he.dart b/lib/generated/l10n/l10n_he.dart index aaab0d9..39eb2cb 100644 --- a/lib/generated/l10n/l10n_he.dart +++ b/lib/generated/l10n/l10n_he.dart @@ -2441,6 +2441,16 @@ class L10nHe extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_hi.dart b/lib/generated/l10n/l10n_hi.dart index 1528cac..50a282c 100644 --- a/lib/generated/l10n/l10n_hi.dart +++ b/lib/generated/l10n/l10n_hi.dart @@ -2446,6 +2446,16 @@ class L10nHi extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_hr.dart b/lib/generated/l10n/l10n_hr.dart index 9f01e8b..6af812d 100644 --- a/lib/generated/l10n/l10n_hr.dart +++ b/lib/generated/l10n/l10n_hr.dart @@ -2455,6 +2455,16 @@ class L10nHr extends L10n { @override String get restricted => 'Ograničeni'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Pokucaj na ograničene sobe'; diff --git a/lib/generated/l10n/l10n_hu.dart b/lib/generated/l10n/l10n_hu.dart index c2dc2ba..b7e15e2 100644 --- a/lib/generated/l10n/l10n_hu.dart +++ b/lib/generated/l10n/l10n_hu.dart @@ -2456,6 +2456,16 @@ class L10nHu extends L10n { @override String get restricted => 'Korlátozott'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Kopogás korlátozva'; diff --git a/lib/generated/l10n/l10n_ia.dart b/lib/generated/l10n/l10n_ia.dart index 6a83aaa..8fcafa0 100644 --- a/lib/generated/l10n/l10n_ia.dart +++ b/lib/generated/l10n/l10n_ia.dart @@ -2446,6 +2446,16 @@ class L10nIa extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_id.dart b/lib/generated/l10n/l10n_id.dart index b162b23..3968737 100644 --- a/lib/generated/l10n/l10n_id.dart +++ b/lib/generated/l10n/l10n_id.dart @@ -2463,6 +2463,16 @@ class L10nId extends L10n { @override String get restricted => 'Dibatasi'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Ketukan dibatasi'; diff --git a/lib/generated/l10n/l10n_ie.dart b/lib/generated/l10n/l10n_ie.dart index 7ee3897..4a06704 100644 --- a/lib/generated/l10n/l10n_ie.dart +++ b/lib/generated/l10n/l10n_ie.dart @@ -2445,6 +2445,16 @@ class L10nIe extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_it.dart b/lib/generated/l10n/l10n_it.dart index 4c6a77f..9012c46 100644 --- a/lib/generated/l10n/l10n_it.dart +++ b/lib/generated/l10n/l10n_it.dart @@ -2470,6 +2470,16 @@ class L10nIt extends L10n { @override String get restricted => 'Limitato'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Limitato al bussare'; diff --git a/lib/generated/l10n/l10n_ja.dart b/lib/generated/l10n/l10n_ja.dart index e3c855f..82bca14 100644 --- a/lib/generated/l10n/l10n_ja.dart +++ b/lib/generated/l10n/l10n_ja.dart @@ -2415,6 +2415,16 @@ class L10nJa extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_ka.dart b/lib/generated/l10n/l10n_ka.dart index eb09a4d..477fe69 100644 --- a/lib/generated/l10n/l10n_ka.dart +++ b/lib/generated/l10n/l10n_ka.dart @@ -2448,6 +2448,16 @@ class L10nKa extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_ko.dart b/lib/generated/l10n/l10n_ko.dart index bad2623..47bf4a0 100644 --- a/lib/generated/l10n/l10n_ko.dart +++ b/lib/generated/l10n/l10n_ko.dart @@ -2397,6 +2397,16 @@ class L10nKo extends L10n { @override String get restricted => '스페이스 멤버로 제한'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => '스페이스 멤버만 참가 요청 가능'; diff --git a/lib/generated/l10n/l10n_lt.dart b/lib/generated/l10n/l10n_lt.dart index 3f437b6..028443b 100644 --- a/lib/generated/l10n/l10n_lt.dart +++ b/lib/generated/l10n/l10n_lt.dart @@ -2452,6 +2452,16 @@ class L10nLt extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_lv.dart b/lib/generated/l10n/l10n_lv.dart index 572f204..c106c8f 100644 --- a/lib/generated/l10n/l10n_lv.dart +++ b/lib/generated/l10n/l10n_lv.dart @@ -2463,6 +2463,16 @@ class L10nLv extends L10n { @override String get restricted => 'Ierobežots'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Pieklauvēt ierobežotajiem'; diff --git a/lib/generated/l10n/l10n_nb.dart b/lib/generated/l10n/l10n_nb.dart index 56fb2a8..eb42c63 100644 --- a/lib/generated/l10n/l10n_nb.dart +++ b/lib/generated/l10n/l10n_nb.dart @@ -2449,6 +2449,16 @@ class L10nNb extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_nl.dart b/lib/generated/l10n/l10n_nl.dart index 893028d..0162add 100644 --- a/lib/generated/l10n/l10n_nl.dart +++ b/lib/generated/l10n/l10n_nl.dart @@ -2452,6 +2452,16 @@ class L10nNl extends L10n { @override String get restricted => 'Beperkt'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Kloppen is beperkt'; diff --git a/lib/generated/l10n/l10n_pl.dart b/lib/generated/l10n/l10n_pl.dart index 0df608c..7222ce9 100644 --- a/lib/generated/l10n/l10n_pl.dart +++ b/lib/generated/l10n/l10n_pl.dart @@ -2462,6 +2462,16 @@ class L10nPl extends L10n { @override String get restricted => 'Ograniczone'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Pukanie jest ograniczone'; diff --git a/lib/generated/l10n/l10n_pt.dart b/lib/generated/l10n/l10n_pt.dart index 681ce6e..87f4ddd 100644 --- a/lib/generated/l10n/l10n_pt.dart +++ b/lib/generated/l10n/l10n_pt.dart @@ -2446,6 +2446,16 @@ class L10nPt extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_ro.dart b/lib/generated/l10n/l10n_ro.dart index 30fe5af..4570862 100644 --- a/lib/generated/l10n/l10n_ro.dart +++ b/lib/generated/l10n/l10n_ro.dart @@ -2463,6 +2463,16 @@ class L10nRo extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_ru.dart b/lib/generated/l10n/l10n_ru.dart index 95528bf..f7b4396 100644 --- a/lib/generated/l10n/l10n_ru.dart +++ b/lib/generated/l10n/l10n_ru.dart @@ -25,20 +25,20 @@ class L10nRu extends L10n { String get resume => 'Продолжить'; @override - String get newSubSpace => 'New sub space'; + String get newSubSpace => 'Новое подпространство'; @override - String get moveToDifferentSpace => 'Move to different space'; + String get moveToDifferentSpace => 'Перенести в другое пространство'; @override - String get moveUp => 'Move up'; + String get moveUp => 'Вверх'; @override - String get moveDown => 'Move down'; + String get moveDown => 'Вниз'; @override String get removeFromSpaceDescription => - 'The chat will be removed from the space but still appear in your chat list.'; + 'Эта комната будет удалена из пространства, но останется в Вашем списке чатов.'; @override String get endPoll => 'Завершить опрос'; @@ -2451,6 +2451,16 @@ class L10nRu extends L10n { @override String get restricted => 'Ограничено'; + @override + String spaceMemberOf(String spaces) { + return 'Участники пространства $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Участники пространства $spaces (по запросу)'; + } + @override String get knockRestricted => 'Ограничено + по запросу'; diff --git a/lib/generated/l10n/l10n_sk.dart b/lib/generated/l10n/l10n_sk.dart index db59bd9..2187884 100644 --- a/lib/generated/l10n/l10n_sk.dart +++ b/lib/generated/l10n/l10n_sk.dart @@ -2447,6 +2447,16 @@ class L10nSk extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_sl.dart b/lib/generated/l10n/l10n_sl.dart index 3ed0849..4566d42 100644 --- a/lib/generated/l10n/l10n_sl.dart +++ b/lib/generated/l10n/l10n_sl.dart @@ -2450,6 +2450,16 @@ class L10nSl extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_sr.dart b/lib/generated/l10n/l10n_sr.dart index e3955ed..175d670 100644 --- a/lib/generated/l10n/l10n_sr.dart +++ b/lib/generated/l10n/l10n_sr.dart @@ -2442,6 +2442,16 @@ class L10nSr extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_sv.dart b/lib/generated/l10n/l10n_sv.dart index 8c80e17..761f2f1 100644 --- a/lib/generated/l10n/l10n_sv.dart +++ b/lib/generated/l10n/l10n_sv.dart @@ -2456,6 +2456,16 @@ class L10nSv extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_ta.dart b/lib/generated/l10n/l10n_ta.dart index 8981c25..d07856d 100644 --- a/lib/generated/l10n/l10n_ta.dart +++ b/lib/generated/l10n/l10n_ta.dart @@ -2477,6 +2477,16 @@ class L10nTa extends L10n { @override String get restricted => 'தடைசெய்யப்பட்டது'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'நாக் தடை'; diff --git a/lib/generated/l10n/l10n_te.dart b/lib/generated/l10n/l10n_te.dart index 6b5ece6..bbeeaac 100644 --- a/lib/generated/l10n/l10n_te.dart +++ b/lib/generated/l10n/l10n_te.dart @@ -2446,6 +2446,16 @@ class L10nTe extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_th.dart b/lib/generated/l10n/l10n_th.dart index dd42ebb..71c7a29 100644 --- a/lib/generated/l10n/l10n_th.dart +++ b/lib/generated/l10n/l10n_th.dart @@ -2448,6 +2448,16 @@ class L10nTh extends L10n { @override String get restricted => 'Restricted'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_tr.dart b/lib/generated/l10n/l10n_tr.dart index 75e2758..e503551 100644 --- a/lib/generated/l10n/l10n_tr.dart +++ b/lib/generated/l10n/l10n_tr.dart @@ -2456,6 +2456,16 @@ class L10nTr extends L10n { @override String get restricted => 'Kısıtlı'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Tıklatma kısıtlı'; diff --git a/lib/generated/l10n/l10n_uk.dart b/lib/generated/l10n/l10n_uk.dart index a692a7b..9a59c4e 100644 --- a/lib/generated/l10n/l10n_uk.dart +++ b/lib/generated/l10n/l10n_uk.dart @@ -2459,6 +2459,16 @@ class L10nUk extends L10n { @override String get restricted => 'Обмежено'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Стук обмежено'; diff --git a/lib/generated/l10n/l10n_vi.dart b/lib/generated/l10n/l10n_vi.dart index 330b6a5..c4058c8 100644 --- a/lib/generated/l10n/l10n_vi.dart +++ b/lib/generated/l10n/l10n_vi.dart @@ -2445,6 +2445,16 @@ class L10nVi extends L10n { @override String get restricted => 'Bị hạn chế'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => 'Knock restricted'; diff --git a/lib/generated/l10n/l10n_zh.dart b/lib/generated/l10n/l10n_zh.dart index 2a9aece..87ea9b5 100644 --- a/lib/generated/l10n/l10n_zh.dart +++ b/lib/generated/l10n/l10n_zh.dart @@ -2371,6 +2371,16 @@ class L10nZh extends L10n { @override String get restricted => '受限'; + @override + String spaceMemberOf(String spaces) { + return 'Space member of $spaces'; + } + + @override + String spaceMemberOfCanKnock(String spaces) { + return 'Space member of $spaces can knock'; + } + @override String get knockRestricted => '“请求加入”请求受限'; From efb8696656df2c841ae0ba23323366de01552a1c Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Sun, 2 Nov 2025 18:41:24 +0500 Subject: [PATCH 22/22] minor chores --- .../synapse_admin_extension.dart | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/utils/matrix_sdk_extensions/synapse_admin_extension.dart b/lib/utils/matrix_sdk_extensions/synapse_admin_extension.dart index 65c5c19..a1de8d4 100644 --- a/lib/utils/matrix_sdk_extensions/synapse_admin_extension.dart +++ b/lib/utils/matrix_sdk_extensions/synapse_admin_extension.dart @@ -6,12 +6,12 @@ import 'package:matrix/matrix.dart' as matrix; extension SynapseAdmin on matrix.Client { Future> getEventReports({int from = 0, int limit = 10}) async { final requestUri = Uri( - path: '/_synapse/admin/v1/event_reports', - query: 'from=$from&limit=$limit&order_by=received_ts&dir=b' - ); + path: '/_synapse/admin/v1/event_reports', + query: 'from=$from&limit=$limit&order_by=received_ts&dir=b', + ); if (baseUri == null) return []; - print(baseUri!.resolveUri(requestUri).toString()); + final request = Request('GET', baseUri!.resolveUri(requestUri)); request.headers['authorization'] = 'Bearer $accessToken'; final response = await httpClient.send(request); @@ -40,14 +40,13 @@ extension SynapseAdmin on matrix.Client { if (room == null) return null; - print('Event content: ${jsonEncode(json['event_json'])}'); - - return new matrix.Event( + return matrix.Event( content: json['event_json']['content'], type: json['event_json']['type'], eventId: json['event_id'], senderId: json['sender'], - originServerTs: DateTime.fromMillisecondsSinceEpoch(json['event_json']['origin_server_ts']), + originServerTs: DateTime.fromMillisecondsSinceEpoch( + json['event_json']['origin_server_ts']), room: room, ); }