From c784094a4c09fedc30473ae1d5bd8d3ca83cf78a Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 00:00:39 -0400 Subject: [PATCH 01/94] add some more message parses --- lib/controllers/message_controller.dart | 46 +++++++++++-------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index d84aabb..5f028a9 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -91,6 +91,14 @@ class MessageController extends AsyncNotifier { ) as TextMessage; + Message toSystemMessage(String content) => Message.system( + metadata: {...metadata, "body": content}, + id: config.event.eventId, + authorId: event.authorId, + deliveredAt: config.event.timestamp, + text: content, + ); + return switch (type) { "m.room.encrypted" => asText.copyWith( text: "Unable to decrypt message.", @@ -132,33 +140,21 @@ class MessageController extends AsyncNotifier { "m.room.member" => content["membership"] == event.unsigned["prev_content"]?["membership"] ? null - : Message.system( - metadata: { - ...metadata, - "body": - "${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) { - "invite" => "was invited to", - "join" => "joined", - "leave" => "left", - "knock" => "asked to join", - "ban" => "was banned from", - _ => "did something relating to", - }} the room.", - }, - id: config.event.eventId, - authorId: event.authorId, - deliveredAt: config.event.timestamp, - text: - "${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) { - "invite" => "was invited to", - "join" => "joined", - "leave" => "left", - "knock" => "asked to join", - "ban" => "was banned from", - _ => "did something relating to", - }} the room.", + : toSystemMessage( + "${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) { + "invite" => "was invited to", + "join" => "joined", + "leave" => event.authorId == event.stateKey ? "was kicked" : "left", + "ban" => "was banned from", + "knock" => "asked to join", + _ => "did something relating to", + }} the room.", ), + "m.room.server_acl" => toSystemMessage( + "${event.authorId} updated the server ban list.", + ), + "m.room.redaction" => config.alwaysReturn ? asText.copyWith( From 18ee13901c31957ed07478d9b7a46628b0190d8e Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 00:04:06 -0400 Subject: [PATCH 02/94] fix error caused by c10y 779 --- lib/controllers/client_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 37dde3d..0f6fc60 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -184,7 +184,7 @@ class ClientController extends AsyncNotifier { Future> getRoomState(GetRoomStateRequest request) async { final response = - (await _sendCommand("get_room_state", request.toJson())) as List; + (await _sendCommand("get_room_state", request.toJson())) as List? ?? []; return response.map((event) => Event.fromJson(event)).toIList(); } From eaf1f3a1784125372963b282ba70fe8a574ff6d3 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 01:14:27 -0400 Subject: [PATCH 03/94] Fix inverted logic in message controller Signed-off-by: Henry-Hiles --- lib/controllers/message_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index 5f028a9..51e4287 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -144,7 +144,7 @@ class MessageController extends AsyncNotifier { "${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) { "invite" => "was invited to", "join" => "joined", - "leave" => event.authorId == event.stateKey ? "was kicked" : "left", + "leave" => event.authorId == event.stateKey ? "left" : "was kicked", "ban" => "was banned from", "knock" => "asked to join", _ => "did something relating to", From ecc40bfe49231e5f42d8612e676233da57ae0511 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 10:20:33 -0400 Subject: [PATCH 04/94] don't auto dispose author controller Signed-off-by: Henry-Hiles --- lib/controllers/author_controller.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/controllers/author_controller.dart b/lib/controllers/author_controller.dart index 72b0f72..ed4e64c 100644 --- a/lib/controllers/author_controller.dart +++ b/lib/controllers/author_controller.dart @@ -44,8 +44,7 @@ class AuthorController extends AsyncNotifier { ); } - static final provider = AsyncNotifierProvider.family - .autoDispose( + static final provider = AsyncNotifierProvider.family( AuthorController.new, ); } From 92e520632661715d7dc79c9cc2c4aa0ae12067ce Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 11:39:43 -0400 Subject: [PATCH 05/94] Workaround for c10y 779 --- lib/controllers/client_controller.dart | 10 +++++++--- lib/models/requests/get_room_state_request.dart | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 0f6fc60..56f606f 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -183,9 +183,13 @@ class ClientController extends AsyncNotifier { // })); Future> getRoomState(GetRoomStateRequest request) async { - final response = - (await _sendCommand("get_room_state", request.toJson())) as List? ?? []; - return response.map((event) => Event.fromJson(event)).toIList(); + Future getState(GetRoomStateRequest request) => + _sendCommand("get_room_state", request.toJson()) as Future; + final response = await getState(request); + + return (response ?? await getState(request.copyWith(refetch: true)) ?? []) + .map((event) => Event.fromJson(event)) + .toIList(); } Future?> getRelatedEvents( diff --git a/lib/models/requests/get_room_state_request.dart b/lib/models/requests/get_room_state_request.dart index de66b72..8ee05f0 100644 --- a/lib/models/requests/get_room_state_request.dart +++ b/lib/models/requests/get_room_state_request.dart @@ -6,6 +6,7 @@ part "get_room_state_request.g.dart"; abstract class GetRoomStateRequest with _$GetRoomStateRequest { const factory GetRoomStateRequest({ required String roomId, + @Default(false) bool refetch, @Default(false) bool fetchMembers, @Default(false) bool includeMembers, }) = _GetRoomStateRequest; From e2d29439d595e3c8b35149e1cc3712b37bd2c1f2 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 11:53:56 -0400 Subject: [PATCH 06/94] Fix state type error --- lib/controllers/client_controller.dart | 4 ++-- lib/helpers/extensions/scheme_to_theme.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 56f606f..d8315c4 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -183,8 +183,8 @@ class ClientController extends AsyncNotifier { // })); Future> getRoomState(GetRoomStateRequest request) async { - Future getState(GetRoomStateRequest request) => - _sendCommand("get_room_state", request.toJson()) as Future; + Future getState(GetRoomStateRequest request) async => + (await _sendCommand("get_room_state", request.toJson())) as List?; final response = await getState(request); return (response ?? await getState(request.copyWith(refetch: true)) ?? []) diff --git a/lib/helpers/extensions/scheme_to_theme.dart b/lib/helpers/extensions/scheme_to_theme.dart index aff5d52..08c0ba6 100644 --- a/lib/helpers/extensions/scheme_to_theme.dart +++ b/lib/helpers/extensions/scheme_to_theme.dart @@ -8,7 +8,7 @@ extension SchemeToTheme on ColorScheme { backgroundColor: surfaceContainerLow, ), textTheme: ThemeData( - fontFamilyFallback: ["sans"], + fontFamilyFallback: ["sans", "emoji"], brightness: brightness, ).textTheme, inputDecorationTheme: const InputDecorationTheme( From e0ba99d9b9742de91ae78d4451f78871815947db Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 12:05:44 -0400 Subject: [PATCH 07/94] cache inline images (e.g. emojis) --- lib/widgets/chat_page/html/html.dart | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index dcc1d49..8e6b919 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -1,8 +1,10 @@ +import "package:cross_cache/cross_cache.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart"; import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; @@ -58,18 +60,21 @@ class Html extends ConsumerWidget { ? SizedBox.shrink() : InlineCustomWidget( alignment: PlaceholderAlignment.middle, - child: Image.network( - Uri.parse(element.attributes["src"]!) - .mxcToHttps( - ref.watch( - ClientStateController.provider.select( - (value) => value?.homeserverUrl, - ), - ) ?? - "", - ) - .toString(), - headers: ref.headers, + child: Image( + image: CachedNetworkImage( + Uri.parse(element.attributes["src"]!) + .mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value?.homeserverUrl, + ), + ) ?? + "", + ) + .toString(), + ref.watch(CrossCacheController.provider), + headers: ref.headers, + ), errorBuilder: (_, error, _) => Text( "Image Failed to Load", style: TextStyle( From 60be7aaf721b72bf3aaf6218111160bd74dbf240 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 14:14:11 -0400 Subject: [PATCH 08/94] don't pass room around, use many watches --- lib/controllers/author_controller.dart | 23 +-- lib/controllers/members_controller.dart | 20 +-- lib/controllers/room_chat_controller.dart | 6 +- lib/models/configs/author_config.dart | 14 -- lib/widgets/chat_page/composer/chat_box.dart | 30 ++-- .../chat_page/composer/mention_overlay.dart | 7 +- .../chat_page/composer/relation_preview.dart | 6 +- .../lazy_loading/message_avatar.dart | 9 +- .../lazy_loading/message_displayname.dart | 9 +- lib/widgets/chat_page/member_list.dart | 6 +- lib/widgets/chat_page/reply_widget.dart | 135 +++++++++--------- lib/widgets/chat_page/room_appbar.dart | 96 +++++++------ lib/widgets/chat_page/room_chat.dart | 59 +++++--- .../chat_page/wrappers/message_wrapper.dart | 13 +- .../wrappers/text_message_wrapper.dart | 5 - 15 files changed, 207 insertions(+), 231 deletions(-) delete mode 100644 lib/models/configs/author_config.dart diff --git a/lib/controllers/author_controller.dart b/lib/controllers/author_controller.dart index ed4e64c..fc35ed3 100644 --- a/lib/controllers/author_controller.dart +++ b/lib/controllers/author_controller.dart @@ -1,31 +1,31 @@ import "dart:async"; import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/members_controller.dart"; -import "package:nexus/models/configs/author_config.dart"; import "package:nexus/models/membership.dart"; class AuthorController extends AsyncNotifier { - final AuthorConfig config; - AuthorController(this.config); + final Message message; + AuthorController(this.message); @override Future build() async { final member = await ref.watch( - MembersController.provider(config.room).selectAsync( + MembersController.provider.selectAsync( (value) => value.firstWhereOrNull( - (membership) => membership.userId == config.message.authorId, + (membership) => membership.userId == message.authorId, ), ), ); - final pmp = config.message.metadata?["pmp"] == null + final pmp = message.metadata?["pmp"] == null ? null : Membership.fromContent( - IMap(config.message.metadata?["pmp"]), - config.message.authorId, + IMap(message.metadata?["pmp"]), + message.authorId, ref.watch( ClientStateController.provider.select( (value) => value?.homeserverUrl, @@ -39,12 +39,13 @@ class AuthorController extends AsyncNotifier { displayName: pmp?.displayName ?? member?.displayName ?? - config.message.authorId.substring(1).split(":").first, - userId: config.message.authorId, + message.authorId.substring(1).split(":").first, + userId: message.authorId, ); } - static final provider = AsyncNotifierProvider.family( + static final provider = + AsyncNotifierProvider.family( AuthorController.new, ); } diff --git a/lib/controllers/members_controller.dart b/lib/controllers/members_controller.dart index 8d79f71..2cf7541 100644 --- a/lib/controllers/members_controller.dart +++ b/lib/controllers/members_controller.dart @@ -2,24 +2,28 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/models/membership.dart"; import "package:nexus/models/requests/get_room_state_request.dart"; -import "package:nexus/models/room.dart"; class MembersController extends AsyncNotifier> { - final Room room; - MembersController(this.room); - @override Future> build() async { - if (room.metadata == null) return const IList.empty(); + final data = ref.watch( + SelectedRoomController.provider.select( + (value) => value?.metadata == null + ? null + : (value!.metadata!.id, value.metadata!.hasMemberList), + ), + ); + if (data == null) return const IList.empty(); final state = await ref .watch(ClientController.provider.notifier) .getRoomState( GetRoomStateRequest( - roomId: room.metadata!.id, - fetchMembers: room.metadata!.hasMemberList == false, + roomId: data.$1, + fetchMembers: data.$2 == false, includeMembers: true, ), ); @@ -42,7 +46,7 @@ class MembersController extends AsyncNotifier> { } static final provider = - AsyncNotifierProvider.family, Room>( + AsyncNotifierProvider>( MembersController.new, ); } diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index d737154..6d5ac7d 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -223,13 +223,13 @@ class RoomChatController extends AsyncNotifier { } Future send( - String message, { + String text, { bool shouldMention = true, - required Iterable tags, + required IList tags, required RelationType relationType, Message? relation, }) async { - var taggedMessage = message; + var taggedMessage = text; for (final tag in tags) { final escaped = RegExp.escape(tag.id); diff --git a/lib/models/configs/author_config.dart b/lib/models/configs/author_config.dart deleted file mode 100644 index af63c63..0000000 --- a/lib/models/configs/author_config.dart +++ /dev/null @@ -1,14 +0,0 @@ -import "package:flutter_chat_core/flutter_chat_core.dart"; -import "package:freezed_annotation/freezed_annotation.dart"; -import "package:nexus/models/room.dart"; -part "author_config.freezed.dart"; -part "author_config.g.dart"; - -@freezed -abstract class AuthorConfig with _$AuthorConfig { - const factory AuthorConfig({required Message message, required Room room}) = - _AuthorConfig; - - factory AuthorConfig.fromJson(Map json) => - _$AuthorConfigFromJson(json); -} diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index a688fa7..d1c9c61 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -1,12 +1,11 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:fluttertagger/fluttertagger.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/models/relation_type.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/chat_page/composer/mention_overlay.dart"; import "package:nexus/widgets/chat_page/composer/relation_preview.dart"; @@ -14,12 +13,17 @@ class ChatBox extends HookConsumerWidget { final Message? relatedMessage; final RelationType relationType; final VoidCallback onDismiss; - final Room room; + final Future Function( + String text, { + required bool shouldMention, + required IList tags, + }) + onSend; const ChatBox({ required this.relatedMessage, required this.relationType, required this.onDismiss, - required this.room, + required this.onSend, super.key, }); @@ -38,16 +42,12 @@ class ChatBox extends HookConsumerWidget { } void send() { - if (controller.value.text.trim().isEmpty || room.metadata == null) return; - ref - .watch(RoomChatController.provider(room.metadata!.id).notifier) - .send( - controller.value.formattedText, - shouldMention: shouldMention.value, - relation: relatedMessage, - relationType: relationType, - tags: controller.value.tags, - ); + onSend( + controller.value.formattedText, + shouldMention: shouldMention.value, + tags: controller.value.tags.toIList(), + ); + onDismiss(); controller.value.text = ""; } @@ -81,7 +81,6 @@ class ChatBox extends HookConsumerWidget { children: [ RelationPreview( relatedMessage, - room: room, shouldMention: shouldMention.value, toggleShouldMention: () => shouldMention.value = !shouldMention.value, @@ -123,7 +122,6 @@ class ChatBox extends HookConsumerWidget { child: FlutterTagger( triggerStrategy: TriggerStrategy.eager, overlay: MentionOverlay( - room, query: query.value, triggerCharacter: triggerCharacter.value, addTag: ({required id, required name}) { diff --git a/lib/widgets/chat_page/composer/mention_overlay.dart b/lib/widgets/chat_page/composer/mention_overlay.dart index d95253d..2c39966 100644 --- a/lib/widgets/chat_page/composer/mention_overlay.dart +++ b/lib/widgets/chat_page/composer/mention_overlay.dart @@ -3,17 +3,14 @@ import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/members_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/loading.dart"; class MentionOverlay extends ConsumerWidget { final String? triggerCharacter; final String query; - final Room room; final void Function({required String id, required String name}) addTag; - const MentionOverlay( - this.room, { + const MentionOverlay({ required this.query, required this.addTag, required this.triggerCharacter, @@ -34,7 +31,7 @@ class MentionOverlay extends ConsumerWidget { child: switch (triggerCharacter) { "@" => ref - .watch(MembersController.provider(room)) + .watch(MembersController.provider) .betterWhen( data: (members) => ListView( children: diff --git a/lib/widgets/chat_page/composer/relation_preview.dart b/lib/widgets/chat_page/composer/relation_preview.dart index 7fded20..7c682b4 100644 --- a/lib/widgets/chat_page/composer/relation_preview.dart +++ b/lib/widgets/chat_page/composer/relation_preview.dart @@ -2,7 +2,6 @@ import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/models/relation_type.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; @@ -12,11 +11,9 @@ class RelationPreview extends ConsumerWidget { final VoidCallback onDismiss; final bool shouldMention; final VoidCallback toggleShouldMention; - final Room room; const RelationPreview( this.relatedMessage, { - required this.room, required this.relationType, required this.onDismiss, required this.shouldMention, @@ -41,10 +38,9 @@ class RelationPreview extends ConsumerWidget { "Editing message:", style: TextStyle(fontWeight: FontWeight.bold), ), - MessageAvatar(relatedMessage!, room), + MessageAvatar(relatedMessage!), MessageDisplayname( relatedMessage!, - room, style: theme.textTheme.labelMedium?.copyWith( fontWeight: FontWeight.bold, ), diff --git a/lib/widgets/chat_page/lazy_loading/message_avatar.dart b/lib/widgets/chat_page/lazy_loading/message_avatar.dart index 71fcf84..9846867 100644 --- a/lib/widgets/chat_page/lazy_loading/message_avatar.dart +++ b/lib/widgets/chat_page/lazy_loading/message_avatar.dart @@ -3,21 +3,16 @@ import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/author_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/models/configs/author_config.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; class MessageAvatar extends ConsumerWidget { final Message message; - final Room room; final double height; - const MessageAvatar(this.message, this.room, {this.height = 16, super.key}); + const MessageAvatar(this.message, {this.height = 16, super.key}); @override Widget build(BuildContext context, WidgetRef ref) => ref - .watch( - AuthorController.provider(AuthorConfig(room: room, message: message)), - ) + .watch(AuthorController.provider(message)) .betterWhen( data: (membership) => AvatarOrHash( membership.avatarUrl, diff --git a/lib/widgets/chat_page/lazy_loading/message_displayname.dart b/lib/widgets/chat_page/lazy_loading/message_displayname.dart index 7c10df3..3bbbfb0 100644 --- a/lib/widgets/chat_page/lazy_loading/message_displayname.dart +++ b/lib/widgets/chat_page/lazy_loading/message_displayname.dart @@ -3,20 +3,15 @@ import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/author_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/models/configs/author_config.dart"; -import "package:nexus/models/room.dart"; class MessageDisplayname extends ConsumerWidget { final Message message; - final Room room; final TextStyle? style; - const MessageDisplayname(this.message, this.room, {this.style, super.key}); + const MessageDisplayname(this.message, {this.style, super.key}); @override Widget build(BuildContext context, WidgetRef ref) => ref - .watch( - AuthorController.provider(AuthorConfig(room: room, message: message)), - ) + .watch(AuthorController.provider(message)) .betterWhen( data: (membership) => Text( "${membership.displayName} ${message.metadata?["pmp"] == null ? "" : "(via ${message.authorId})"}", diff --git a/lib/widgets/chat_page/member_list.dart b/lib/widgets/chat_page/member_list.dart index 8cdbbb9..2e0834f 100644 --- a/lib/widgets/chat_page/member_list.dart +++ b/lib/widgets/chat_page/member_list.dart @@ -2,16 +2,14 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/members_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; class MemberList extends ConsumerWidget { - final Room room; - const MemberList(this.room, {super.key}); + const MemberList({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final membersProvider = ref.watch(MembersController.provider(room)); + final membersProvider = ref.watch(MembersController.provider); return Drawer( shape: Border(), child: Column( diff --git a/lib/widgets/chat_page/reply_widget.dart b/lib/widgets/chat_page/reply_widget.dart index b9fa2e1..343215f 100644 --- a/lib/widgets/chat_page/reply_widget.dart +++ b/lib/widgets/chat_page/reply_widget.dart @@ -3,10 +3,10 @@ import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/event_controller.dart"; import "package:nexus/controllers/message_controller.dart"; +import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/models/configs/message_config.dart"; import "package:nexus/models/requests/get_event_request.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/chat_page/html/quoted.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; @@ -16,12 +16,10 @@ typedef OnTapReply = void Function(Message message)?; class ReplyWidget extends ConsumerWidget { final Message message; final bool alwaysShow; - final Room room; final MessageGroupStatus? groupStatus; final OnTapReply onTapReply; const ReplyWidget( this.message, { - required this.room, required this.groupStatus, this.onTapReply, this.alwaysShow = false, @@ -29,73 +27,74 @@ class ReplyWidget extends ConsumerWidget { }); @override - Widget build(BuildContext context, WidgetRef ref) => - message.replyToMessageId == null - ? SizedBox.shrink() - : Padding( - padding: EdgeInsets.only(bottom: 12), - child: Quoted( - ref - .watch( - EventController.provider( - GetEventRequest( - room: room, - eventId: message.replyToMessageId!, + Widget build(BuildContext context, WidgetRef ref) { + final room = ref.watch(SelectedRoomController.provider); + return message.replyToMessageId == null || room == null + ? SizedBox.shrink() + : Padding( + padding: EdgeInsets.only(bottom: 12), + child: Quoted( + ref + .watch( + EventController.provider( + GetEventRequest( + room: room, + eventId: message.replyToMessageId!, + ), ), - ), - ) - .betterWhen( - loading: () => Text("Fetching event..."), - data: (event) => event == null - ? SizedBox.shrink() - : ref - .watch( - MessageController.provider( - MessageConfig(room: room, event: event), - ), - ) - .betterWhen( - loading: () => Text("Parsing message..."), - data: (replyMessage) { - if (replyMessage == null) { - return SizedBox.shrink(); - } + ) + .betterWhen( + loading: () => Text("Fetching event..."), + data: (event) => event == null + ? SizedBox.shrink() + : ref + .watch( + MessageController.provider( + MessageConfig(room: room, event: event), + ), + ) + .betterWhen( + loading: () => Text("Parsing message..."), + data: (replyMessage) { + if (replyMessage == null) { + return SizedBox.shrink(); + } - return InkWell( - onTap: () => onTapReply?.call(replyMessage), - child: Row( - mainAxisSize: MainAxisSize.min, - spacing: 8, - children: [ - MessageAvatar(replyMessage, room), - Flexible( - child: MessageDisplayname( - replyMessage, - room, - style: Theme.of(context) - .textTheme - .labelMedium - ?.copyWith( - fontWeight: FontWeight.bold, - ), + return InkWell( + onTap: () => onTapReply?.call(replyMessage), + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + MessageAvatar(replyMessage), + Flexible( + child: MessageDisplayname( + replyMessage, + style: Theme.of(context) + .textTheme + .labelMedium + ?.copyWith( + fontWeight: FontWeight.bold, + ), + ), ), - ), - Flexible( - child: Text( - replyMessage.metadata!["body"], - overflow: TextOverflow.ellipsis, - style: Theme.of( - context, - ).textTheme.labelMedium, - maxLines: 1, + Flexible( + child: Text( + replyMessage.metadata!["body"], + overflow: TextOverflow.ellipsis, + style: Theme.of( + context, + ).textTheme.labelMedium, + maxLines: 1, + ), ), - ), - ], - ), - ); - }, - ), - ), - ), - ); + ], + ), + ); + }, + ), + ), + ), + ); + } } diff --git a/lib/widgets/chat_page/room_appbar.dart b/lib/widgets/chat_page/room_appbar.dart index 03cd994..3df7f9d 100644 --- a/lib/widgets/chat_page/room_appbar.dart +++ b/lib/widgets/chat_page/room_appbar.dart @@ -1,18 +1,17 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; -import "package:nexus/models/room.dart"; +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/chat_page/expandable_image.dart"; import "package:nexus/widgets/chat_page/room_menu.dart"; -class RoomAppbar extends StatelessWidget implements PreferredSizeWidget { +class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { final bool isDesktop; - final Room room; final void Function(BuildContext context) onOpenMemberList; final void Function(BuildContext context) onOpenDrawer; - const RoomAppbar( - this.room, { + const RoomAppbar({ required this.isDesktop, required this.onOpenMemberList, required this.onOpenDrawer, @@ -23,50 +22,53 @@ class RoomAppbar extends StatelessWidget implements PreferredSizeWidget { Size get preferredSize => AppBar().preferredSize; @override - Widget build(BuildContext context) => Appbar( - leading: isDesktop - ? ExpandableImage( - room.metadata?.avatar?.toString(), - child: AvatarOrHash( - room.metadata?.avatar, - room.metadata?.name ?? "Unnamed Rooms", - height: 24, - fallback: Icon(Icons.numbers), - ), - ) - : DrawerButton(onPressed: () => onOpenDrawer(context)), - scrolledUnderElevation: 0, - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - room.metadata?.name ?? "Unnamed Room", - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - if (room.metadata?.topic?.isNotEmpty == true) + Widget build(BuildContext context, WidgetRef ref) { + final room = ref.watch(SelectedRoomController.provider)!; + return Appbar( + leading: isDesktop + ? ExpandableImage( + room.metadata?.avatar?.toString(), + child: AvatarOrHash( + room.metadata?.avatar, + room.metadata?.name ?? "Unnamed Rooms", + height: 24, + fallback: Icon(Icons.numbers), + ), + ) + : DrawerButton(onPressed: () => onOpenDrawer(context)), + scrolledUnderElevation: 0, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Text( - room.metadata!.topic!, - maxLines: 1, + room.metadata?.name ?? "Unnamed Room", overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.labelMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + maxLines: 1, ), - ], - ), - actions: [ - IconButton( - onPressed: null, - icon: Icon(Icons.push_pin), - tooltip: "Open pinned messages", + if (room.metadata?.topic?.isNotEmpty == true) + Text( + room.metadata!.topic!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.labelMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], ), - IconButton( - onPressed: () => onOpenMemberList(context), - tooltip: "Open member list", - icon: Icon(Icons.people), - ), - RoomMenu(room), - ].toIList(), - ); + actions: [ + IconButton( + onPressed: null, + icon: Icon(Icons.push_pin), + tooltip: "Open pinned messages", + ), + IconButton( + onPressed: () => onOpenMemberList(context), + tooltip: "Open member list", + icon: Icon(Icons.people), + ), + RoomMenu(room), + ].toIList(), + ); + } } diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index cfbd1a8..6a14795 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -35,16 +35,18 @@ class RoomChat extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final client = ref.watch(ClientController.provider.notifier); - final replyToMessage = useState(null); + final relatedMessage = useState(null); final memberListOpened = useState(showMembersByDefault); final relationType = useState(RelationType.reply); - final room = ref.watch(SelectedRoomController.provider); final userId = ref.watch(ClientStateController.provider)?.userId; + final roomId = ref.watch( + SelectedRoomController.provider.select((value) => value?.metadata?.id), + ); final theme = Theme.of(context); final danger = theme.colorScheme.error; - if (room == null || userId == null || room.metadata?.id == null) { + if (roomId == null || userId == null) { return Center( child: Text( "Nothing to see here...", @@ -53,7 +55,7 @@ class RoomChat extends HookConsumerWidget { ); } - final controllerProvider = RoomChatController.provider(room.metadata!.id); + final controllerProvider = RoomChatController.provider(roomId); final notifier = ref.watch(controllerProvider.notifier); List getMessageOptions(Message message) { @@ -61,7 +63,7 @@ class RoomChat extends HookConsumerWidget { return [ PopupMenuItem( onTap: () { - replyToMessage.value = message; + relatedMessage.value = message; relationType.value = RelationType.reply; }, child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")), @@ -69,7 +71,7 @@ class RoomChat extends HookConsumerWidget { if (message is TextMessage && isSentByMe) PopupMenuItem( onTap: () { - replyToMessage.value = message; + relatedMessage.value = message; relationType.value = RelationType.edit; }, child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")), @@ -153,10 +155,9 @@ class RoomChat extends HookConsumerWidget { ), TextButton( onPressed: () { - if (room.metadata == null) return; client.reportEvent( ReportRequest( - roomId: room.metadata!.id, + roomId: roomId, eventId: message.id, reason: reasonController.text.isEmpty ? null @@ -189,7 +190,6 @@ class RoomChat extends HookConsumerWidget { return Scaffold( appBar: RoomAppbar( - room, isDesktop: isDesktop, onOpenDrawer: (_) => Scaffold.of(context).openDrawer(), onOpenMemberList: (thisContext) { @@ -237,18 +237,41 @@ class RoomChat extends HookConsumerWidget { chatAnimatedListBuilder: (_, itemBuilder) => ChatAnimatedList( itemBuilder: itemBuilder, - onEndReached: room.hasMore + onEndReached: + ref.watch( + SelectedRoomController.provider.select( + (room) => room?.hasMore == true, + ), + ) ? notifier.loadOlder : null, - onStartReached: () => client.markRead(room), + onStartReached: () async { + final room = ref.watch( + SelectedRoomController.provider, + ); + return room == null + ? null + : await client.markRead(room); + }, bottomPadding: 72, ), composerBuilder: (_) => ChatBox( + onSend: + ( + text, { + required shouldMention, + required tags, + }) => notifier.send( + text, + tags: tags, + relationType: relationType.value, + shouldMention: shouldMention, + relation: relatedMessage.value, + ), relationType: relationType.value, - relatedMessage: replyToMessage.value, - onDismiss: () => replyToMessage.value = null, - room: room, + relatedMessage: relatedMessage.value, + onDismiss: () => relatedMessage.value = null, ), textMessageBuilder: @@ -259,7 +282,6 @@ class RoomChat extends HookConsumerWidget { required bool isSentByMe, MessageGroupStatus? groupStatus, }) => TextMessageWrapper( - room: room, message, content: message.text, groupStatus: groupStatus, @@ -277,7 +299,6 @@ class RoomChat extends HookConsumerWidget { MessageGroupStatus? groupStatus, }) => TextMessageWrapper( message, - room: room, content: message.text, groupStatus: groupStatus, onTapReply: notifier.scrollToMessage, @@ -309,7 +330,6 @@ class RoomChat extends HookConsumerWidget { ), child: FlyerChatFileMessage( topWidget: ReplyWidget( - room: room, message, onTapReply: notifier.scrollToMessage, groupStatus: groupStatus, @@ -319,7 +339,6 @@ class RoomChat extends HookConsumerWidget { ), ), groupStatus, - room, ), systemMessageBuilder: @@ -358,11 +377,11 @@ class RoomChat extends HookConsumerWidget { ), if (memberListOpened.value == true && showMembersByDefault) - MemberList(room), + MemberList(), ], ), - endDrawer: showMembersByDefault ? null : MemberList(room), + endDrawer: showMembersByDefault ? null : MemberList(), ); } } diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart index 1be6c2b..b6af370 100644 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/message_wrapper.dart @@ -1,21 +1,13 @@ import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; class MessageWrapper extends StatelessWidget { final Message message; final Widget child; - final Room room; final MessageGroupStatus? groupStatus; - const MessageWrapper( - this.message, - this.child, - this.groupStatus, - this.room, { - super.key, - }); + const MessageWrapper(this.message, this.child, this.groupStatus, {super.key}); @override Widget build(BuildContext context) => ClipRRect( @@ -33,7 +25,7 @@ class MessageWrapper extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ groupStatus?.isFirst != false - ? MessageAvatar(message, room, height: 40) + ? MessageAvatar(message, height: 40) : SizedBox(width: 40), Expanded( child: Column( @@ -43,7 +35,6 @@ class MessageWrapper extends StatelessWidget { if (groupStatus?.isFirst != false) MessageDisplayname( message, - room, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), diff --git a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart index 41bc01e..d4e8ed9 100644 --- a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart @@ -1,7 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_link_previewer/flutter_link_previewer.dart"; -import "package:nexus/models/room.dart"; import "package:nexus/widgets/chat_page/html/html.dart"; import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart"; import "package:nexus/widgets/chat_page/reply_widget.dart"; @@ -9,7 +8,6 @@ import "package:nexus/widgets/chat_page/reply_widget.dart"; class TextMessageWrapper extends StatelessWidget { final Message message; final String? content; - final Room room; final MessageGroupStatus? groupStatus; final Future Function(Message oldMessage, Message newMessage) updateMessage; @@ -21,7 +19,6 @@ class TextMessageWrapper extends StatelessWidget { this.message, { this.content, this.onTapReply, - required this.room, required this.updateMessage, required this.groupStatus, required this.isSentByMe, @@ -51,7 +48,6 @@ class TextMessageWrapper extends StatelessWidget { children: [ ReplyWidget( message, - room: room, groupStatus: groupStatus, onTapReply: onTapReply, ), @@ -109,7 +105,6 @@ class TextMessageWrapper extends StatelessWidget { ), ), groupStatus, - room, ); } } From 8c7adbc9d3b959930b7374f52b1abdf2225c7b5f Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 29 Mar 2026 15:01:13 -0400 Subject: [PATCH 09/94] update platform support --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7af23a0..ba1ebe7 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Allow using remote Gomuks over websocket - [ ] Platform Support - [x] Linux - - [x] Windows + - [ ] Windows (WIP) - [ ] MacOS - - [ ] Android + - [x] Android - [ ] iOS - [ ] Web (may not be possible) - [x] Login From 55ecbc3590baeff0f898e354c0b29e7b41bf9e10 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 30 Mar 2026 12:47:49 -0400 Subject: [PATCH 10/94] Add better error handling, send messages early and update when delivered --- lib/controllers/client_controller.dart | 12 ++- lib/controllers/message_controller.dart | 1 + lib/controllers/room_chat_controller.dart | 40 ++++---- lib/models/configs/message_config.dart | 10 -- lib/widgets/chat_page/room_chat.dart | 12 ++- .../chat_page/wrappers/message_wrapper.dart | 97 ++++++++++++------- .../wrappers/text_message_wrapper.dart | 4 +- pubspec.lock | 8 ++ pubspec.yaml | 1 + 9 files changed, 111 insertions(+), 74 deletions(-) diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index d8315c4..ced57f7 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -9,6 +9,7 @@ import "package:flutter/foundation.dart"; import "package:nexus/controllers/account_data_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/init_complete_controller.dart"; +import "package:nexus/controllers/new_events_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/space_edges_controller.dart"; import "package:nexus/controllers/sync_status_controller.dart"; @@ -74,6 +75,13 @@ class ClientController extends AsyncNotifier { case "init_complete": ref.watch(InitCompleteController.provider.notifier).complete(); break; + case "send_complete": + final event = Event.fromJson(decodedMuksEvent["event"]); + + ref + .watch(NewEventsController.provider(event.roomId).notifier) + .add(IList([event])); + break; case "sync_complete": final syncData = SyncData.fromJson(decodedMuksEvent); final roomProvider = RoomsController.provider; @@ -150,8 +158,8 @@ class ClientController extends AsyncNotifier { Future redactEvent(RedactEventRequest report) => _sendCommand("redact_event", report.toJson()); - Future sendMessage(SendMessageRequest request) => - _sendCommand("send_message", request.toJson()); + Future sendMessage(SendMessageRequest request) async => + Event.fromJson(await _sendCommand("send_message", request.toJson())); Future verify(String recoveryKey) async { try { diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index 51e4287..675a6e5 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -46,6 +46,7 @@ class MessageController extends AsyncNotifier { "big": event.localContent?.bigEmoji == true, "eventType": type, "pmp": event.content["com.beeper.per_message_profile"], + "error": event.sendError, "editSource": event.localContent?.editSource ?? newContent?["body"] ?? diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 6d5ac7d..6512031 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -28,7 +28,6 @@ class RoomChatController extends AsyncNotifier { final client = ref.watch(ClientController.provider.notifier); var room = ref.read(RoomsController.provider)[roomId]; if (room == null) return InMemoryChatController(); - final state = await client.getRoomState( GetRoomStateRequest(roomId: roomId), ); @@ -78,7 +77,6 @@ class RoomChatController extends AsyncNotifier { ref.onDispose( ref.listen(NewEventsController.provider(roomId), (_, next) async { - final controller = await future; for (final event in next) { if (event.type == "m.room.redaction") { final controller = await future; @@ -116,12 +114,8 @@ class RoomChatController extends AsyncNotifier { ), ); } - if (message != null && - !controller.messages.any( - (oldMessage) => oldMessage.id == message.id, - ) && - ref.mounted) { - await controller.insertMessage(message); + if (message != null && ref.mounted) { + await insertMessage(message); } } } @@ -152,19 +146,11 @@ class RoomChatController extends AsyncNotifier { : controller.updateMessage(oldMessage, message); } - Future deleteMessage(Message message, {String? reason}) async { - final controller = await future; - await controller.removeMessage(message); - await ref - .watch(ClientController.provider.notifier) - .redactEvent( - RedactEventRequest( - eventId: message.id, - roomId: roomId, - reason: reason, - ), - ); - } + Future deleteMessage(Message message, {String? reason}) => ref + .watch(ClientController.provider.notifier) + .redactEvent( + RedactEventRequest(eventId: message.id, roomId: roomId, reason: reason), + ); Future loadOlder([InMemoryChatController? chatController]) async { final response = await ref @@ -242,7 +228,8 @@ class RoomChatController extends AsyncNotifier { } final client = ref.watch(ClientController.provider.notifier); - client.sendMessage( + final room = ref.read(RoomsController.provider)[roomId]; + final event = await client.sendMessage( SendMessageRequest( roomId: roomId, mentions: Mentions( @@ -260,6 +247,15 @@ class RoomChatController extends AsyncNotifier { : Relation(eventId: relation.id, relationType: relationType), ), ); + final message = room == null + ? null + : await ref.watch( + MessageController.provider( + MessageConfig(room: room, event: event), + ).future, + ); + + if (message != null) insertMessage(message); } Future resolveUser(String id) async { diff --git a/lib/models/configs/message_config.dart b/lib/models/configs/message_config.dart index 9020f78..f7490e5 100644 --- a/lib/models/configs/message_config.dart +++ b/lib/models/configs/message_config.dart @@ -6,7 +6,6 @@ part "message_config.g.dart"; @freezed abstract class MessageConfig with _$MessageConfig { - const MessageConfig._(); const factory MessageConfig({ @Default(false) bool alwaysReturn, @Default(false) bool includeEdits, @@ -14,15 +13,6 @@ abstract class MessageConfig with _$MessageConfig { required Event event, }) = _MessageConfig; - @override - bool operator ==(Object other) => - other.runtimeType == runtimeType && - other is MessageConfig && - other.event.eventId == event.eventId; - - @override - int get hashCode => Object.hash(runtimeType, event.eventId); - factory MessageConfig.fromJson(Map json) => _$MessageConfigFromJson(json); } diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index 6a14795..a30a145 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -21,7 +21,7 @@ import "package:nexus/widgets/chat_page/room_appbar.dart"; import "package:nexus/widgets/chat_page/wrappers/text_message_wrapper.dart"; import "package:nexus/widgets/chat_page/reply_widget.dart"; import "package:nexus/widgets/form_text_input.dart"; -// import "package:dynamic_polls/dynamic_polls.dart"; +import "package:nexus/main.dart"; class RoomChat extends HookConsumerWidget { final bool isDesktop; @@ -108,11 +108,13 @@ class RoomChat extends HookConsumerWidget { ), TextButton( onPressed: () async { - notifier.deleteMessage( - message, - reason: deleteReasonController.text, - ); Navigator.of(context).pop(); + await notifier + .deleteMessage( + message, + reason: deleteReasonController.text, + ) + .onError(showError); }, child: Text("Delete"), ), diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart index b6af370..216d68d 100644 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/message_wrapper.dart @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; +import "package:timeago/timeago.dart"; class MessageWrapper extends StatelessWidget { final Message message; @@ -10,41 +11,69 @@ class MessageWrapper extends StatelessWidget { const MessageWrapper(this.message, this.child, this.groupStatus, {super.key}); @override - Widget build(BuildContext context) => ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(12)), - child: AnimatedContainer( - padding: message.metadata?["flashing"] == true - ? EdgeInsets.all(8) - : EdgeInsets.all(0), - color: message.metadata?["flashing"] == true - ? Theme.of(context).colorScheme.onSurface.withAlpha(50) - : Colors.transparent, - duration: Duration(milliseconds: 250), - child: Row( - spacing: 8, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - groupStatus?.isFirst != false - ? MessageAvatar(message, height: 40) - : SizedBox(width: 40), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 4, - children: [ - if (groupStatus?.isFirst != false) - MessageDisplayname( - message, - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, + Widget build(BuildContext context) { + final theme = Theme.of(context); + final error = message.metadata?["error"]; + return ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(12)), + child: AnimatedContainer( + padding: message.metadata?["flashing"] == true + ? EdgeInsets.all(8) + : EdgeInsets.all(0), + color: message.metadata?["flashing"] == true + ? Theme.of(context).colorScheme.onSurface.withAlpha(50) + : Colors.transparent, + duration: Duration(milliseconds: 250), + child: Row( + spacing: 8, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + groupStatus?.isFirst != false + ? MessageAvatar(message, height: 40) + : SizedBox(width: 40), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4, + children: [ + if (groupStatus?.isFirst != false) + Row( + children: [ + Flexible( + child: MessageDisplayname( + message, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + if (message.deliveredAt != null && + groupStatus?.isFirst == true) + Tooltip( + message: message.deliveredAt!.toString(), + child: Text( + format(message.deliveredAt!), + style: theme.textTheme.labelSmall?.copyWith( + color: Colors.grey, + ), + ), + ), + ], ), - ), - child, - ], + child, + if (error != null && error != "not sent") + Text( + error, + style: theme.textTheme.labelSmall?.copyWith( + color: theme.colorScheme.error, + ), + ), + ], + ), ), - ), - ], + ], + ), ), - ), - ); + ); + } } diff --git a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart index d4e8ed9..63329b9 100644 --- a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart @@ -40,7 +40,9 @@ class TextMessageWrapper extends StatelessWidget { padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12), decoration: BoxDecoration( color: isSentByMe - ? colorScheme.primaryContainer + ? (message.id.startsWith("~") + ? colorScheme.onPrimary + : colorScheme.primaryContainer) : colorScheme.surfaceContainer, ), child: Column( diff --git a/pubspec.lock b/pubspec.lock index de807aa..e50446c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1357,6 +1357,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0+1" + timeago: + dependency: "direct main" + description: + name: timeago + sha256: b05159406a97e1cbb2b9ee4faa9fb096fe0e2dfcd8b08fcd2a00553450d3422e + url: "https://pub.dev" + source: hosted + version: "3.7.1" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 80c3687..b290a45 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,6 +61,7 @@ dependencies: hooks: ^1.0.0 code_assets: ^1.0.0 ffigen: ^20.1.1 + timeago: ^3.7.1 dev_dependencies: build_runner: ^2.4.11 From cdba3c480ee4c47d4959ca268980bffcedf6127d Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 30 Mar 2026 13:29:51 -0400 Subject: [PATCH 11/94] fix timestamp displays --- lib/widgets/chat_page/wrappers/message_wrapper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart index 216d68d..c998ff3 100644 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/message_wrapper.dart @@ -48,7 +48,7 @@ class MessageWrapper extends StatelessWidget { ), ), if (message.deliveredAt != null && - groupStatus?.isFirst == true) + groupStatus?.isFirst != false) Tooltip( message: message.deliveredAt!.toString(), child: Text( From 08cca4d3d3bb126c089ba524aa663279f71976c7 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 30 Mar 2026 13:42:41 -0400 Subject: [PATCH 12/94] Re-add custom hashCode and == on MessageConfig, fixing constant MessageController reloads due to room changing --- lib/models/configs/message_config.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/models/configs/message_config.dart b/lib/models/configs/message_config.dart index f7490e5..66a437c 100644 --- a/lib/models/configs/message_config.dart +++ b/lib/models/configs/message_config.dart @@ -6,6 +6,7 @@ part "message_config.g.dart"; @freezed abstract class MessageConfig with _$MessageConfig { + const MessageConfig._(); const factory MessageConfig({ @Default(false) bool alwaysReturn, @Default(false) bool includeEdits, @@ -13,6 +14,15 @@ abstract class MessageConfig with _$MessageConfig { required Event event, }) = _MessageConfig; + @override + bool operator ==(Object other) => + other.runtimeType == runtimeType && + other is MessageConfig && + other.event == event; + + @override + int get hashCode => Object.hash(runtimeType, event); + factory MessageConfig.fromJson(Map json) => _$MessageConfigFromJson(json); } From 0a6c097c50785fe6c310ab004c363301199d794c Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 30 Mar 2026 15:24:26 -0400 Subject: [PATCH 13/94] expandable inline images --- lib/widgets/chat_page/html/html.dart | 57 +++++++++++++++------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index 8e6b919..cbd79ef 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -9,6 +9,7 @@ import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/launch_helper.dart"; +import "package:nexus/widgets/chat_page/expandable_image.dart"; import "package:nexus/widgets/chat_page/html/mention_chip.dart"; import "package:nexus/widgets/chat_page/html/spoiler_text.dart"; import "package:nexus/widgets/chat_page/html/code_block.dart"; @@ -34,6 +35,16 @@ class Html extends ConsumerWidget { final height = int.tryParse(element.attributes["height"] ?? "") ?? 300; final width = int.tryParse(element.attributes["width"] ?? ""); + final src = Uri.tryParse(element.attributes["src"] ?? "") + ?.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value?.homeserverUrl, + ), + ) ?? + "", + ) + .toString(); return switch (element.localName) { "code" => @@ -56,37 +67,31 @@ class Html extends ConsumerWidget { : InlineCustomWidget(child: MentionChip(element.text)), "img" => - element.attributes["src"] == null + src == null ? SizedBox.shrink() : InlineCustomWidget( alignment: PlaceholderAlignment.middle, - child: Image( - image: CachedNetworkImage( - Uri.parse(element.attributes["src"]!) - .mxcToHttps( - ref.watch( - ClientStateController.provider.select( - (value) => value?.homeserverUrl, - ), - ) ?? - "", - ) - .toString(), - ref.watch(CrossCacheController.provider), - headers: ref.headers, - ), - errorBuilder: (_, error, _) => Text( - "Image Failed to Load", - style: TextStyle( - color: Theme.of(context).colorScheme.error, + child: ExpandableImage( + src, + child: Image( + image: CachedNetworkImage( + src, + ref.watch(CrossCacheController.provider), + headers: ref.headers, ), + errorBuilder: (_, error, _) => Text( + "Image Failed to Load", + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + ), + height: height.toDouble(), + width: width?.toDouble(), + loadingBuilder: (_, child, loadingProgress) => + loadingProgress == null + ? child + : CircularProgressIndicator(), ), - height: height.toDouble(), - width: width?.toDouble(), - loadingBuilder: (_, child, loadingProgress) => - loadingProgress == null - ? child - : CircularProgressIndicator(), ), ), ("del" || From 4dc692634ec467af82c178126881bd807b02a9ea Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 30 Mar 2026 17:24:13 -0400 Subject: [PATCH 14/94] fix timestamp for messages sent with a PMP --- lib/widgets/chat_page/lazy_loading/message_displayname.dart | 2 +- lib/widgets/chat_page/wrappers/message_wrapper.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/chat_page/lazy_loading/message_displayname.dart b/lib/widgets/chat_page/lazy_loading/message_displayname.dart index 3bbbfb0..ec56371 100644 --- a/lib/widgets/chat_page/lazy_loading/message_displayname.dart +++ b/lib/widgets/chat_page/lazy_loading/message_displayname.dart @@ -14,7 +14,7 @@ class MessageDisplayname extends ConsumerWidget { .watch(AuthorController.provider(message)) .betterWhen( data: (membership) => Text( - "${membership.displayName} ${message.metadata?["pmp"] == null ? "" : "(via ${message.authorId})"}", + "${membership.displayName}${message.metadata?["pmp"] == null ? "" : " (via ${message.authorId})"}", style: style, overflow: TextOverflow.ellipsis, ), diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart index c998ff3..33de6db 100644 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/message_wrapper.dart @@ -38,6 +38,7 @@ class MessageWrapper extends StatelessWidget { children: [ if (groupStatus?.isFirst != false) Row( + spacing: 4, children: [ Flexible( child: MessageDisplayname( From 388e09abb7d6ff55184881033dca9bcb4f7885df Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 09:56:25 -0400 Subject: [PATCH 15/94] fix constraint on `flutter_chat_ui --- pubspec.lock | 9 ++++----- pubspec.yaml | 10 ++-------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index e50446c..571a13b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -466,11 +466,10 @@ packages: flutter_chat_ui: dependency: "direct main" description: - path: "packages/flutter_chat_ui" - ref: HEAD - resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627" - url: "https://github.com/Henry-Hiles/flutter_chat_ui" - source: git + name: flutter_chat_ui + sha256: cfbaac38f429beb33d9cc1ca920ae7ccbadbed282c99335d590d61306d3a3d0f + url: "https://pub.dev" + source: hosted version: "2.11.1" flutter_hooks: dependency: "direct main" diff --git a/pubspec.yaml b/pubspec.yaml index b290a45..07baf46 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,14 +40,8 @@ dependencies: flyer_chat_image_message: ^2.2.2 flyer_chat_system_message: ^2.1.13 flyer_chat_file_message: ^2.3.1 - flutter_chat_ui: - git: - url: https://github.com/Henry-Hiles/flutter_chat_ui - path: packages/flutter_chat_ui - flutter_link_previewer: - git: - url: https://github.com/Henry-Hiles/flutter_chat_ui - path: packages/flutter_link_previewer + flutter_chat_ui: ^2.11.1 + flutter_link_previewer: ^4.2.0 color_hash: ^1.0.1 flutter_widget_from_html_core: ^0.17.0 flutter_svg: ^2.2.2 From 6d903a88825fb101cb40be223cf9d8c17334bd3d Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 10:15:34 -0400 Subject: [PATCH 16/94] disable some sidebar buttons that aren't done yet --- lib/widgets/chat_page/sidebar.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/widgets/chat_page/sidebar.dart b/lib/widgets/chat_page/sidebar.dart index 771a7ae..18413a0 100644 --- a/lib/widgets/chat_page/sidebar.dart +++ b/lib/widgets/chat_page/sidebar.dart @@ -6,7 +6,6 @@ import "package:nexus/controllers/key_controller.dart"; import "package:nexus/controllers/selected_space_controller.dart"; import "package:nexus/controllers/spaces_controller.dart"; import "package:nexus/helpers/extensions/join_room_with_snackbars.dart"; -import "package:nexus/pages/settings_page.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/chat_page/room_menu.dart"; import "package:nexus/widgets/form_text_input.dart"; @@ -146,7 +145,7 @@ class Sidebar extends HookConsumerWidget { ), ), PopupMenuItem( - onTap: () {}, + onTap: null, child: ListTile( title: Text("Create a new room"), leading: Icon(Icons.add), @@ -157,17 +156,15 @@ class Sidebar extends HookConsumerWidget { ), IconButton( tooltip: "Explore other rooms", - onPressed: () => showDialog( - context: context, - builder: (context) => AlertDialog(title: Text("To-do")), - ), + onPressed: null, icon: Icon(Icons.explore), ), IconButton( tooltip: "Open settings", - onPressed: () => Navigator.of( - context, - ).push(MaterialPageRoute(builder: (_) => SettingsPage())), + onPressed: null, + // () => Navigator.of( + // context, + // ).push(MaterialPageRoute(builder: (_) => SettingsPage())), icon: Icon(Icons.settings), ), ], From e42aaeb30a9e052b806d8809c881c12d63c0be82 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 10:27:52 -0400 Subject: [PATCH 17/94] fix readme step for generating bindings to be in line with new command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba1ebe7..d5f3e21 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ flutter pub get Generate Gomuks bindings: ```sh -scripts/generate.sh +dart scripts/generate.dart ``` Build generated files, and watch for new changes: From 60b7f22566537dd86351eff10c6255364dbe80dd Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 12:29:43 -0400 Subject: [PATCH 18/94] Update prerequisites in readme --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d5f3e21..91b9b40 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,19 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. ## Build Instructions +### Prerequisites + +#### Linux + +- With Nix: Either use direnv and `direnv allow`, or `nix flake develop` +- Without Nix: Install Flutter, Go, Git, Libclang, and Glibc. + +#### Windows / MacOS + +I don't really know. You will need Flutter, Git, Go, and Visual Studio tools, and otherwise I guess just keep installing stuff until there aren't any errors. I will look into this sometimeTM. + +### Clone repo + First, clone and open the repo: ```sh @@ -134,17 +147,6 @@ git clone --recurse-submodules https://git.federated.nexus/Henry-Hiles/nexus cd nexus ``` -### Prerequisites - -#### Linux - -- With Nix: Either use direnv and `direnv allow`, or `nix flake develop` -- Without Nix: Install Flutter, Go, Olm, Git, Clang, and GLibc. - -#### Windows / MacOS - -I don't really know. You will need Flutter, Git, Go, and Visual Studio tools, and otherwise I guess just keep installing stuff until there aren't any errors. I will look into this sometimeTM. - ### Set up Flutter Get dependencies: From 7ee165b3005d9a85e2d67fa1142bd30b702ada6a Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 13:51:29 -0400 Subject: [PATCH 19/94] add snap note to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91b9b40..ec26f09 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. #### Linux - With Nix: Either use direnv and `direnv allow`, or `nix flake develop` -- Without Nix: Install Flutter, Go, Git, Libclang, and Glibc. +- Without Nix: Install Flutter, Go, Git, Libclang, and Glibc. Do not use any Snap packages, they cause various compilation issues. #### Windows / MacOS From 0b9ddbfbc8b76dbd28d72d4c0e24be6c146042d3 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 16:29:19 -0400 Subject: [PATCH 20/94] add profile popovers --- lib/controllers/profile_controller.dart | 17 ++ lib/helpers/extensions/scheme_to_theme.dart | 9 + lib/helpers/extensions/show_context_menu.dart | 1 + lib/helpers/extensions/show_user_popover.dart | 18 ++ lib/widgets/avatar_or_hash.dart | 2 +- lib/widgets/chat_page/html/html.dart | 236 +++++++++--------- .../lazy_loading/message_avatar.dart | 17 +- .../lazy_loading/message_displayname.dart | 17 +- lib/widgets/chat_page/member_list.dart | 34 +-- lib/widgets/chat_page/user_popover.dart | 91 +++++++ pubspec.lock | 9 +- 11 files changed, 302 insertions(+), 149 deletions(-) create mode 100644 lib/controllers/profile_controller.dart create mode 100644 lib/helpers/extensions/show_user_popover.dart create mode 100644 lib/widgets/chat_page/user_popover.dart diff --git a/lib/controllers/profile_controller.dart b/lib/controllers/profile_controller.dart new file mode 100644 index 0000000..120d4e4 --- /dev/null +++ b/lib/controllers/profile_controller.dart @@ -0,0 +1,17 @@ +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/models/profile.dart"; + +class ProfileController extends AsyncNotifier { + final String userId; + ProfileController(this.userId); + + @override + Future build() { + final client = ref.watch(ClientController.provider.notifier); + return client.getProfile(userId); + } + + static final provider = AsyncNotifierProvider.autoDispose + .family(ProfileController.new); +} diff --git a/lib/helpers/extensions/scheme_to_theme.dart b/lib/helpers/extensions/scheme_to_theme.dart index 08c0ba6..d106186 100644 --- a/lib/helpers/extensions/scheme_to_theme.dart +++ b/lib/helpers/extensions/scheme_to_theme.dart @@ -7,6 +7,15 @@ extension SchemeToTheme on ColorScheme { titleSpacing: 0, backgroundColor: surfaceContainerLow, ), + menuTheme: MenuThemeData( + style: MenuStyle( + backgroundColor: WidgetStatePropertyAll(primaryContainer), + ), + ), + chipTheme: ChipThemeData( + labelStyle: TextStyle(color: onPrimary), + color: WidgetStatePropertyAll(primary), + ), textTheme: ThemeData( fontFamilyFallback: ["sans", "emoji"], brightness: brightness, diff --git a/lib/helpers/extensions/show_context_menu.dart b/lib/helpers/extensions/show_context_menu.dart index f4762c3..7d8cab6 100644 --- a/lib/helpers/extensions/show_context_menu.dart +++ b/lib/helpers/extensions/show_context_menu.dart @@ -9,6 +9,7 @@ extension ShowContextMenu on BuildContext { showMenu( context: this, + constraints: BoxConstraints.loose(Size.infinite), position: RelativeRect.fromLTRB( globalPosition.dx, globalPosition.dy, diff --git a/lib/helpers/extensions/show_user_popover.dart b/lib/helpers/extensions/show_user_popover.dart new file mode 100644 index 0000000..1698879 --- /dev/null +++ b/lib/helpers/extensions/show_user_popover.dart @@ -0,0 +1,18 @@ +import "package:flutter/material.dart"; +import "package:nexus/helpers/extensions/show_context_menu.dart"; +import "package:nexus/models/membership.dart"; +import "package:nexus/widgets/chat_page/user_popover.dart"; + +extension ShowUserPopover on BuildContext { + void showUserPopover(Membership member, {required Offset globalPosition}) => + showContextMenu( + globalPosition: globalPosition, + children: [ + PopupMenuItem( + enabled: false, + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: IconTheme(data: IconThemeData(), child: UserPopover(member)), + ), + ], + ); +} diff --git a/lib/widgets/avatar_or_hash.dart b/lib/widgets/avatar_or_hash.dart index 147c249..28662e2 100644 --- a/lib/widgets/avatar_or_hash.dart +++ b/lib/widgets/avatar_or_hash.dart @@ -50,7 +50,7 @@ class AvatarOrHash extends ConsumerWidget { ref.watch(CrossCacheController.provider), headers: ref.headers, ), - fit: BoxFit.contain, + fit: BoxFit.cover, errorBuilder: (_, _, _) => box, ), ), diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index cbd79ef..907ca7b 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -21,133 +21,135 @@ class Html extends ConsumerWidget { const Html(this.html, {this.textStyle, super.key}); @override - Widget build(BuildContext context, WidgetRef ref) => HtmlWidget( - html, - textStyle: textStyle, - customWidgetBuilder: (element) { - if (element.attributes.keys.contains("data-mx-profile-fallback")) { - return SizedBox.shrink(); - } + Widget build(BuildContext context, WidgetRef ref) => SelectionArea( + child: HtmlWidget( + html, + textStyle: textStyle, + customWidgetBuilder: (element) { + if (element.attributes.keys.contains("data-mx-profile-fallback")) { + return SizedBox.shrink(); + } - if (element.attributes.keys.contains("data-mx-spoiler")) { - return InlineCustomWidget(child: SpoilerText(text: element.text)); - } + if (element.attributes.keys.contains("data-mx-spoiler")) { + return InlineCustomWidget(child: SpoilerText(text: element.text)); + } - final height = int.tryParse(element.attributes["height"] ?? "") ?? 300; - final width = int.tryParse(element.attributes["width"] ?? ""); - final src = Uri.tryParse(element.attributes["src"] ?? "") - ?.mxcToHttps( - ref.watch( - ClientStateController.provider.select( - (value) => value?.homeserverUrl, - ), - ) ?? - "", - ) - .toString(); + final height = int.tryParse(element.attributes["height"] ?? "") ?? 300; + final width = int.tryParse(element.attributes["width"] ?? ""); + final src = Uri.tryParse(element.attributes["src"] ?? "") + ?.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value?.homeserverUrl, + ), + ) ?? + "", + ) + .toString(); - return switch (element.localName) { - "code" => - element.parent?.localName == "pre" - ? element.outerHtml.contains("
") - ? Html( - """
${element.outerHtml.replaceAll("
", "\n")}
""", - ) - : CodeBlock( - element.text, - lang: element.className.replaceAll("language-", ""), - ) - : null, + return switch (element.localName) { + "code" => + element.parent?.localName == "pre" + ? element.outerHtml.contains("
") + ? Html( + """
${element.outerHtml.replaceAll("
", "\n")}
""", + ) + : CodeBlock( + element.text, + lang: element.className.replaceAll("language-", ""), + ) + : null, - "blockquote" => Quoted(Html(element.innerHtml)), + "blockquote" => Quoted(Html(element.innerHtml)), - "a" => - element.attributes["href"]?.mention == null - ? null - : InlineCustomWidget(child: MentionChip(element.text)), + "a" => + element.attributes["href"]?.mention == null + ? null + : InlineCustomWidget(child: MentionChip(element.text)), - "img" => - src == null - ? SizedBox.shrink() - : InlineCustomWidget( - alignment: PlaceholderAlignment.middle, - child: ExpandableImage( - src, - child: Image( - image: CachedNetworkImage( - src, - ref.watch(CrossCacheController.provider), - headers: ref.headers, - ), - errorBuilder: (_, error, _) => Text( - "Image Failed to Load", - style: TextStyle( - color: Theme.of(context).colorScheme.error, + "img" => + src == null + ? SizedBox.shrink() + : InlineCustomWidget( + alignment: PlaceholderAlignment.middle, + child: ExpandableImage( + src, + child: Image( + image: CachedNetworkImage( + src, + ref.watch(CrossCacheController.provider), + headers: ref.headers, ), + errorBuilder: (_, error, _) => Text( + "Image Failed to Load", + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + ), + height: height.toDouble(), + width: width?.toDouble(), + loadingBuilder: (_, child, loadingProgress) => + loadingProgress == null + ? child + : CircularProgressIndicator(), ), - height: height.toDouble(), - width: width?.toDouble(), - loadingBuilder: (_, child, loadingProgress) => - loadingProgress == null - ? child - : CircularProgressIndicator(), ), ), - ), - ("del" || - "h1" || - "h2" || - "h3" || - "h4" || - "h5" || - "h6" || - "p" || - "ul" || - "ol" || - "sup" || - "sub" || - "li" || - "b" || - "i" || - "u" || - "strong" || - "em" || - "s" || - "code" || - "hr" || - "br" || - "div" || - "table" || - "thead" || - "tbody" || - "tr" || - "th" || - "td" || - "caption" || - "pre" || - "span" || - "details" || - "summary") => - null, + ("del" || + "h1" || + "h2" || + "h3" || + "h4" || + "h5" || + "h6" || + "p" || + "ul" || + "ol" || + "sup" || + "sub" || + "li" || + "b" || + "i" || + "u" || + "strong" || + "em" || + "s" || + "code" || + "hr" || + "br" || + "div" || + "table" || + "thead" || + "tbody" || + "tr" || + "th" || + "td" || + "caption" || + "pre" || + "span" || + "details" || + "summary") => + null, - _ => SizedBox.shrink(), - }; - }, - customStylesBuilder: (element) => { - "width": "auto", - ...Map.fromEntries( - element.attributes - .mapTo?>( - (key, value) => switch (key) { - "data-mx-color" => MapEntry("color", value), - "data-mx-bg-color" => MapEntry("background-color", value), - _ => null, - }, - ) - .nonNulls, - ), - }, - onTapUrl: (url) => - ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(url)), + _ => SizedBox.shrink(), + }; + }, + customStylesBuilder: (element) => { + "width": "auto", + ...Map.fromEntries( + element.attributes + .mapTo?>( + (key, value) => switch (key) { + "data-mx-color" => MapEntry("color", value), + "data-mx-bg-color" => MapEntry("background-color", value), + _ => null, + }, + ) + .nonNulls, + ), + }, + onTapUrl: (url) => + ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(url)), + ), ); } diff --git a/lib/widgets/chat_page/lazy_loading/message_avatar.dart b/lib/widgets/chat_page/lazy_loading/message_avatar.dart index 9846867..ca1e06b 100644 --- a/lib/widgets/chat_page/lazy_loading/message_avatar.dart +++ b/lib/widgets/chat_page/lazy_loading/message_avatar.dart @@ -1,8 +1,9 @@ -import "package:flutter/widgets.dart"; +import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/author_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; class MessageAvatar extends ConsumerWidget { @@ -14,10 +15,16 @@ class MessageAvatar extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) => ref .watch(AuthorController.provider(message)) .betterWhen( - data: (membership) => AvatarOrHash( - membership.avatarUrl, - membership.displayName, - height: height, + data: (membership) => InkWell( + onTapDown: (details) => context.showUserPopover( + membership, + globalPosition: details.globalPosition, + ), + child: AvatarOrHash( + membership.avatarUrl, + membership.displayName, + height: height, + ), ), loading: () => AvatarOrHash(null, message.authorId.substring(1), height: height), diff --git a/lib/widgets/chat_page/lazy_loading/message_displayname.dart b/lib/widgets/chat_page/lazy_loading/message_displayname.dart index ec56371..2ec867d 100644 --- a/lib/widgets/chat_page/lazy_loading/message_displayname.dart +++ b/lib/widgets/chat_page/lazy_loading/message_displayname.dart @@ -1,8 +1,9 @@ -import "package:flutter/widgets.dart"; +import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/author_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/helpers/extensions/show_user_popover.dart"; class MessageDisplayname extends ConsumerWidget { final Message message; @@ -13,10 +14,16 @@ class MessageDisplayname extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) => ref .watch(AuthorController.provider(message)) .betterWhen( - data: (membership) => Text( - "${membership.displayName}${message.metadata?["pmp"] == null ? "" : " (via ${message.authorId})"}", - style: style, - overflow: TextOverflow.ellipsis, + data: (membership) => InkWell( + onTapDown: (details) => context.showUserPopover( + membership, + globalPosition: details.globalPosition, + ), + child: Text( + "${membership.displayName}${message.metadata?["pmp"] == null ? "" : " (via ${message.authorId})"}", + style: style, + overflow: TextOverflow.ellipsis, + ), ), loading: () => Text(""), ); diff --git a/lib/widgets/chat_page/member_list.dart b/lib/widgets/chat_page/member_list.dart index 2e0834f..0367a8a 100644 --- a/lib/widgets/chat_page/member_list.dart +++ b/lib/widgets/chat_page/member_list.dart @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/members_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; class MemberList extends ConsumerWidget { @@ -35,23 +36,24 @@ class MemberList extends ConsumerWidget { child: ListView( children: members .map( - (member) => ListTile( - onTap: () => showDialog( - context: context, - builder: (context) => - Dialog(child: Text("TODO: Open member popover")), + (member) => InkWell( + onTapDown: (details) => context.showUserPopover( + member, + globalPosition: details.globalPosition, ), - leading: AvatarOrHash( - member.avatarUrl, - member.displayName, - ), - title: Text( - member.displayName, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - member.userId, - overflow: TextOverflow.ellipsis, + child: ListTile( + leading: AvatarOrHash( + member.avatarUrl, + member.displayName, + ), + title: Text( + member.displayName, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + member.userId, + overflow: TextOverflow.ellipsis, + ), ), ), ) diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart new file mode 100644 index 0000000..9907741 --- /dev/null +++ b/lib/widgets/chat_page/user_popover.dart @@ -0,0 +1,91 @@ +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/profile_controller.dart"; +import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/models/membership.dart"; +import "package:nexus/widgets/avatar_or_hash.dart"; + +class UserPopover extends ConsumerWidget { + final Membership member; + const UserPopover(this.member, {super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = Theme.of(context); + final textTheme = theme.textTheme; + return IntrinsicWidth( + child: Column( + spacing: 16, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + spacing: 16, + mainAxisSize: MainAxisSize.min, + children: [ + AvatarOrHash(member.avatarUrl, member.displayName, height: 80), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SelectableText( + member.displayName, + style: textTheme.headlineSmall, + ), + SelectableText(member.userId, style: textTheme.titleSmall), + SizedBox(height: 4), + ref + .watch(ProfileController.provider(member.userId)) + .betterWhen( + loading: SizedBox.shrink, + data: (profile) => Row( + spacing: 4, + children: [ + for (final pronoun in profile.pronouns.where( + (pronoun) => pronoun.language == "en", + )) + Chip(label: Text(pronoun.summary)), + if (profile.timezone != null) + Chip(label: Text(profile.timezone!)), + ], + ), + ), + ], + ), + ), + ], + ), + Row( + spacing: 8, + children: [ + FilledButton.icon(onPressed: null, label: Text("Message")), + FilledButton.icon( + onPressed: null, + label: Text("Kick"), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.error, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onError, + ), + ), + ), + ElevatedButton.icon( + onPressed: null, + label: Text("Ban"), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.errorContainer, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onErrorContainer, + ), + ), + ), + ].map((e) => Expanded(child: e)).toList(), + ), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 571a13b..1352319 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -490,11 +490,10 @@ packages: flutter_link_previewer: dependency: "direct main" description: - path: "packages/flutter_link_previewer" - ref: HEAD - resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627" - url: "https://github.com/Henry-Hiles/flutter_chat_ui" - source: git + name: flutter_link_previewer + sha256: "346f345064e65bc8bf739bccf19d6d6ca50f8183ffc52e452afa58c06ee2cbf7" + url: "https://pub.dev" + source: hosted version: "4.2.0" flutter_lints: dependency: "direct dev" From bb842abfb1b1d7c3c1696fdcee4a22b6f71f0fe4 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 22:27:18 -0400 Subject: [PATCH 21/94] Don't make message text selectable as it breaks long press context menus --- lib/widgets/chat_page/html/html.dart | 238 +++++++++++++-------------- 1 file changed, 118 insertions(+), 120 deletions(-) diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index 907ca7b..cbd79ef 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -21,135 +21,133 @@ class Html extends ConsumerWidget { const Html(this.html, {this.textStyle, super.key}); @override - Widget build(BuildContext context, WidgetRef ref) => SelectionArea( - child: HtmlWidget( - html, - textStyle: textStyle, - customWidgetBuilder: (element) { - if (element.attributes.keys.contains("data-mx-profile-fallback")) { - return SizedBox.shrink(); - } + Widget build(BuildContext context, WidgetRef ref) => HtmlWidget( + html, + textStyle: textStyle, + customWidgetBuilder: (element) { + if (element.attributes.keys.contains("data-mx-profile-fallback")) { + return SizedBox.shrink(); + } - if (element.attributes.keys.contains("data-mx-spoiler")) { - return InlineCustomWidget(child: SpoilerText(text: element.text)); - } + if (element.attributes.keys.contains("data-mx-spoiler")) { + return InlineCustomWidget(child: SpoilerText(text: element.text)); + } - final height = int.tryParse(element.attributes["height"] ?? "") ?? 300; - final width = int.tryParse(element.attributes["width"] ?? ""); - final src = Uri.tryParse(element.attributes["src"] ?? "") - ?.mxcToHttps( - ref.watch( - ClientStateController.provider.select( - (value) => value?.homeserverUrl, - ), - ) ?? - "", - ) - .toString(); + final height = int.tryParse(element.attributes["height"] ?? "") ?? 300; + final width = int.tryParse(element.attributes["width"] ?? ""); + final src = Uri.tryParse(element.attributes["src"] ?? "") + ?.mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value?.homeserverUrl, + ), + ) ?? + "", + ) + .toString(); - return switch (element.localName) { - "code" => - element.parent?.localName == "pre" - ? element.outerHtml.contains("
") - ? Html( - """
${element.outerHtml.replaceAll("
", "\n")}
""", - ) - : CodeBlock( - element.text, - lang: element.className.replaceAll("language-", ""), - ) - : null, + return switch (element.localName) { + "code" => + element.parent?.localName == "pre" + ? element.outerHtml.contains("
") + ? Html( + """
${element.outerHtml.replaceAll("
", "\n")}
""", + ) + : CodeBlock( + element.text, + lang: element.className.replaceAll("language-", ""), + ) + : null, - "blockquote" => Quoted(Html(element.innerHtml)), + "blockquote" => Quoted(Html(element.innerHtml)), - "a" => - element.attributes["href"]?.mention == null - ? null - : InlineCustomWidget(child: MentionChip(element.text)), + "a" => + element.attributes["href"]?.mention == null + ? null + : InlineCustomWidget(child: MentionChip(element.text)), - "img" => - src == null - ? SizedBox.shrink() - : InlineCustomWidget( - alignment: PlaceholderAlignment.middle, - child: ExpandableImage( - src, - child: Image( - image: CachedNetworkImage( - src, - ref.watch(CrossCacheController.provider), - headers: ref.headers, - ), - errorBuilder: (_, error, _) => Text( - "Image Failed to Load", - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), - ), - height: height.toDouble(), - width: width?.toDouble(), - loadingBuilder: (_, child, loadingProgress) => - loadingProgress == null - ? child - : CircularProgressIndicator(), + "img" => + src == null + ? SizedBox.shrink() + : InlineCustomWidget( + alignment: PlaceholderAlignment.middle, + child: ExpandableImage( + src, + child: Image( + image: CachedNetworkImage( + src, + ref.watch(CrossCacheController.provider), + headers: ref.headers, ), + errorBuilder: (_, error, _) => Text( + "Image Failed to Load", + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + ), + height: height.toDouble(), + width: width?.toDouble(), + loadingBuilder: (_, child, loadingProgress) => + loadingProgress == null + ? child + : CircularProgressIndicator(), ), ), - ("del" || - "h1" || - "h2" || - "h3" || - "h4" || - "h5" || - "h6" || - "p" || - "ul" || - "ol" || - "sup" || - "sub" || - "li" || - "b" || - "i" || - "u" || - "strong" || - "em" || - "s" || - "code" || - "hr" || - "br" || - "div" || - "table" || - "thead" || - "tbody" || - "tr" || - "th" || - "td" || - "caption" || - "pre" || - "span" || - "details" || - "summary") => - null, + ), + ("del" || + "h1" || + "h2" || + "h3" || + "h4" || + "h5" || + "h6" || + "p" || + "ul" || + "ol" || + "sup" || + "sub" || + "li" || + "b" || + "i" || + "u" || + "strong" || + "em" || + "s" || + "code" || + "hr" || + "br" || + "div" || + "table" || + "thead" || + "tbody" || + "tr" || + "th" || + "td" || + "caption" || + "pre" || + "span" || + "details" || + "summary") => + null, - _ => SizedBox.shrink(), - }; - }, - customStylesBuilder: (element) => { - "width": "auto", - ...Map.fromEntries( - element.attributes - .mapTo?>( - (key, value) => switch (key) { - "data-mx-color" => MapEntry("color", value), - "data-mx-bg-color" => MapEntry("background-color", value), - _ => null, - }, - ) - .nonNulls, - ), - }, - onTapUrl: (url) => - ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(url)), - ), + _ => SizedBox.shrink(), + }; + }, + customStylesBuilder: (element) => { + "width": "auto", + ...Map.fromEntries( + element.attributes + .mapTo?>( + (key, value) => switch (key) { + "data-mx-color" => MapEntry("color", value), + "data-mx-bg-color" => MapEntry("background-color", value), + _ => null, + }, + ) + .nonNulls, + ), + }, + onTapUrl: (url) => + ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(url)), ); } From a562d043a83e15bc9160cb920517ee63067d55dd Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 22:36:03 -0400 Subject: [PATCH 22/94] fix mobile issues with popover --- lib/widgets/chat_page/user_popover.dart | 134 ++++++++++++------------ 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 9907741..88993ed 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -13,79 +13,77 @@ class UserPopover extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final textTheme = theme.textTheme; - return IntrinsicWidth( - child: Column( - spacing: 16, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - spacing: 16, - mainAxisSize: MainAxisSize.min, - children: [ - AvatarOrHash(member.avatarUrl, member.displayName, height: 80), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SelectableText( - member.displayName, - style: textTheme.headlineSmall, + return Column( + spacing: 16, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Wrap( + alignment: WrapAlignment.center, + spacing: 16, + runSpacing: 8, + children: [ + AvatarOrHash(member.avatarUrl, member.displayName, height: 80), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SelectableText( + member.displayName, + style: textTheme.headlineSmall, + ), + SelectableText(member.userId, style: textTheme.titleSmall), + SizedBox(height: 4), + ref + .watch(ProfileController.provider(member.userId)) + .betterWhen( + loading: SizedBox.shrink, + data: (profile) => Wrap( + spacing: 4, + children: [ + for (final pronoun in profile.pronouns.where( + (pronoun) => pronoun.language == "en", + )) + Chip(label: Text(pronoun.summary)), + if (profile.timezone != null) + Chip(label: Text(profile.timezone!)), + ], + ), ), - SelectableText(member.userId, style: textTheme.titleSmall), - SizedBox(height: 4), - ref - .watch(ProfileController.provider(member.userId)) - .betterWhen( - loading: SizedBox.shrink, - data: (profile) => Row( - spacing: 4, - children: [ - for (final pronoun in profile.pronouns.where( - (pronoun) => pronoun.language == "en", - )) - Chip(label: Text(pronoun.summary)), - if (profile.timezone != null) - Chip(label: Text(profile.timezone!)), - ], - ), - ), - ], + ], + ), + ], + ), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + FilledButton.icon(onPressed: null, label: Text("Message")), + FilledButton.icon( + onPressed: null, + label: Text("Kick"), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.error, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onError, ), ), - ], - ), - Row( - spacing: 8, - children: [ - FilledButton.icon(onPressed: null, label: Text("Message")), - FilledButton.icon( - onPressed: null, - label: Text("Kick"), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.colorScheme.error, - ), - foregroundColor: WidgetStatePropertyAll( - theme.colorScheme.onError, - ), + ), + ElevatedButton.icon( + onPressed: null, + label: Text("Ban"), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.errorContainer, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onErrorContainer, ), ), - ElevatedButton.icon( - onPressed: null, - label: Text("Ban"), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.colorScheme.errorContainer, - ), - foregroundColor: WidgetStatePropertyAll( - theme.colorScheme.onErrorContainer, - ), - ), - ), - ].map((e) => Expanded(child: e)).toList(), - ), - ], - ), + ), + ], + ), + ], ); } } From 2ead857805723cf4c655824a2b204a1222c4fede Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 1 Apr 2026 22:38:22 -0400 Subject: [PATCH 23/94] make it so you can't ban yourself --- lib/widgets/chat_page/user_popover.dart | 56 +++++++++++++------------ 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 88993ed..4279c59 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -1,5 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/models/membership.dart"; @@ -52,37 +53,38 @@ class UserPopover extends ConsumerWidget { ), ], ), - Wrap( - spacing: 8, - runSpacing: 8, - children: [ - FilledButton.icon(onPressed: null, label: Text("Message")), - FilledButton.icon( - onPressed: null, - label: Text("Kick"), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.colorScheme.error, - ), - foregroundColor: WidgetStatePropertyAll( - theme.colorScheme.onError, + if (member.userId != ref.watch(ClientStateController.provider)?.userId) + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + FilledButton.icon(onPressed: null, label: Text("Message")), + FilledButton.icon( + onPressed: null, + label: Text("Kick"), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.error, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onError, + ), ), ), - ), - ElevatedButton.icon( - onPressed: null, - label: Text("Ban"), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.colorScheme.errorContainer, - ), - foregroundColor: WidgetStatePropertyAll( - theme.colorScheme.onErrorContainer, + ElevatedButton.icon( + onPressed: null, + label: Text("Ban"), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.errorContainer, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onErrorContainer, + ), ), ), - ), - ], - ), + ], + ), ], ); } From 4e4e387aa2157cc9e9c66326f080484e77e5b521 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 2 Apr 2026 10:26:48 -0400 Subject: [PATCH 24/94] onTapDown -> onTapUp, helps on mobile for scrolling --- lib/widgets/chat_page/lazy_loading/message_avatar.dart | 2 +- lib/widgets/chat_page/lazy_loading/message_displayname.dart | 2 +- lib/widgets/chat_page/member_list.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/widgets/chat_page/lazy_loading/message_avatar.dart b/lib/widgets/chat_page/lazy_loading/message_avatar.dart index ca1e06b..dc8dfef 100644 --- a/lib/widgets/chat_page/lazy_loading/message_avatar.dart +++ b/lib/widgets/chat_page/lazy_loading/message_avatar.dart @@ -16,7 +16,7 @@ class MessageAvatar extends ConsumerWidget { .watch(AuthorController.provider(message)) .betterWhen( data: (membership) => InkWell( - onTapDown: (details) => context.showUserPopover( + onTapUp: (details) => context.showUserPopover( membership, globalPosition: details.globalPosition, ), diff --git a/lib/widgets/chat_page/lazy_loading/message_displayname.dart b/lib/widgets/chat_page/lazy_loading/message_displayname.dart index 2ec867d..ca9eed7 100644 --- a/lib/widgets/chat_page/lazy_loading/message_displayname.dart +++ b/lib/widgets/chat_page/lazy_loading/message_displayname.dart @@ -15,7 +15,7 @@ class MessageDisplayname extends ConsumerWidget { .watch(AuthorController.provider(message)) .betterWhen( data: (membership) => InkWell( - onTapDown: (details) => context.showUserPopover( + onTapUp: (details) => context.showUserPopover( membership, globalPosition: details.globalPosition, ), diff --git a/lib/widgets/chat_page/member_list.dart b/lib/widgets/chat_page/member_list.dart index 0367a8a..ffa572c 100644 --- a/lib/widgets/chat_page/member_list.dart +++ b/lib/widgets/chat_page/member_list.dart @@ -37,7 +37,7 @@ class MemberList extends ConsumerWidget { children: members .map( (member) => InkWell( - onTapDown: (details) => context.showUserPopover( + onTapUp: (details) => context.showUserPopover( member, globalPosition: details.globalPosition, ), From e30355a6f10b4165af4f56d019c78392f5e478f0 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 2 Apr 2026 10:58:34 -0400 Subject: [PATCH 25/94] Support stable identifiers for MSC4175 --- lib/models/profile.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/models/profile.dart b/lib/models/profile.dart index d92b4f6..584f27b 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -3,15 +3,22 @@ import "package:freezed_annotation/freezed_annotation.dart"; part "profile.freezed.dart"; part "profile.g.dart"; +Object? readPronouns(Map map, _) => + map["m.pronouns"] ?? map["io.fsky.nyx.pronouns"]; + +Object? readTimezone(Map map, _) => + map["m.tz"] ?? map["us.cloke.msc4175.tz"]; + @freezed abstract class Profile with _$Profile { const factory Profile({ String? avatarUrl, @JsonKey(name: "displayname") String? displayName, - @JsonKey(name: "us.cloke.msc4175.tz") String? timezone, + + @JsonKey(readValue: readTimezone) String? timezone, @Default(IList.empty()) - @JsonKey(name: "io.fsky.nyx.pronouns") + @JsonKey(readValue: readPronouns) IList pronouns, }) = _Profile; From 5a0a5cb1381d21d8c520489d1d65a361460b8454 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 2 Apr 2026 10:58:59 -0400 Subject: [PATCH 26/94] update todo --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ec26f09..ebc6615 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,8 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Displaying - [ ] Creating - [ ] Threads -- [ ] Profile popouts +- [x] Profile popouts + - [ ] Working actions - [ ] Copy link to [room, space] - [ ] Reporting - [x] Events From e669ede6feb86fb74e4877795012577e44cb9bd4 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 2 Apr 2026 11:17:03 -0400 Subject: [PATCH 27/94] add "Try it out" section to README --- README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ebc6615..a952e0f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Nexus Client > [!WARNING] -> Nexus Client is still heavily in development, and is not ready for use! +> Nexus Client is still in development, and doesn't support everything needed for daily use. ## Description @@ -126,7 +126,21 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] About - [x] Log Out -## Build Instructions +## Try it out + +If you want to try out Nexus, grab one of the following artifacts from CI: + +- [Android APK](https://nightly.link/Henry-Hiles/nexus/workflows/android/main/APK.zip) +- Windows + - [Portable Build](https://nightly.link/Henry-Hiles/nexus/workflows/windows/main/windows-portable.zip) + - [Installer](https://nightly.link/Henry-Hiles/nexus/workflows/windows/main/windows-installer.zip) +- Flatpak + - [AArch64/Arm64](https://nightly.link/Henry-Hiles/nexus/workflows/flatpak/main/flatpak-aarch64.zip) + - [x86_64/AMD64](https://nightly.link/Henry-Hiles/nexus/workflows/flatpak/main/flatpak-x86_64.zip) + +Or, try the Nix package: `nix run git+https://git.federated.nexus/Henry-Hiles/nexus` + +## Build it yourself ### Prerequisites From c130d28b9342b7a66c469caaf03c39852ee64fcc Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 2 Apr 2026 11:19:37 -0400 Subject: [PATCH 28/94] add popover for TWIM --- assets/popover.png | Bin 0 -> 23261 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/popover.png diff --git a/assets/popover.png b/assets/popover.png new file mode 100644 index 0000000000000000000000000000000000000000..20f468e4cb9640ad4808cccb144eb9040ef12f63 GIT binary patch literal 23261 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 literal 0 HcmV?d00001 From 3a1bcb5b8ff7778d37fd9b3031a004830cba2d01 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 2 Apr 2026 17:37:27 -0400 Subject: [PATCH 29/94] Add (WIP) ban/klck ability --- lib/controllers/author_controller.dart | 2 + lib/controllers/client_controller.dart | 8 +++- .../members_by_type_controller.dart | 25 ++++++++++++ lib/controllers/members_controller.dart | 2 +- lib/controllers/message_controller.dart | 4 +- lib/controllers/room_chat_controller.dart | 16 -------- lib/models/membership.dart | 6 +++ lib/models/membership_status.dart | 4 ++ .../requests/set_membership_request.dart | 20 ++++++++++ .../chat_page/composer/mention_overlay.dart | 7 +++- lib/widgets/chat_page/member_list.dart | 7 +++- lib/widgets/chat_page/room_chat.dart | 2 +- lib/widgets/chat_page/user_popover.dart | 40 +++++++++++++++++-- 13 files changed, 113 insertions(+), 30 deletions(-) create mode 100644 lib/controllers/members_by_type_controller.dart create mode 100644 lib/models/membership_status.dart create mode 100644 lib/models/requests/set_membership_request.dart diff --git a/lib/controllers/author_controller.dart b/lib/controllers/author_controller.dart index fc35ed3..8b709d3 100644 --- a/lib/controllers/author_controller.dart +++ b/lib/controllers/author_controller.dart @@ -6,6 +6,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/members_controller.dart"; import "package:nexus/models/membership.dart"; +import "package:nexus/models/membership_status.dart"; class AuthorController extends AsyncNotifier { final Message message; @@ -35,6 +36,7 @@ class AuthorController extends AsyncNotifier { ); return Membership( + status: member?.status ?? MembershipStatus.leave, avatarUrl: pmp?.avatarUrl ?? member?.avatarUrl, displayName: pmp?.displayName ?? diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index ced57f7..3de4bc0 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -28,6 +28,7 @@ import "package:nexus/models/requests/paginate_request.dart"; import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/requests/report_request.dart"; import "package:nexus/models/requests/send_message_request.dart"; +import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/models/room.dart"; import "package:nexus/models/sync_data.dart"; import "package:nexus/models/sync_status.dart"; @@ -224,8 +225,11 @@ class ClientController extends AsyncNotifier { Future getProfile(String userId) async => Profile.fromJson(await _sendCommand("get_profile", {"user_id": userId})); - Future reportEvent(ReportRequest report) => - _sendCommand("report_event", report.toJson()); + Future reportEvent(ReportRequest request) => + _sendCommand("report_event", request.toJson()); + + Future setMembership(SetMembershipRequest request) => + _sendCommand("set_membership", request.toJson()); Future markRead(Room room) async { final event = room.events.firstWhereOrNull( diff --git a/lib/controllers/members_by_type_controller.dart b/lib/controllers/members_by_type_controller.dart new file mode 100644 index 0000000..cdc8d07 --- /dev/null +++ b/lib/controllers/members_by_type_controller.dart @@ -0,0 +1,25 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/models/membership.dart"; +import "package:nexus/models/membership_status.dart"; + +class MembersByTypeController extends AsyncNotifier> { + final MembershipStatus status; + MembersByTypeController(this.status); + + @override + Future> build() => ref.watch( + MembersController.provider.selectAsync( + (members) => + members.where((membership) => membership.status == status).toIList(), + ), + ); + + static final provider = + AsyncNotifierProvider.family< + MembersByTypeController, + IList, + MembershipStatus + >(MembersByTypeController.new); +} diff --git a/lib/controllers/members_controller.dart b/lib/controllers/members_controller.dart index 2cf7541..39666d4 100644 --- a/lib/controllers/members_controller.dart +++ b/lib/controllers/members_controller.dart @@ -29,7 +29,7 @@ class MembersController extends AsyncNotifier> { ); return state.nonNulls - .where((member) => member.content["membership"] == "join") + .where((state) => state.type == "m.room.member") .map( (membership) => Membership.fromContent( membership.content, diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index 675a6e5..ed52ef2 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -145,11 +145,11 @@ class MessageController extends AsyncNotifier { "${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) { "invite" => "was invited to", "join" => "joined", - "leave" => event.authorId == event.stateKey ? "left" : "was kicked", + "leave" => event.authorId == event.stateKey ? "left" : (event.unsigned["prev_content"]?["membership"] == "ban" ? "was unbanned from" : "was kicked from"), "ban" => "was banned from", "knock" => "asked to join", _ => "did something relating to", - }} the room.", + }} the room. ${content["reason"] ?? ""}", ), "m.room.server_acl" => toSystemMessage( diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 6512031..3fe9d74 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -2,7 +2,6 @@ import "dart:async"; import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; -import "package:flutter_chat_core/flutter_chat_core.dart" as chat; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:fluttertagger/fluttertagger.dart"; import "package:nexus/controllers/client_controller.dart"; @@ -258,21 +257,6 @@ class RoomChatController extends AsyncNotifier { if (message != null) insertMessage(message); } - Future resolveUser(String id) async { - final user = await ref - .watch(ClientController.provider.notifier) - .getProfile(id); - return chat.User( - id: id, - name: user.displayName, - // imageSource: user.avatarUrl == null - // ? null - // : (await ref.watch( - // AvatarController.provider(user.avatarUrl!.toString()).future, - // )).toString(), - ); - } - Future scrollToMessage(Message message) async { final controller = await future; Future setFlashing(bool flashing) => controller.updateMessage( diff --git a/lib/models/membership.dart b/lib/models/membership.dart index 4e2bf4c..ce0cc42 100644 --- a/lib/models/membership.dart +++ b/lib/models/membership.dart @@ -1,12 +1,14 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:freezed_annotation/freezed_annotation.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; +import "package:nexus/models/membership_status.dart"; part "membership.freezed.dart"; @freezed abstract class Membership with _$Membership { const Membership._(); const factory Membership({ + required MembershipStatus status, required Uri? avatarUrl, required String displayName, required String userId, @@ -17,6 +19,10 @@ abstract class Membership with _$Membership { String userId, String homeserver, ) => Membership( + status: MembershipStatus.values.firstWhere( + (status) => status.name == content["membership"], + orElse: () => MembershipStatus.leave, + ), avatarUrl: Uri.tryParse( content["avatar_url"] ?? "", )?.mxcToHttps(homeserver), diff --git a/lib/models/membership_status.dart b/lib/models/membership_status.dart new file mode 100644 index 0000000..bc85e22 --- /dev/null +++ b/lib/models/membership_status.dart @@ -0,0 +1,4 @@ +import "package:freezed_annotation/freezed_annotation.dart"; + +@JsonEnum() +enum MembershipStatus { leave, invite, ban, join } diff --git a/lib/models/requests/set_membership_request.dart b/lib/models/requests/set_membership_request.dart new file mode 100644 index 0000000..7384f5d --- /dev/null +++ b/lib/models/requests/set_membership_request.dart @@ -0,0 +1,20 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +part "set_membership_request.freezed.dart"; +part "set_membership_request.g.dart"; + +@freezed +abstract class SetMembershipRequest with _$SetMembershipRequest { + const factory SetMembershipRequest({ + required String userId, + required String roomId, + + @JsonKey(name: "action") required MembershipAction action, + @Default(false) @JsonKey(name: "msc4293_redact_events") bool redact, + }) = _SetMembershipRequest; + + factory SetMembershipRequest.fromJson(Map json) => + _$SetMembershipRequestFromJson(json); +} + +@JsonEnum() +enum MembershipAction { ban, kick, unban, invite } diff --git a/lib/widgets/chat_page/composer/mention_overlay.dart b/lib/widgets/chat_page/composer/mention_overlay.dart index 2c39966..a78bdd1 100644 --- a/lib/widgets/chat_page/composer/mention_overlay.dart +++ b/lib/widgets/chat_page/composer/mention_overlay.dart @@ -1,8 +1,9 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/controllers/members_by_type_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/models/membership_status.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/loading.dart"; @@ -31,7 +32,9 @@ class MentionOverlay extends ConsumerWidget { child: switch (triggerCharacter) { "@" => ref - .watch(MembersController.provider) + .watch( + MembersByTypeController.provider(MembershipStatus.join), + ) .betterWhen( data: (members) => ListView( children: diff --git a/lib/widgets/chat_page/member_list.dart b/lib/widgets/chat_page/member_list.dart index ffa572c..72ce744 100644 --- a/lib/widgets/chat_page/member_list.dart +++ b/lib/widgets/chat_page/member_list.dart @@ -1,8 +1,9 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/controllers/members_by_type_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart"; +import "package:nexus/models/membership_status.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; class MemberList extends ConsumerWidget { @@ -10,7 +11,9 @@ class MemberList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final membersProvider = ref.watch(MembersController.provider); + final membersProvider = ref.watch( + MembersByTypeController.provider(MembershipStatus.join), + ); return Drawer( shape: Border(), child: Column( diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index a30a145..d85c826 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -369,7 +369,7 @@ class RoomChat extends HookConsumerWidget { ), ), ), - resolveUser: notifier.resolveUser, + resolveUser: (_) async => null, chatController: controller, ), ), diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 4279c59..365b6ef 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -1,10 +1,15 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/profile_controller.dart"; +import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/models/membership.dart"; +import "package:nexus/models/membership_status.dart"; +import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; +import "package:nexus/main.dart"; class UserPopover extends ConsumerWidget { final Membership member; @@ -14,6 +19,11 @@ class UserPopover extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final textTheme = theme.textTheme; + final client = ref.watch(ClientController.provider.notifier); + final roomId = ref.watch( + SelectedRoomController.provider.select((room) => room?.metadata?.id), + ); + return Column( spacing: 16, crossAxisAlignment: CrossAxisAlignment.stretch, @@ -53,14 +63,24 @@ class UserPopover extends ConsumerWidget { ), ], ), - if (member.userId != ref.watch(ClientStateController.provider)?.userId) + if (member.userId != + ref.watch(ClientStateController.provider)?.userId && + roomId != null) Wrap( spacing: 8, runSpacing: 8, children: [ FilledButton.icon(onPressed: null, label: Text("Message")), FilledButton.icon( - onPressed: null, + onPressed: () => client + .setMembership( + SetMembershipRequest( + userId: member.userId, + roomId: roomId, + action: MembershipAction.kick, + ), + ) + .onError(showError), label: Text("Kick"), style: ButtonStyle( backgroundColor: WidgetStatePropertyAll( @@ -72,8 +92,20 @@ class UserPopover extends ConsumerWidget { ), ), ElevatedButton.icon( - onPressed: null, - label: Text("Ban"), + onPressed: () => client + .setMembership( + SetMembershipRequest( + userId: member.userId, + roomId: roomId, + action: member.status == MembershipStatus.ban + ? MembershipAction.unban + : MembershipAction.ban, + ), + ) + .onError(showError), + label: Text( + member.status == MembershipStatus.ban ? "Unban" : "Ban", + ), style: ButtonStyle( backgroundColor: WidgetStatePropertyAll( theme.colorScheme.errorContainer, From cadd5c1255db9ea5f089e7b82578726a071ed5d6 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 3 Apr 2026 17:58:59 -0400 Subject: [PATCH 30/94] expandable profile pictures in popout --- lib/widgets/chat_page/user_popover.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 365b6ef..e8f4dea 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -10,6 +10,7 @@ import "package:nexus/models/membership_status.dart"; import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/main.dart"; +import "package:nexus/widgets/chat_page/expandable_image.dart"; class UserPopover extends ConsumerWidget { final Membership member; @@ -33,7 +34,14 @@ class UserPopover extends ConsumerWidget { spacing: 16, runSpacing: 8, children: [ - AvatarOrHash(member.avatarUrl, member.displayName, height: 80), + ExpandableImage( + member.avatarUrl?.toString(), + child: AvatarOrHash( + member.avatarUrl, + member.displayName, + height: 80, + ), + ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ From f4624c2866cb5accff4cbe59bdf288f450d8aa62 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 3 Apr 2026 19:39:39 -0400 Subject: [PATCH 31/94] Add server-generated URL preview support --- README.md | 53 ++++++++-------- lib/controllers/url_preview_controller.dart | 63 +++++++++++++++++++ .../wrappers/text_message_wrapper.dart | 58 ++++++++++------- pubspec.lock | 2 +- pubspec.yaml | 1 + 5 files changed, 128 insertions(+), 49 deletions(-) create mode 100644 lib/controllers/url_preview_controller.dart diff --git a/README.md b/README.md index a952e0f..070c5db 100644 --- a/README.md +++ b/README.md @@ -67,31 +67,32 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [x] Plain text - [x] Per message profiles - [x] HTML - - [x] Replies - - [x] Viewing - - [ ] Jump to original message - - [x] In loaded timeline - - [ ] Out of loaded timeline - - [x] Edits - - [x] Attachments - - [x] Unencrypted - - [ ] Encrypted - - [x] Blurhashing - - [ ] Downloading attachments - - [x] Opening attachments in their own view - - [ ] Polls: Waiting on https://github.com/SwanFlutter/dynamic_polls/issues/1 - - [x] Mentions - - [x] Users - - [x] Rooms - - [ ] Plain text (not sure if I want to add this or not, I probably won't unless there's interest) - - [x] Matrix URIs - - [x] Matrix.to links - - [ ] Do some fancy fetching to get nice names - - [ ] Make clickable - - [x] Custom emojis/stickers - - [x] History loading - - [x] Backwards - - [ ] Forwards + - [x] URL Previews + - [x] Replies + - [x] Viewing + - [ ] Jump to original message + - [x] In loaded timeline + - [ ] Out of loaded timeline + - [x] Edits + - [x] Attachments + - [x] Unencrypted + - [ ] Encrypted + - [x] Blurhashing + - [ ] Downloading attachments + - [x] Opening attachments in their own view + - [ ] Polls: Waiting on https://github.com/SwanFlutter/dynamic_polls/issues/1 + - [x] Mentions + - [x] Users + - [x] Rooms + - [ ] Plain text (not sure if I want to add this or not, I probably won't unless there's interest) + - [x] Matrix URIs + - [x] Matrix.to links + - [ ] Do some fancy fetching to get nice names + - [ ] Make clickable + - [x] Custom emojis/stickers + - [x] History loading + - [x] Backwards + - [ ] Forwards - [x] Editing - [x] Deleting - [ ] Reactions: Waiting on https://github.com/flyerhq/flutter_chat_ui/pull/838 or me doing a custom impl @@ -116,7 +117,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Devices - [ ] Viewing devices - [ ] Verifying devices - - [ ] URL preview: Server / Client / None + - [ ] URL preview: Server / Sending Client (Beeper spec) / None - [ ] Account changes - [ ] Display name - [ ] Profile picture diff --git a/lib/controllers/url_preview_controller.dart b/lib/controllers/url_preview_controller.dart new file mode 100644 index 0000000..119a845 --- /dev/null +++ b/lib/controllers/url_preview_controller.dart @@ -0,0 +1,63 @@ +import "dart:convert"; +import "package:flutter_chat_core/flutter_chat_core.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:http/http.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/header_controller.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; + +class UrlPreviewController extends AsyncNotifier { + final TextMessage message; + UrlPreviewController(this.message); + + @override + Future build() async { + final homeserver = ref.watch(ClientStateController.provider)?.homeserverUrl; + final link = RegExp( + r'''https?://[^\s"'<>]+''', + ).allMatches(message.text).firstOrNull?.group(0); + + if (homeserver != null && link != null) { + { + final response = await get( + Uri.parse(homeserver) + .resolve("/_matrix/client/v1/media/preview_url") + .replace(queryParameters: {"url": link}), + headers: await ref.watch(HeaderController.provider.future), + ); + + if (response.statusCode == 200) { + final decodedValue = json.decode(response.body); + final mxc = decodedValue["og:image"]; + final image = mxc == null + ? null + : Uri.tryParse(mxc)?.mxcToHttps(homeserver); + + return LinkPreviewData( + link: link, + title: decodedValue["og:title"], + description: decodedValue["og:description"], + image: image == null + ? null + : ImagePreviewData( + url: image.toString(), + width: + (decodedValue["og:image:width"] as int?)?.toDouble() ?? + 0, + height: + (decodedValue["og:image:height"] as int?)?.toDouble() ?? + 0, + ), + ); + } + } + } + + return null; + } + + static final provider = AsyncNotifierProvider.autoDispose + .family( + UrlPreviewController.new, + ); +} diff --git a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart index 63329b9..08e583e 100644 --- a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart @@ -1,11 +1,17 @@ +import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_link_previewer/flutter_link_previewer.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; +import "package:nexus/controllers/url_preview_controller.dart"; +import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/widgets/chat_page/html/html.dart"; import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart"; import "package:nexus/widgets/chat_page/reply_widget.dart"; -class TextMessageWrapper extends StatelessWidget { +class TextMessageWrapper extends ConsumerWidget { final Message message; final String? content; final MessageGroupStatus? groupStatus; @@ -27,7 +33,7 @@ class TextMessageWrapper extends StatelessWidget { }); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; final textMessage = message is TextMessage ? message as TextMessage : null; @@ -80,27 +86,35 @@ class TextMessageWrapper extends StatelessWidget { if (textMessage?.editedAt != null) Text("(edited)", style: theme.textTheme.labelSmall), if (textMessage != null) - LinkPreview( - text: textMessage.text, - backgroundColor: isSentByMe - ? colorScheme.inversePrimary - : colorScheme.surfaceContainerLow, - outsidePadding: EdgeInsets.only(top: 4), - insidePadding: EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - linkPreviewData: message.metadata?["linkPreviewData"], - onLinkPreviewDataFetched: (linkPreviewData) => updateMessage( - message, - message.copyWith( - metadata: { - ...(message.metadata ?? {}), - "linkPreviewData": linkPreviewData, - }, + ref + .watch(UrlPreviewController.provider(textMessage)) + .betterWhen( + loading: SizedBox.shrink, + data: (preview) => preview == null + ? SizedBox.shrink() + : LinkPreview( + imageBuilder: (url) => Image( + image: CachedNetworkImage( + url, + ref.watch(CrossCacheController.provider), + headers: ref.headers, + ), + fit: BoxFit.cover, + errorBuilder: (_, _, _) => SizedBox.shrink(), + ), + text: textMessage.text, + backgroundColor: isSentByMe + ? colorScheme.inversePrimary + : colorScheme.surfaceContainerLow, + outsidePadding: EdgeInsets.only(top: 4), + insidePadding: EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + linkPreviewData: preview, + onLinkPreviewDataFetched: (_) => null, + ), ), - ), - ), if (extra != null) extra!, ], ), diff --git a/pubspec.lock b/pubspec.lock index 1352319..46b8ee6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -655,7 +655,7 @@ packages: source: hosted version: "0.15.6" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" diff --git a/pubspec.yaml b/pubspec.yaml index 07baf46..482e809 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,6 +56,7 @@ dependencies: code_assets: ^1.0.0 ffigen: ^20.1.1 timeago: ^3.7.1 + http: ^1.6.0 dev_dependencies: build_runner: ^2.4.11 From 35bf379f030b10ffe95569aa58d35facc1a0045c Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 3 Apr 2026 20:00:04 -0400 Subject: [PATCH 32/94] only render message as html when content is set to html --- lib/controllers/message_controller.dart | 1 + .../wrappers/text_message_wrapper.dart | 50 +++++++++++-------- pubspec.lock | 16 ++++++ pubspec.yaml | 1 + 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index ed52ef2..6ec62c9 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -47,6 +47,7 @@ class MessageController extends AsyncNotifier { "eventType": type, "pmp": event.content["com.beeper.per_message_profile"], "error": event.sendError, + "format": content["format"], "editSource": event.localContent?.editSource ?? newContent?["body"] ?? diff --git a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart index 08e583e..f6fe13d 100644 --- a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart @@ -2,6 +2,7 @@ import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_link_previewer/flutter_link_previewer.dart"; +import "package:flutter_linkify/flutter_linkify.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/controllers/url_preview_controller.dart"; @@ -60,29 +61,36 @@ class TextMessageWrapper extends ConsumerWidget { onTapReply: onTapReply, ), if (content != null) - Html( - textStyle: message.metadata?["big"] == true - ? TextStyle(fontSize: 32) - : null, - content! - .replaceAllMapped( - RegExp( - "(]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)", - caseSensitive: false, - ), - (m) { - // If it's already an tag, leave it unchanged - if (m.group(1) != null) { - return m.group(1)!; - } + message.metadata?["format"] == "org.matrix.custom.html" + ? Html( + textStyle: message.metadata?["big"] == true + ? TextStyle(fontSize: 32) + : null, + content!.replaceAllMapped( + RegExp( + "(]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)", + caseSensitive: false, + dotAll: true, + ), + (m) { + // If it's already an tag, leave it unchanged + if (m.group(1) != null) { + return m.group(1)!; + } - // Otherwise, wrap the bare URL - final url = m.group(2)!; - return "$url"; - }, + // Otherwise, wrap the bare URL + final url = m.group(2)!; + return "$url"; + }, + ), ) - .replaceAll("\n", "
"), - ), + : Linkify( + text: content!, + options: LinkifyOptions(humanize: false), + linkStyle: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), if (textMessage?.editedAt != null) Text("(edited)", style: theme.textTheme.labelSmall), if (textMessage != null) diff --git a/pubspec.lock b/pubspec.lock index 46b8ee6..ce90832 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -495,6 +495,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.0" + flutter_linkify: + dependency: "direct main" + description: + name: flutter_linkify + sha256: "74669e06a8f358fee4512b4320c0b80e51cffc496607931de68d28f099254073" + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_lints: dependency: "direct dev" description: @@ -822,6 +830,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + linkify: + dependency: transitive + description: + name: linkify + sha256: "4139ea77f4651ab9c315b577da2dd108d9aa0bd84b5d03d33323f1970c645832" + url: "https://pub.dev" + source: hosted + version: "5.0.0" lints: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 482e809..f7fe258 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: ffigen: ^20.1.1 timeago: ^3.7.1 http: ^1.6.0 + flutter_linkify: ^6.0.0 dev_dependencies: build_runner: ^2.4.11 From 51dd8c56682b12bc29956deeadf6c9fc9caf048b Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 3 Apr 2026 20:04:33 -0400 Subject: [PATCH 33/94] Fix readme formatting --- README.md | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 070c5db..1e78900 100644 --- a/README.md +++ b/README.md @@ -68,31 +68,31 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [x] Per message profiles - [x] HTML - [x] URL Previews - - [x] Replies - - [x] Viewing - - [ ] Jump to original message - - [x] In loaded timeline - - [ ] Out of loaded timeline - - [x] Edits - - [x] Attachments - - [x] Unencrypted - - [ ] Encrypted - - [x] Blurhashing - - [ ] Downloading attachments - - [x] Opening attachments in their own view - - [ ] Polls: Waiting on https://github.com/SwanFlutter/dynamic_polls/issues/1 - - [x] Mentions - - [x] Users - - [x] Rooms - - [ ] Plain text (not sure if I want to add this or not, I probably won't unless there's interest) - - [x] Matrix URIs - - [x] Matrix.to links - - [ ] Do some fancy fetching to get nice names - - [ ] Make clickable - - [x] Custom emojis/stickers - - [x] History loading - - [x] Backwards - - [ ] Forwards + - [x] Replies + - [x] Viewing + - [ ] Jump to original message + - [x] In loaded timeline + - [ ] Out of loaded timeline + - [x] Edits + - [x] Attachments + - [x] Unencrypted + - [ ] Encrypted + - [x] Blurhashing + - [ ] Downloading attachments + - [x] Opening attachments in their own view + - [ ] Polls: Waiting on https://github.com/SwanFlutter/dynamic_polls/issues/1 + - [x] Mentions + - [x] Users + - [x] Rooms + - [ ] Plain text (not sure if I want to add this or not, I probably won't unless there's interest) + - [x] Matrix URIs + - [x] Matrix.to links + - [ ] Do some fancy fetching to get nice names + - [ ] Make clickable + - [x] Custom emojis/stickers + - [x] History loading + - [x] Backwards + - [ ] Forwards - [x] Editing - [x] Deleting - [ ] Reactions: Waiting on https://github.com/flyerhq/flutter_chat_ui/pull/838 or me doing a custom impl From f460a3baccc2ae193021b8ef39183eace8bd365d Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 09:48:13 -0400 Subject: [PATCH 34/94] fix linkify callback --- lib/widgets/chat_page/wrappers/text_message_wrapper.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart index f6fe13d..d3329ed 100644 --- a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart @@ -8,6 +8,7 @@ import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/controllers/url_preview_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; +import "package:nexus/helpers/launch_helper.dart"; import "package:nexus/widgets/chat_page/html/html.dart"; import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart"; import "package:nexus/widgets/chat_page/reply_widget.dart"; @@ -87,6 +88,9 @@ class TextMessageWrapper extends ConsumerWidget { : Linkify( text: content!, options: LinkifyOptions(humanize: false), + onOpen: (link) => ref + .watch(LaunchHelper.provider) + .launchUrl(Uri.parse(link.url)), linkStyle: TextStyle( color: Theme.of(context).colorScheme.primary, ), From a64cfd35be0b886a2bb99c2609c068ecf365315a Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 09:49:24 -0400 Subject: [PATCH 35/94] Update android icon --- .../drawable-hdpi/ic_launcher_background.png | Bin 0 -> 7013 bytes .../drawable-hdpi/ic_launcher_foreground.png | Bin 8943 -> 8962 bytes .../drawable-mdpi/ic_launcher_background.png | Bin 0 -> 4091 bytes .../drawable-mdpi/ic_launcher_foreground.png | Bin 5818 -> 5820 bytes .../drawable-xhdpi/ic_launcher_background.png | Bin 0 -> 9914 bytes .../drawable-xhdpi/ic_launcher_foreground.png | Bin 11956 -> 12014 bytes .../ic_launcher_background.png | Bin 0 -> 21475 bytes .../ic_launcher_foreground.png | Bin 18571 -> 18695 bytes .../ic_launcher_background.png | Bin 0 -> 29309 bytes .../ic_launcher_foreground.png | Bin 34319 -> 25605 bytes .../res/mipmap-anydpi-v26/ic_launcher.xml | 2 +- 11 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 android/app/src/main/res/drawable-hdpi/ic_launcher_background.png create mode 100644 android/app/src/main/res/drawable-mdpi/ic_launcher_background.png create mode 100644 android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png create mode 100644 android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png create mode 100644 android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..791aed8bb2d8c379e68529054ea267a3b12f7ab1 GIT binary patch literal 7013 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 O7(8A5T-G@yGywn?=PX_T literal 8943 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 diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..b00666df7a966ecac49f51fb8825d64f95e7f371 GIT binary patch literal 4091 zcmeAS@N?(olHy`uVBq!ia0y~yV8{Vs4mJh`hW@nhvkVLjk|nMYCBgY=CFO}lsSM@i z<$9TU*~Q6;1*v-ZMd`EO*+?-k@Xz&haSW-L^E8&XAmpm(zWmK6^XkkLPpY-3IPlaf zswg@x>}cq-Zw}+`Nu9w};!rRr%l&|(>qf0b%F{H=116pY3H^J&t2;NY3}v8otuwH zCIzqdNV9n1Zofrl#fyv^TVJ#<(--@-cV5y{!$~X;jCXj3*L$yuu8@}cv--|;E3ua! z0`r4&iWc*FPg;BY_R}|?zKNJ`GhdLfm-X%8-GNP}EB6`He@ef6?A)JS+bip ztPo(DvHteldu8wXPus4O{m%bo>y*w1ofC

pccOAdH`wuUv{Pci` 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 QFfcH9y85}Sb4q9e06A6b1poj5 diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..bad307d6a4f67bbbffe451bb19e34aff5614ffee GIT binary patch literal 9914 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 LtDnm{r-UW|%qWvH literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png index df784a1b2dc6ae494739c3f18212ef296c2609e4..536e844c8400b0792451c12f228e0ba98a36f91a 100644 GIT binary patch literal 12014 zcmeAS@N?(olHy`uVBq!ia0y~yV7LLo9Bd2>44n$PZy6XEBuiW)N`mv#O3D+9QW?t2 z%k?tzvWt@w3sUv+i_&Mmvyoz8(3{}t;uumf=j~kf5|OKN$M)Yeang6J5@%V;5hSmr z#k5GHYte#^)@2IY)Wbq*zA<(9ziHxV;&l{s6c9MrUNYg{64p#cA*PonZQgy-UH1Ik zxxJfbr_?=v^KfT=`n$95YK*`C%U+Q=tDvy(qI1Rq^#_lFrWCS1d$dR}JV8%SiU_9I zo)E?Y-G*%pQp_uu7sxR1Hv}{AG2GxxFm2e$u#Khake+2Bt5{47%L4m`Jce0J948+a zKV`haa)IxFEW7?M8e9NXDh$*Hu!a&*Pb>d`FzLgKnC;qf*-t&J};kmL^L8nz<=d! z#ytns`}MLIfAAjIoqnvQvD#dmy^!^pjp|PST_+cQ^qM1hE!kwo9fpMI&X0HPU@Dg3 zpWbqvE#VD!=ARuR2mUei#OoBYK9fn+SeM5*$2pgwzJO1f*`hzGS=%68VA`R4#uDBy zE<2w8?o|8N+&IrC@VMv<<2@28k3K2($j3A85OSzmpdLJ>kad}SqLIAd3(qHqQW@Sc ze(+qN4hr98^UeSKop(02o^ihpL)cozZMp1yvdl(43vwbCINC0#G=6Z(&*0WJMxOnD zE-&6u();y!!t|y>)@9T8F|6BOHDPC>vcbF9gnvzU>?cfMkg;OlI6kC@U^mADzy#%2+puS+xqd{ zZHE<0=Wi8n2xGVwF=tgb%en(!6lG3^u9Zos`ylw?)$D)$7SGdXeCKaBtdkLt%X@l) zaovG0AQPF_)ZF;UqB}GC%%sCtUGZ?LKY!g*L_r_JnIQXCIp>oY?-$Q>-?oF;cln?d|QIEInIl zkLB+RHrg07^P{c7C(qSkuJUqG;zwUwcD?mxn9pdjsBV+1aCkzI%cYEuEWux|D$FxF z9j;*=%a@d#n0K4;@glXd^D5t_>He&mr&N8m_msXwc*0NRX^qZ1Q&uhd^<-Ir*zx15 z?Q+Am<+H!x)-hK7Ik&@L^?AlUPv2erbWAkjoe9$i243-}TY8H=OxPA|ZO~S+il5P7 zLg?CiY#+MU9putpcVLsysRJ>Bi_=dAzZTcjywzTiz!QIQ-s_BK^6%bV{C80FMx@h@ z<5jQD{9Cjxd9mBA?Gss!O8hU-PS!s@f9JM1CV}Tq9vt*26rXX>O7wg&^LriBt2ezr zEt$rgoT56()ZxgdsFr1W>eE6CZoTwm;XWFfu*mHw`zhuNr5ufxtK8&=}PEMd;prlBiP({gFWm1ceRIgO2rS8%_vId(o)@X?k-fCpSi|Zzs5TI^Upg%QF`w;7Zok8sZ2Ps_yF_w z!y+$r_U-C0JbrH3jw!m#SJv}y$$0j4v1DN2zkjmzOch4czaI_GWs5&F@tZ({BVRhfG`*_Z&Ej&}U)^&34u>LN6t)r)<&{h2b*VVs2_bf3I zvfJ8lUiQiT4>E`6&wD!AO?-bYuGtzTJ*(ML!i5ZQagzHq#F_4gNN%5CE`YYbiH9C2sUZKf6R2jaBmvavorIbr6FL$!0~#w?FC*%c9` zm6c*T-{aqe1Ifz^)>qGcwDV`jqffh8Cfj^|%<+HO(wJ*`7fsYpvos%Yy0JcsAz1d1 zj@)YRm?X;|THhmCbr+{pG>AV}W-O~%zogmx;IqnYpKZ)HKDqs~>)CY4o3{5WW7^8o zTTT>BGo2y!LhVDNSVXK<({0uYxtkv?9TtnuGoD+wF=WV9(c+l<3~XPZ2p-+PkT8=5!&QQb4icILix;}enYfq@(O z_s#e3yXYwLUQ=h%=NBIJMgp!?Oo|2@6X)_R$YeNnu0OOS;CXskc;iXio)y=>$u;oJ zI&OVH`dAA4oE`HoK2VvL_Sd9e)te(<=WZtJ--{;?&J~OBw@Ph(%J=-S^RKEoI+A<) zcu%rtX@yQWkn`xj#UB37rOi<~?#I0(H0CT~I{M$>u1?>BB?qcD-i$VI-LiZWOXPxG zHeAJ@-DT=I%wLq{+pQG2#?4rEt!sa_q_$ru{{tH%;X}F&?f`) z@?s`iL9Vy8QHR}b+FXBMBgM`v9Wg1Ew)w2jAR! z+Bna{@PR%X;~H*ovqBD$5f})CIcpvr%=Pumr z*OAv4d(3xFyvi}P#QU3=Ew+5rNcukCrOM07$-8Bd)7_#Kch|9HZC%OczvBM1cgr&( ztzW$7j9K!b!1LMnZwG8P3je5P75FVap@_@C#^K~ChgLR+tKJDyRz7^ca`q+9e>3*0 zYjDhET796SSbL(Rfv<4&G`HlN)vGsVJ}Xvj#fQgdv3jr zvgx~RRSUXR-+fY_a6rT<>c8fOuhI`v{ym(Zw$p)6|pHwvX)pdf9C8@fqhbj2E!-{M@#3!Sw(3chq;h zI9T*+LS>1t!FRg{PvScLyG>7cy+8e@Sm{EtP|u`km&$q0Z4BT%Mx4s&SLe+X zY%OKC`|i7UYO+tsN|~cy#KlW1cCbu8cE^G>;!n@sz@tS)&(<1L`?UscocJbiVU2=K zYW{~i{%e}8Q|vW(#3xtzZP(S4f4wZ%t=D_iqfPF&PRS*G;7q&z|H-7Oeqp~tXIG0e z?f*I3adK~q@`0Xv2G##L7O&Zoz92tQ=*HDe@-e@Us=bL>U>2YEZ^hD#()8ddU6cAQ z34ShRT_*N2`+$!f=b35&1JP@WzE+HDB$#7t!@hM`g&G3JhmYhh zzPfu+kl)gTbHd#Pr_<< z7N%S_SunSx@>JdyEp6xa151AABu93}g373}belof0v-IT0IyZGnrYdOnov5F#q~c}nIW6yr*J_E^0y9cdHuXBh zYW4_tCk7u~^?6a+ll|WPC0Ba?pU~y6)!55d!tut!d?#zrvZzcI8Sg6wt(&K;4ZC&f zIJXAV`y|Gn?}J>9W*=+XEV*huvnN))WGuS0pzS?e|x_bIHtqtcR)>Zdt zo9*vR`MvP?(tZwY&MW(5ZmeEumhm@xO-)qgPX<}0*~b^nWsRD2L~dp1{$&xSfqV6r zJl`F=_vU#qjnj{&EV6pr&2Y>*=GfW|muIIQTU$Oc(=??2z$x>FK!2+Z9BX73i^wur zpZ)cG`pKFdpHKANYls&NUEkFf`}*Ocd6!ml-PAn)Y776zygd##k94W^Mi`!X|IWox=Z7pytI{obCrWs`E$Yv!R>;a(R-eeK=D5*F=qn}70p@-12W3gcOCg#yiF%=hTZ zo|r4Yi2d667^^OorH8frqFOW#`aDpVU`*^k9nY4ujp?|V_DmV6?{?8n4)a`VEHqrF zA5b&S2zvGBY+h#1mM0}OwdeZ{F8}3c=GK;M`*;6t&nfW}>_Q?A9J}{@NxOT>Gw%ha9r_vh^t{*21w zt{Re}o$G&}KlD=K^}dZ-;;sw7x4aJ6ytQP>G|^L^{NAqK_j}#4zcabkul76twl~Hu z`qc61M>S%3)7HOlP(9CR{Z~WrJtym}-iC{x>{dUzbv};q)Wygz&t$ckgkvtOwQF0y zkKMTa+~I3Ct(W#0+OVywS;f4r?q5)a(-w1W!QY#=8@)aM#^mW~AM0EcpZ|0(T+1SSUj_HIl4|~EKW!UY`%gdboVuDj=BrxItGd!dRq+?UPhp`5&sM1XxM;-Rl8uOC(N${>~ja3Wzog}xvOOv|PFOMD%-T#{mV!vn3Kc8Ovbk^_Z<%gzB z30V`fUvmHYU&7Jl?~dJLZWpN6%03>hxJfGXyz}B;TQ9Cl4KCTSL{e+o`Onru|BruW z$a`R9vR`K6uQyG~b639Nc$6j-Dd@K3($!PZZ@8E9s%Kr+el4nlt@ZFcTkkz#&ZMsW-()}Rd-iYFnd_PVe$q#@OjcHDc5loH!9!K9-xd6}>IhIr~ zcH;ANo#Wn*)f5ap*B-Su)s^L*w(iTmUteGKC6?c9nlM3zt-;YpdzVGWEiTK}A4WP) zjed3qGp*L0tsHiQW3i($H#`6Fgg5G!KfE^jYg)Ff#+=B&W!_h34faw>NFU{+5Ej#apb6W|EZIc_g-W-%y}X$QFeXp zi|enYR`VK_OgLz;B}6KY$hZ_^zdL;Y=*v}cZ|6!J zIVpG6Z{oonMh_34jJaB3?t3NtLWo{$oFA)G%zK|7r%oysKi&Q``{SEv`J^+&{MLU} zx*z9%D%`e9Jl?45Z>ChV%uVTw55&BFH<-;4n(=H;!Foa8DOx`o1G#^;ru;wE`!_ge zpV-REyoil?lfR!7)%mlp$ky)fCw87Uw=Z`-Y?-m_QuvJU){7PYvQ;xqJQCbQ^a4oZNTa zv8QN4-tmd@pX;Ohj@C!yTuZD7*jQTc;*$FJmeHjkfz4lJm#DAeo^4%aBc|W?TV%bB zVaU}woks63=AR<@RvmdFyrOsh@$CEU?7I(?MXwA<5&k%%a`x;G%YQ!kQhiu=TAO~~ z?1aA##?0#1{G3CMCvxci?9aa^EY(-!n}6iv)U3};d--1WX%~J?owiCp)jj4#{Kbxm z^~a08=IFd*+8oF7RQ}1NLsyDsX==Rxkf#&cq-U-4$J6D&@8+4)T7Kl(FLpY;X6@pm z!D~~_Ui0V(lb)|N=b{(t8qK9jR2*3_6^629^^^!A=)^KEZ#^s4c` z_N@6O!?{LDbvDytZtJhCt6rF0wN^BE$!#odVkR)zNvC$-1Gng^dbLH5lKpS3KOnO^ zv8{aN3(mjGuqhrKv?dT;XKjJa2~1r<3Z zgyzUd^VlnFdV7A}!+$#dr>ArHo=}k>>vb(>>eIhQy}OiNK46=XnWgm6UsdH%zo~SAYkQ3K{;t|{4uTwu zQVh1rEZlnYzDT*M*yBAHbT??sF04N#wNUe!fbvCGw$JV}7dwWnxgV3TX-Vj#@-yz8 zTH=R}xnsrtboSjTPyY&q&RUT2cALkI<;Oxbp6NAB z+WhlK8q3BVH8R^34c7jhT*7@O@72zfv+EiHb*nqBPMcfBGr#lV>VQ>#)h$!cMt$Gt zvsll=W`kJTuBCbNb{5%uQ%QRKC{Qswmp_qZL%Pt_{<*JRvMX8cGHs3yvYhZNUa}~} z@@~09eDe3n(P@j5ZZk&CGnlu*V@lo)$&b;`H6$MwocZ{M>2lD?P{Xoy!oO4Ze?G{m z+`fOwD?il$hiPAT7wDv~|F=Zwp?LRwGx1dE^7EhSR_^-mhJzZ&!lkED7huaMvPT#*nKdG+%dfA#J zz53>FC337cc7|=qa@c-=Y2NI2D$Asj;~GC=})&*W!YJjB}WUpQyJ z{_Fe8x(3$eV(hoh-&X5RbrfN~>bs-B=z7M~$A5pk{XC)UR9*Yhjtw&P&WRaL8PB6M zuSlLPtD6&G#eent^icO{UuT?HEuR2iu6v-$Wb&pXwn)llh<=X|n^iAy^d`c~z zvfz4HSd^lm(EO9!Yt04jGHrfrZ7JJ|1|fu4evUOt}VK?j_*{=1=H@$lJ`O-B0s6<3chk4`94jyuh=^T48=Ja*qb1T%kFu{<-)3CW%I z_-{+C_Jq19-viz$ogH^r#B@F`_?fu;bNAmBZ)z^+8tmR*)2gt)SO8+qLvnnM20PHSw)8CpHSd-q$j7T^{$vjlSJ)@;lYPIHoecIPBN0TH0B< zb3t@M;hxjKqeES@=gjbRnxQ;x>FdQAiY1PJciq_K?d#_5k>WaK-F?mDzn6d4bB@}x zYsJJ169i%|Dz=wyV)JIS$=jGUeR9Z_O4;najTRI6%Z|8Zl^K|+e%O$Azc@#4Mzh}Z z?V(Br4{xu!@k}q@A|&mSP~YP#eSdCEh+JN~dfCpcHbU8#qGwuu(Cof>xQ}o)-mGM~tvO@yB#pR`@)H&Rjv8$Y z-aYL{>PxTv>^!=`tP@S6pI6u$R+$F%t@J&xHe{iU)wZ1x&lWGe(N|{CaM0|v+^R$8 zOpAQV9d?3>-z5cG>T2{VDz4Yv%zD|p&vy4sp@^Gmwnb6A{>LknUxi+N-nMRMB>!jU zI|a+SA4T4o&w6P}{&T5|`|gxFC6wiD?|ref+$!eygME*8Hce};ytwA%&mEtiWgAxR zDpfC-$-VC5wYw+I++6z5N3}A3rse)T-!Ct39-Z%ew7f)GJD;_v{@Rse8>6PT7kDI; zX*s)YFxx47wYJt}WBwJ>GJlgV>!LTL{ET|jxov}&nbU--PYQl({#%{Cot8DpJl0ip z;X=iVpC1=?aBLSACIX>+iIP|i7pX$ns_o7UerZhe8x zHMGygrCzQ)5lS6Rw-d929UNvqUIS*$(y`#fm zAJsEKY<9o(c^lpMs}uL$UKN?lvwyewF;>U?4QD#mX_bHfwmPG*t|Rqv=oenDjlEWz zkKWGuDD(E`?DB`Nj^57-G|IV9k*Dv+q0<~+Yulcs-BMNd>pl*W7!nW zv+;TRUrMdHz2|V1GKX)-+L)8KpI&-=vFz=yZ|Qq?noDa=+;wGEXw&*jrYm!1?1~mY zc#y3|+Jsxjn3v(t5!F)~b~iT#&ti3vFI$wqc>|l>{}T>2=D(D6VmJDIFZii+#Pr$E z_PJS#uOABSm)`&SPEppY%~#*^@~M}dRTW7BgiF9sBFPDf;|)#h(A!dikdL#>J7hyFVV<&(6NczSz4=?Cc(U;}`zbQ+>bA zdB(5KR_K+`_-aq0LEHxQ*>89^MQY!_m%_u${OgO$;$Nj7ZvF7Is5&z<#w}4-gpGIO z^GiE)bUM!b-kdM_{Kt9+U$-fZB}oFyS+-5=%#HF+d3w0A$AN?E$mA!J(t1B$vKIbu zdXi|=@vMO5YH^j1>aMJgb$)FjX0XGY+oWoL#iJ<)S{5dEH~-#{kevRlgE=tn!PDdK z-JkcZHLUz4lh3U<)g@G;{dV4qSEWkNA3nczEOpb|l%t2b-dA1e3ufAV{C-D=)48a3 zmT86ezjH-Ds}S@)c5eE%ulu&Wxzqg8UiZkN`{9cJw$yvxQF^*0cJ;D*2kv^`j+qnx z}AOGdK zkgVSk?<4YEZguVba^JapMOa1Ocb*D;dTN^EQ?I9maZ9^4HSX$uA%EuVcjcP)w=W9n zXD$2u=)mm~S(&S*4S9{W@ACT}A6a7Z$D&@pF1Cz8db`~5f6U7#OqtXdG%;~S_->wC zTURx$)?eM(m%B>I#3W(b#xpt#)-PWmC3dK!LVkL8|AkF6SATTZQPiHI{kryN=Z)+? z4)&KPG1<*NwBW90%`ZDqnctgpdn5%e-}lS+=2(1F>Y~8S)Au`8&hpv0(H(S1frY}!`8F0OTX4=0ystq;&v`ONuS@^5=H(`NhVGZ77Y9_?{` z@K5VsXTQ;|SDXxI*YaJN`1>ONoAZ|HR;=F^T>YGXTeSP!-ueTL&YzaP`X+19>e&Cc zi}Uifgkvq)0ZTU}uf7=aZ&7mYyyq76clb57O*A{deTrY%ZkZ=jU*DepG)m6VHnXOp zZ*zd4b?d@@sA}bA@LZtr*7r9OyEws`$@oitEl=U$s)6v z{#ymjvIUD*vv&bt+2(7g@2WnZ2BT zx-LS(&E?Ud!KtepZhyI{=f9@BGHxF}yH>fo}&0(Cx z&OQBA=I6)B2QOu4Gj%`Sm$YWL%$IJKC@K3TtzWrz9(LK0w&qx-?xs%<)$Tmf$c|s* zoY1Rc_c-CwxME5iIMxy1o0VLn6)%LZ%(%O z{#iZx(@v|YyMkple29G}$G2R}+dOLN^~Sz#uUlDyl9F9JDz;9|+C2Y0QA&P5AoA`Nc%H1U^w<)|9xWT6O zLgYlKZ1#i4`d_BH?bx(D$sj#@qk8Uy3f6mfM=xxuc;} zgKNoi)2{-d8(rpA#+SXgu}$k%oKEfqpK@ihDU(h~uWfm$*Ay83K6}=wyLdCeh>u*iEq-6E#t+>^*7wf0;-!{4D)UB=kB=@nizIduWy}Ca5+U(9d zThI63_`m(Xzft1UKb2YhQ}jgDKE5nob7rw-W4G;*yPr(=sTfFb%}O!W*J&!P)|t-x z^S{bg+!ozx@b<6XiU7SsuNIbNC+v7~ z*5aD(#4~kb=Epo^jP|{7Ka;C+S|%#5b9Z3)f0nrq9sj4gD!sh0a@W>V30Kc=Pxy0$ z_ea5}w3VulS$R(-UrEj_xXsJEM#8j4C($rK+*W1dk5#R+XSAO_n)fF>Ejpyjb*EcI zsn+ie(K|H!OSp2RUz_jAeyZ@1Me1ikw#`Qe?LV7Tq`LoqH#f_(eP-IEvV2LkZogB< zTi4hlr?VG@Za($lNBBD3xv2~>Up`rNCr7-oQ@kbd$7lMi^?Hx^q^fI61@#v!{ZM7S z@fP=w8&{g%FUE!aa2>BY%eIN{zO0-j z9Tk1hIsDMqExfy5EM}RWa8PaC_srARyMiqP9wn_ibv9)~No3}+E{*1!aT*gUZY=(h z#`-+#)T&eSSshEfYVSpWCXQzs-rV)#z8}xClt~XQtUeuMzc_h~)@pZK)%WWI&+UKF zyffh9&qY6C_n)~JFRx{_K6Kraa_OAf<8GJlyx{Ol|Jc>AIhjF?<)-+5uIUEKCRh1S z^KMj^b@g7>cYmSD{`aqMeobB8X80*dVZM-GO7N+qON)MPncU2#qniv#nF&A&a&+3{SXah3DN zsrJ8Y|2Q^1^$Tw{%v|)~g@$i($6- zJI?p;j(s1`q~+H%yF90Sp0{l0td%AQ6%6E8{wYfPv|sv?(AA#ulc)X!wyJ(I_}#f~ zxx@bDUwusF^pdLsQiT7W;j)=uH$CA-KG(aXhu`%7*!)Ynr?Vt_`VU98a8vaerj>&E zyOv5+D&Dg{kT-At#=EI)$A12BHQ>~-w*E2cp%qu~b)n5h&b99z%RNiyK3-Lk!|7Z6 zdHTkl!<&QNEH|9qY@S`VbxGmv2ifAL?vEp)IMW&<6Io9S*Pa$L*_Cnl#O1SFn!27Q zJNBNH`=l;**}W#pCTw0zqwjLFM;WgIXDsV1TKM$Bh7jeLpk=06(#ev+WX7#cRopjw%CC|XT#^lR} zlWJX&<+i!UHN2RZzo*yks0kD-3lRv_-1I!7s$MU;`sVgE&jMw?%(vSft=*fSkhOVzL80X7+lvfqKK-8bUdwpX)4KNQTeB;hb%N&RcFj6)p!eO| zJvZDVJb!FyJQ#6EZco6z z7NxlxHvW-b$8(^rkZJk?Kao}CQx<6$Tv%x_=~%~lnJ?=rGd^w#(e~M(9?0<|OUBFc z#rFrxFZnM#-*)fEs+-$i$d#_0>dZWK*W^P}CVpg;inn$RO|YA`A^V}svi)qTYCd}Odut7sKecI*a{MW*pD-(6qJFsCl>7VGgd_w18J~U4%Veu4zgwv6 z(aqTN8ustsZ}k57bm7Vu;cNGQD!#2SvHG#$!51Re918_!^jr3D{Fw0UWrOkC3nA|} z`LR#t_v2D)T>ErVLHPppM=D2s6BcO3A9>hmdUJOAyo_m8GAq;8EwQ{OKkcZlb4--Z zsa7ws>sOtn7Q4Aw&d3%w5pi$)Ipf411;Gp(6#>T8%P;vQ#_rj-Oz+Xj9X5HEyTV>A z%k8N-@t`X)rSM%$@Ele)D^u53$8MW6-FcT{*}Pw^L=UF$?(N`GU3Xy9rasq}slU&k8$Qk# zO=Q>BcQLIy@F`s|e(nv~cSTd3RtGQAJJe&aWY)@6elwffRR3nR1qiVJsI7^L`4jo? z5tmQjQJv%qU1GN`W7XZn=fUi(L1me{#U2=3UiWz&+!CS{9$Hk|#siFYOgGD_~8N z4?LS;7Jqogs!P)|C0O5G_iCSV?8hu;KMKgZ(ODaDmKyaJ0;nDm|(urBNO+{7fj z=V}4ZKR>&Mn}*+3S(|s{G~~}d?{HGU`|XXi-H$FcYa7)1ByMJ0If?bqq93l8^)F`E z`s>(iJg}ZI+wIB`f8BbwuNs**|aeB1@Shu&9v|dDByUMqK z@7E+A)TYjmym#XFizONPUur`lQcd)(-uQiF^U2>lb@J^oE#VbrUX|LJA#BFm?NA|mou*LgFLYL-uSyV?{eEE3M zl_Tp69&_nbSe!l4_5Eh-C%3l9t!I}XtXA?4I{)x;LGR(>GLW$cPbOYV|DxJ4b@2-? z<4)s=2amYl{ndQ1S9`-DN11rNR-SMomwG>Yv}odg=68-quitCaTfo4;z~JfX=d#Wz Gp$PzYwmxV8 literal 11956 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 K=d#Wzp$P!J%REW| diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png new file mode 100644 index 0000000000000000000000000000000000000000..b3e4f120b4afc4572a131be25cb7a28461ce5769 GIT binary patch literal 21475 zcmeAS@N?(olHy`uVBq!ia0y~yU~~at4mJh`hWJO%DG-=o$}wp}ilfJkO=aJf zT61!Gs(kl-?;L%TUWK~Y+F;K zks0~_h{w_^52IF2oGtc0@yE8a`aIV>jZ^}&Rs^kDAGE4HENj2ZO6Dm+zcrVB<2;%9 ze)mj`z0d#W+Nb|NEqnOW)p>=NB`W>iHcc0ZtN!^lAb$>bPj0x^+nm4a!?N1Lvbuv# zW-Xn3b$3~LbnT}lKf~r~T&th?J2&tu z|CyirmwKt5|N6G}!en7LS=02|`OnnvUH&n#)a}*RjQ%P|mZZazX7XBZUpr?@>EAb< z*4xh7C7u2iDl9xz=2GCFq^TB_DY0!YXPmmzacR-1gReR2E>04&szVUJ)lU**Du?)$GZ zuF#YI+VsOMC#LH6g00>Ee%qeSPOEiL&GLF1aqsg~Qv-3=LcJfpS|`nWx8A>UD`di- zM;AXmtvd0+g7t!Df1Z{n->bcAuRDI8ZfjZVf9`X2QJDIA_3e9VFGZT~&wSLl*)K$$ z>2vAY7n@iM?@qe?J2%=s{nxWl{<+_>J|Ft175Mo3i@>S3rgP3*z488%Z#CiJC!ezG z_aAZnbd}*i{iyoZI*4U;n6>T z^RDaD*tingBNaDmCa#v5pmK8M=V`XJulA&#-y(K)x*NaMm-gfPo-T>^moZ!^sd9;P zvA4*P(DVO)PmI21Tlwx-s`34+zZ8~4ALUk1O?uy+=z7F{p6&c5cJWY2d+RtE+dVsf zsYw2Pv`aj{=HtTY>wX2#Usx`0R`W)_PpK{+10e;+tk}q+jM9CTo|hT{#U`GD=LpgFL5>( zAHHtI`J?8u;XjED|Cc$xaX-bqfXDxo?7e=TcdttSMOdw=`VzCb@~o)+j_2(088Q~H z%#+*te_s8_E^od&fBn8^v*I_tFkvk`ed_qGy??$;-}fo%^PzmLrQhBfg-)n#oGr)k z<8Hn4=hAP-Bm6oSxlh!)eZS6Ny8Pv<-{bb=om?)yKKj?o-_P#VpZ`+0yzbQ*&-W2Y za~3%T{%qP&_%WAvwfNs3lV@K)zc)bBbWw(#;o<$OC%$jr`Cum3q?@icGC|gcMmk}EnaO{?@JjwCQ_p zMA`G{A(Qsp{IaZcZ^2#u!_&HM#Q50wo;QD(c*juuH9!CP=~kijdb=+yzY|>d?Zf){ zQnw6#>_0D6e(S4y->e^82C12ggIG7doVnUf@BeMy?b=J#w*5O>aGmetzy1r77rw0v z*due%K+k`bdqlWoy1h`zMmNW%op0u?o^?I$$K}ZVKR+zgxA`~qyT9FYb?4vwru%hO zySzk>5$#ktsfWnY?Nv$ zw|NUd8^z+#AH(jjP@61|ZE596$cxBT!Y5Cvp_2qx9E#CKI<3rx&?UFA=B$n!Q z+@2-=t@7W);`Mj8rhZlLeIGQP|DWs2?47K#-M!QK%Wcn8l+_8@MBBBcKboKwWhFAt z_Wq2^x|Q#rmfy4ZHs_;y{l7V!>h>IJ;u1@>JG7t0TNnQPll|U1#Arc=UMhF>@^5?Y zoa*JA9{slE=V@Jg)(tBBQ*9n4{tQv?D4hN&o7<<~<@HD7=Xd`fc%#a#_xs$<`TK8q zY;Vx;b-eR2>$2VYGyC2opY<)TRS)$1zv$-_(?8bYIV)duP2YFZ!2IO#X3Gy;L2uf< zL~Lz#o|Ddeo64nJl!C_C_#KE=esSL&Fh|h zuHESs({G`7?c900<*oDGjxpE-J9n%&uGoM7_o+XrwRJz{>d*bO@b&MnZ$6hNtkMX~ zaEvj0e=9i5ruyz(EzNxf)9sz!|1W9T+ad7$$_a@Ap5l#rt_#HT*XzxG_NsDylzMmT^?yH?9m_ty?}hQP>5>*)nl9`2-kM@!Tl-D@nr>`O>_yj|T#Thz z!SnV6pXOdrzr3Z_^+x^aGnrP?_~P}VpS|)utGZ1tMLb&~J1g?{O5^mpXY&72|D@X) zmR^az&aj-V*Qx8`BKCWKm%i2QUa!lvJL}-vD=$0t%0D|8+O_lez52(yw@Fkb$TLVY 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 literal 18571 zcmeAS@N?(olHy`uVBq!ia0y~yU~~at4mJh`hWJyE+<%f$yh{F;MkICy6e3^8C$+&cfFs4UZYNF`c+7t0Qc)LBXg@fPDdb!)t~Z zh6Q>I?d9Q zAVVbs-$a9d3<<(#l|GBq$omK!YC3U3N$kK|R_}Z69t@!ZUYk!#zYvmP{lM37opFiA zE`4E@4GKnKg4OklJvknm-mzyqaGBv9V*%?~Imu({5dxChSC}_2IB?%>yuf%ihT;0< zqFIT1+E^!k6y3Pr{Jemnj?0;V{p=qk8>+d})-5k(Kk`Fdr=v%|guy`gfaNac!o$5K zvub%8uI_41{KGEha!gu}@dLx$Uv01Wmhl{Uc-kw& zN!{hnbfy*Y8}{V$D+HH2^xWK_U^LI*4*$^&8fP>17Yj7#+&yUU>`>E*DXlY7ZP!?H zaCW5r)A2g{&?Rq~ON7A8O9cU!ii__ydL$V%Y-hN$(pun{4@kH|<^acCM~^g>hPw=L z7iBq;<#alHJUPr~afbXA?AXJyKrQt8lNWq#hU{W4%T)F;U5GCi;Nn~uGRKr*-xXP& zWImk^pTf3VN^Rn0vJ;N2LolGR9* zYf&}V0g0K-M*q0Em4c_lU2>1v%XUuv`24wozfSepKaeq%s(3AHF|VHO#ht*kmc#?8+eW^eb9ymWNF^+TB|*@C;K1vXzT1f;c-uViR> z%Wx<-A8Y9O57P8W;Od{KW0qfY6HY}NwQ0@NPd~J?NK|HiEz_0t>d8mqQi^^r>NzO! zALRX%8B+pS&os9 zeXQmj&SMM;Udj@x<}UPbPpOJbaXRD0i(&5BJPVAUsu?+QaVs$wZQj`PdF96gezSAg zk2ap(k^W$Yh2yb|!w~|_(qcy*xc+%}e(~X!+dBQ<{_-EVUc8j$nd#CXUuH~MSabB}1LXppS^QTv zZJDnzN#^|m_9O96HvLuad~C)beN=MSo)wap6C8PRj_?%dgr={F3R06Z_vU_L?_y&UT3z8x+{&5<|Len*Q+wR&7_cXIvz9 zlVw{@aBX|llp}wo8d@3dEw{f}VCInE$Rm>#xP9mD7rois=XTz|QM!85&5!M|Qxl;(`ln5*N*_Y?;>4b4B*x{*?ia)@SFxhoW5xd|XEIw?wiI0Ry)GNF z;ZV~G@xWiDliw-SJeyP8Fk!-G&Rc2~&|}4eS)!t*>sqsdg7Lms*6tTh*2#&iKV%!uyXK zZm^%TjN{SH-comk$#p5#-*nE(_}-q&;SnM5b}?t}obqe39U*%1OnZL)x+#@(*CczH zHPg)_0av~4WVac1B|2_l5A@z<6p+^Xe?gUL!ij|1yqm5sZ^V_etg2}2$y9&Mr*a|D zF~r+h>#mgOgd`7(c{MVA4^1}b?cJxatE6eMdwK2maBqvB2KO9om+V&&=5#aC>B!<( zY|_=Px!+*J${5bRO!kT%9AC`1D%vMnI>ETW1c?XtRo)w$Fxc2DAEpyUT4mN4b zXZdt|w@10cHX&)|1WzJuQdlzMW6b?3N zh$o8uT75rc)`?}2!4_)!Se~z7f4j1nkF)Xd*YF0hvsdq*_E8IXy!z@cW=@d^fv2Cg z$#*BViPRrG$jo>#Ea9b*rsK||4;voOEIibE~9F#m6$Zkb93}*3%Od3b5FIeTAuHi`A~X2lQY{%nb4fVBTPGHNnJaB zN+w2eV#r68OFQ2&PgrWLP~XOSQEKXn-FI#4g4%UrX4fy@C(r$I$)*>#zNs_V>fU$N znH#R#9K7U_P6S8J?ZnCx{}rbl=eK3}vP~{cux00={4f9QFDAO`GvuzEzExm$mQnr+ zhL2WnysNu{Gmf3E;N)KPzC)(}KU2qbSr5r4LGC-YpE$M8WSOU+%bc_RC3lL|71r@e zKb&L2DzMote&+^unZ@tJFY2de9@KfgH0ght(1Dy?Trd6X4`18FJi){ER`?pvxeSu} ziH=`*H+KJTmVaZZ%WiO|yXwY!4~M7w^loi$PX2Yg;M@Gm5BQun#Y=VHvPE4DDuw2_n1fdh;Vy|1*K zG*fsR*2dM;MIEd^cKLn1it3%~LE`hycYObvo;1&IqmkU9rWdyT7vp+6JDew7UwP#8 z^^^n4UnV%7m9{kz$+&x}RC2;byYt7_Tl;oiTvYWWjbS~LhPtKM>Xr-H3#Fdw7|9)I zdSS^|_0WEW^#mag^>7i%*)La?ocgtRg1+a+nI3j;*G4JMuHok9Up2)qz4nQrm7)C6 zKFd^LohFSVYq(5H)_eYp7S)JV>zTK#QbkAd%kx63*1OyflCl(UI(|0~V)^*!UW~xy z?F@EHEK)xTpKZFoyy}fZvcBnuTe_Kh?!RzY%-vx3W%KKlfYUdsHO&IK=Q>>t;Ve;* zaDLxjU4ZPk+NCd^y|m!=NFW87T4~(FM7{;&c1p6 z>X&;(?j6tjw?sJkX7A&pIfbmTk6%2rJief0jbF&sq}IzN5udGOwb*1DH99!AY>{1N zEx_8b{CDorOO7E$Q_`$w^W0JX+f2=O z*b02g6E<;aRj0?l4W?EdJM*uHq5Y4=aB<8bCFISv1NkDg4Fu+3KNbz^hS zkpEWA@bAVBmp7HU7i11|MIG%~{^XrQ&DpNCtHQRlR|S+C?0eAPr15PN8}kmK0}B{x z6J?jGzZHK{na))Gg16T8?fEj}JAbZuF5fSI(fKlqkp^#?r6kAf7`7MOiYCE(1P=&# z@%?X0dr{Z8pYzetlzmZ=L+hLw=NX5R)5+1GW*fiEU^ow zQ|hLvO=tY&WFZ^Z`I_ae>8+jRuZ{nybSSUbVHZ=bz;)Bb`2PM<_D76|Zah+}a+qlp zTykm66TwsSjpwd^p^(IIIMd`=Y{|vQOor{bFkY{U2Q|?lvo; zUh%c@U6;Eq+cN!+tLNqS7ns!qBt4=X_-Ec@J|@p{(b4^PqZi-afMu`vj<`>|?D?Nz zRr9sLvzuGZzAVq)v7N_gg_>1kP&3m+_7zHLEhiUc>{8$|teN~xfoY=gmh(vmH7;;` zTG>)yr`vm^XX7^B@3Fq^!k?L!Eb)0VbDi#wpP^S}JCyR4p6Q&p?B6~gF&D$v71J-- zGEHO-DSh-lDMCbDHs#9}&56IG^!Ld9ZLAH+ub1Ar;reyi2~0T`v5N)>%0JvaCm-kI0ejCl_f?nCTEIZ(YiMNWwe#mOO7tUV@{} z+-E5_Q@JZ|R(e=YVoRSVyfJiR0_~yU0 zQ{xO4g)7ra?iXvm|CVi?_l$EH=Oa-qcKccb(QNx$Eq_<@D;J&)T*76jqvZb3@S#VB zSpTftRSPd3{d%z`K>7N9%|+Tz=dTSbWj>Z)*$_2|3lpq zo+YA_Sw5D|+?oB_{%%vgyT04A%=}sB{!TVNere;!&$;WoFT86LllbxL=(?v{RaycM z1+z(77*q-R-;oUB7curbu=7)7!t$4~e%^R2h6&aDwYcg*?03+=6a4k=UjustZ7 z+5D?Xv+0J*vcq5B$LAdn->x^o^v-{QLqEQ6`n%)#^oJ_~qPrSPBxc^S(@8cs_TJKe zm4w>5_Tr0k)6=VsZhq~}eti34wvvFDYIexPwr`W(S=9xz6i!(aacR=IgoDbjYC?kf zmL@%1>|JW30PI0a<&laCxa zdGN0h+h(DbyhWKWGiDkeH~vs}Uvk}Hp{Y6Zwle#&n>p=%9eqM#ozmI9`%?`i9$!CU z>0!B#`JDWF_BrpqpI%x}B0T4FUPJPU=?)>PlFIiy>L%VQD)AL?DSVUnbNS5KGrRrS z&N^KR`}#NU$jL9^+jSgvzS@{^#oMd&>Rg?Uo;9;-pD+7qO> zdFi5Vhw~aUC-LPct$o04{f>`NI-#>##_ z$2DvVrcGt+%kgpw>w4dL-6+yDRd(0i>la_eC5ek2S#V_5^X10;ej8)1^?H2#SUbXI?Hsc7>7m3-N5f^ow}a&J!niiP zS6|5(d35LN+nd=0_6bihb~*jk?oiD^l}k^K1W#u*T(Khg=IWV{TvaU#<|fZhLoczgyNKOS7hnQZK~dVgbK%Do&nw@IU{S=UnOh=f{4A!iWZag*!uhGxZgE`^8ZDd`GihHQ$doUWuU z@cyD^$L}?K3ORo!Z;Pz9J-H;N*Fy5}jHxjvR~)zX7ln-wDJo#y` zWc)?Vj%nGwI$^CI%Zr>($NuCyb;B#+PuJxwzK^Q^=H2CPklnn5EwwZ1n%Cy$(zYF^ zUonKv_ImkLp;xO{cXqOsruY99b2=pK9oFdU&OK)N>TAzgQed-q6)!ol89m90x z9ivQ)qOQaJ0*1TCPD*%miz}IMFA_K`&k*zUH3R3_RSW)Zn6^&tj8$hx_W{$(Y>`|4 z{kmUpCD}Emo2$-`|5Z(+!6m_SW}8#LDY$!R$keJIw%pskf^UM^n(HCCjPA{LMs8eQ zjv{|jTm@Kb=f8aTs%iP=tfj|a_;svZwdBtx`G$L}xBi=booBc~r}5*2Sy##{W~3V5 znAEY3Y0FO82=mBa7vzGUGes)DooG5?`wOunmJG`#u}<<2SrIHJZhu$yes83jxe@=$ zs=U-MGR6B&MYja6za`D^g?&TV)3XvUT`uXwe>pR8QriPQ_v61G+}SH&u`+%6(TfKr zG1e(Ax^!rhb<6q}VlET4H?TFRy6$|iQ13_b!}~Wj--(-iX2Xeqn7uURVmAvkbrSz9IN^&4MUvN#(zbj4wNFS7=J>oW>IP z=I&jC$3}Zf|Cy+~x7O9)WF%p;l>b%9%FNq;Jw*018_ZAZ-TiitVeYoR^?g7PA^#|FK%|E(0n||;$yk^+4-N++mYs8(+RTk6s?_yiM zY@Ku5wq5EeM|V9n{IZzQVtKl4>UWLiMpon3-rWCU+}C>BC-S?#wzeB_xhl}qNQ@7r3Jvqh&L7HpB`bn*6V6u#~F{-nf-a32AeLLIZ9WA`FroOFKe zzjRu)k6S+Uv62ptOxyXrLZ-LFf{pf>FTBvXbjLf!f~_w~ms+n-a1{}$cWzoC>;7}s z0nyEkvc?bp{Nyz4v$%Te(8c%m4s)*51t_nbwJ+i*izCa~l4;#<9VDAO%N8s;R(bWw z))VI^PhT9dxRhz#zZ8Y6Ef=2^yyM%L7w`B|rvHRS_m&2Mtli7b*{;~-zTA1j3E98v zHIshu&x&PspE|8TPg3{r(O>^pUgYdkjAvMQZO-YgjUor!W-^1Sb&ZN1vk={U7mn-= zjKb9m&t5&d;lahF)A*xXI$hSQ?|SrS_wCc0egrlC*vY%}=`9xqeepj&#yNZHi)Ssp zm$O*GZ~6OR8xA|&h;?}zUhY$v{oDOz>FTo=w)nW@J-EPnU|Rpfn9o`hW@qN^nf3hO z#qw1-h9aA%85*&F5uMCZXsVhLweGj(lCtGe5XkA_s1yJwoK>8>D8;m5}#kq z&IvEswUu{~mPak)3YR^B^Vtfd-fAFbjx3?;JcN%p zNqt@Qepi|P?f&?CG5xix`4zuMXCz zSs%9t@V>b?n>Jc8!^TMH%8X45UP*)8ZLls8z;Puei(Gm#E#iq_^PRPezMB(giu% z6P_;hxY@z_e8Cn--9 z?9K11wj_y3MCu7CF8svJX%aYp+QiwKtSN$LkJR2R)^=|yw!Hc^47gR z|L0FlOKYp5jn0*?=_xJe=ZZvK`1G^%SDiS+4DDV0KP=)B@23=2)l5FIC;9>WP zlV8^{iOY$#P4ksrRJ!?CM!<5hTidO~udc3|dP~s%s;zlI(U$~yITms8j(;MBHV>a( z|H<#9aP*D;U7iglb_)(WBpdh&IHp>EpYWJldH%5}cXU~jX1B)|$2Yp|eQ?&-$Y}G1 zqPOoOHWx}?%fHv?+Ae`i(WO}y|?zg_Fq2ZdgYAu znny~cYGoJqW$t3u=<96K_%Y*~ko+}SkA_PVf@JKLMaE}tdz>`w%bw-T&#mYFsCpQ* z=j+LEkMkQU7De_nyZ7x{moL8li0kw1?*HTNURax)-O^Gq<$n#MtO4(G_9knspshz* zKPjbqJxD#uUT2rMN}=hJq|49C3U?b>G?Ml|;JdZCR_ws+qtm81rN%9}GReq1baQc$ zVf{xgpV@l_+9zKrb3AeS^x;WUvty^vFy-65$)4@YyS|POM)BuDSG)y{BfX0z2Y#&gWY=^xj1>6%^6ZJG6=H=3pV&e0`CX(Bzx=l7bPD!HpS zt9P3Ch`Agz-NnqIG;8AL3qDpivSMzj&-XlkVEX+(4f_9n+g>`Cv36Tg;TspDca@(+ zV=gR})w9~qysIpuwWx6EH=TVG(|Sx-oKi^$;{MaH`h@f)3m@?#M(G+oA&+cNYX5n+ zJpRL{=*tu4X_oh2(6Y4L`9Iq0xQlDznZ4!<@4GIQ()_wv-)b4h^^FQu{-U2IC-W#; zTeAn-&upFfx$cUr*G!I$a^GLoZ>Tyf)+1ZGTD!O1ux;9w(BhzSg`>UHe~mvfX&! z$7n;5j+ztJ^79+6AG_PX?d;z&akn}D?ESW0X_Zk*etLXGUv%bqv6B*(n@vLnT;*<5 z?rPR(_VLP#^Ywq5npfp$aZ>2DvRC%bmRGsG>OV`k?)`rxeI{~8NV&!|Phaci-SYL4 z)4%Q(*dA-m@^Pxx3%--WN3zqcm%7$II=03sVo$HSbN9NC*ka42qBDKMwa#z7yl9<| zkLZzZjlvrtF1*h&^k+SMGC4tSv3jLG!{iPxOEWY&wET~9)gM$-z9%#P z;?;T6ENnyWhsZ2BGvj;n6~oWPmkNvSnuJ)^s~pmv@@CI_4o=sY!`u`0JS<-M{o9mU z_Wm7w6Vmp5W>Qklxq0kwXI5qC`F$w?7L6~~X4I_ootv7lNMTh@;!A%0LyK}0k}o@M znG&{bs)A4K<#QSyoW;)`UYTaO;?h?+i#rlK-NG_^CUjUlNw1!DR7L#ZeT(voMlseq z)}Oj9qj1~sam=FA3EfVPYgS~$%r^`a2z@6n7izK1%cXgRYRFQBzYknz?5;B`pZF&6 zzk6Pm(z!SPBnx}1ru`^%&#*YPU7{gaI$f)Kp^n|`%^cw|w@wRBj?DK|zkHKZOe*$e z%IZTKBL!?1G#=VGS+sBMtP(BWn(jS+mb~4}wLJUkvvryEe_Li)%@q)eEGmsG`Fiby z$Bm{ni*HXo_K+`d){caeUNQIHE6qHdx0wA#-OT1&XYyK;H!DnQteCud&4KOrFGcFD zoHFa9+Dx|yy?hbdfMxZ21N=&sJ}7ynXq;)C^s21vx20$l&#{k_cTb$ZI$g;$_Zt(> z&L>VS7E5fhuLV18`Mcb;Nb}{FNHNiu7cR_jR=vz(7+0wDLa;UY_R0Jw3of^%DQpj3 zwD$U$AJ5p=TR-~gb!(nz$Etfwih`>0QEhHhou8iDu2A`y=Sk`f`&T|RMo+72&6Ux85ng~Fo!`R21<{NisY z=bo0~y!U6)MXfJ;&p%()y-uR?rFvn}-}Daq|9lpA*8DiEeZ}pmpZVh-r_3LU^j^+= zWvBKk=31xIO3$y3u|?|Z58F)(c=u@S74Mx{ne%mKop|lQ5u0+g zSWxV7ov7IL1=i=@{1yEfxpLbvHH$eFXM`PnmfbrvStDM5Lg37~3%B~Luvo^q=#}K6 zz$+6`GbzNz=Z zqjVHq#eN>Hn{(``uDVFy*`^3C!>;4k@9ST-J7QF2&|s@-si`h!op?LZ%QI#EjovTK zuLXmbFTQ=g)8~Rp$GdvHB()nezvVV}KIS_9Otn~ZO1RBF%{0EUiSP2R=|+{ldiLd8 ze(Y2o!_wVTethisQ7oW6?Ob)(Evf7GnDZtJ9y+nj{zkw|t0y1R*2>OM>(C0$6KH9Q zxII_##rxYl7B^0xKl(EFvq0QRxkIP_-i|r=uwX&WW$%Y)&bs_+EZx>RwdBlOF2k0z zj#t9wXPuTRgnfEYoO9U3F+{fiZ>3CC;colQ?EzFjlnU=4g4Hrepl34;KE(A%@0Ys%d>@me#v*lMmt$&opq8_p<42dw*eG|&DW zM|V-^#yK(ecc)bZUAh!wuk3!3-R@ZBkDCprf=jNhc3RJ2=BTqWJ5nL|QMazT`t_%* z8C*Nl`;?UnwHK}4Uh>;=BByVxy!ia(v-a$%Nl8gnRMT3ugn#1=+dYn}Ti&R=X-tz~O_d9K1LyC24a^Axk3 zQf+=ZS~%+%_Il1ZILoc>o8+7yZ>@v0NXJbIddtKE$$0yIFll4CFx8x_~-7?(Xq`{#d z&aC+G;)!6Lj-=>=yl!VRa+Km9{ywh3YosM{)caPzCUI`HM4dt{3BCI)p z^*P91{qF^~>7FfFNy&G_d~e2kub6Mp`ulfT>5eZPm+wig`u@YIIp=1~qi$WBGh5=V zW2)Pq&YqUXHgEpawkh^+GX2DbV;}ufYzh$wJ=YZ};O)3If_1jSud_TGow^m5%B?sM zdeg}N=eq2$wQrRqDr zpI15=_2nFWxwbf`TU~MArrk?&0_IOia9*lAKk)tW1mPn)7+$P?#G5x;dTZvr8yt>3 zJ{IB^O`~@It@w7*)?#aBo5uQ$y34Xcz0a=sd32k@8OPNBDT{wCnXT5b%DkiJROP0| z;4LaYuge8ATmABNb2_9PTiyOnpm(KT{><#v5?orL1+%N?uD)S=DqYNdpC|rHnyvFoR3#EUiW>u2QAxtQ$+_n6&0C$@t?S;!+r5eO%m7q{TvJ$Hy*j!-s4ApSp*!xO-&G z^fNo8VrFk1oGG)^@XxA$>*lxfbKKr#ePmnC!TzFNf$T8 zFYS}Q{$f#(^()SEZz5UK=hc&n7yGe2OyJgq}+L6AW2E&G;* zO+U6D+St0&xyf2w!ddY5yyOhdWm}vhxBKNT;aZfz8nEu6>(aVDV}16Sv$TR(#OK~W z__bDhnOeht28qB8J~ayxShsk6)n+3nZX`4zvMS;S}d>GP(7nzPkQ>-r38N}s*3^bqvC!GEuC@7(QgJWk#z zzmRjWB;@-7yJZzM>kR`1E}sZ=x%z3FZi8ybRw4i2MV3#vCcFKX`qgK3K4^V{=x<(C zhTCpCcS!W?{V`%_l)@zeBWJM@BI-dowbdfDx?BZl8@vo=LYA7EjQ%f2`zD)>hJ zEk{-%^&E{9k06_U*Jo&SG@a#Q4P4^7$7z2>xk})#^ITq;k@?LZT@<&Lb|fm+R+Su_ z)qC6Eefv|ZynO9uy>|;=Uwgfx1WUuwa;4+Q_@(g7cRz6hfRB!iX;8}S2ao(43ZM0-ppL5GiU6UGK*ideH}ZCC&G2hsTVG*7p9e% zF6|P|-?k`ZKVP}F-2H4HQJ2f#emK3%)LOTX;ob68v;4WH-3?y3?5cQHz{~LRlOLm$ zbWB7Jua|0&n|nL)NV2|y>Q#;4WbY$K-5&3qKHp*SuhNqCpeesn4i$2rdp2!f;=@F3 zxpnfhzRfCP=3dnP$2TlN?YDTB?eyOacbFd(uTuP`w%#izu9o*fO{-zlBFDS4Dr)a! z+_8M7`sCTq`iHI|DJ*LCpC@D{MNHAS6)xiavR$M9VsMsNWZUdt`}9sp`knbU$HvHP zuS2W{$=(B^ zd3Nq88vc3_YSZJcWJ_$1GnP1A7umn$vD?=DA0#%W|CrC2>@!btm-C)DGo~F<2kO0l zROp_Xap6Ayq3WDZb3XG;X1yrCdv>SDyrm^we|;v-+x&4$rJX}@ja}eMC(Awdg*tLI zX+r;QRR@$?#1+3bZT=I;!!4-YrO#j!cg94i>EB)XqXNs+me1Dd=w7+#$h0LXoh=uI z(j;VeJ-L)tS$nrJ((%b_-}!%duKn4^`?)@*^J%u3#N`+JR9s?tI`Cg z7aXfh`1AV4*5ew7lf|7ll;BcE!e>%Vx#S(CJu^xphBRl0&fR zKA||NVt%*YYTowAXI2~#nre35u10;=%k%mvUdL~|HMq=L*!zy5X$G^&=~C~zQCAH= z&lNW)zi3ormM++rWm6(p*QRe%B_3t|{l?Zb=7wpjc#UTW8GftH-K5YqX(MB@{vxU8 zj<;4(ZgSCZ6m+sw=aMH_X_Fjf|p|`UQyS}lT$C@5UefMhN{DlU5 zm3@4BxxQD{ZaSG(^DNXW(D)qJ*%Zf`oALgse`oBR@+hz(>XS%rUA=QyK%Su5=IP&e zb2#hm6o}W4>Dq2Tx7WDq)V~&Q-}!QLFYc~Rzy8TW<;deKlZ96fd%j`4yh5aS(@D-x zj3*v6rN8tJIg=ow#+4+x>gArGas|gja;p}{`b-r(+TS;QlJ)m))<+eIk={NRC#Sq< zxTjZmr(WZtX|zanPy6J))vmIqy$FlkoXo|^B%Ef*#TsD!NS$qLopeW@-> zCB*S;R%op66W9Asf}&cd9r0VmFw<9cx~m}Pq1cl?i|wbT#Vxo{*Js)NLSX0IOBZ_% znmF<-d{;QPgk6Y5Mq&BXOu3qORzh8~RCH3m)rft)!29}IQS3Vb;}c)b=qy>XL{Z)R z`cwy5S=rVZD?E-It9W$qq2Omt*QX_t^S5`do4!K*)b34833omzJq=n_cJ-sgWtNTY zu}KwQD%i^2dmrq4;**l{Y*AK?Nmf|D>)u;W1q9_jOm3FXy!mLY`2EDFqB_omNmrL7 zC{{gM6&2}ed_*?n+cWR@%l{`Nwx9T$y;Q-h`NX1A2W&$;#hpu8c;9$1uJg2A8y`}u z>~H8RpdW7eW71T1!>7%0r`sV9Ia<@oq%a0Yc-F4-Ft@-v6&J>1+OHv}yoNBGD-Ew;Z8pEJ++^iWJEW)uH8qf;k07k}DVRXgc*RsOFso5K3) zo*fGMLtDYI7y~pXQ%qAb^BXz4@G=300cdO{` z>-*K+-7Bjbm!JG1{W4lif429_|4YBm=~^K9$aE9?H`d848?|RfcqPAm@BZ<_=KV=O zUmdoq{rmDtj?K3}Sw1s%3HIN=F1I)@zx6~%z)ZzkjT=;?*Kc@y+h*6Si``W_&c2P~ zpV)irIG44V+GMRizUr6i+9G&-7V2olbGdxV&@Qukn^mh;eau4SKsH0&WXl!$Gn6{E zW)z2d+1=!2W`@KBIbb;c$ngz^RA;tGg?h2G$ zmb+1HHr2D!cK63A_rEJ%&0oEY{cOeCO%rE1X<9!r`6V=S@5ZYe8tX(CaUU!6OkSrD zTwdr=vsZ`R`s2R&j?CF_ujX_q_)Q31u`l&FXH3=MnmL}+^KBIPlJ5O0k>2*FQ0VmY zRj&#XX4-3RtXjD2x3#Us#i>UbdFNGGzpQEwIx^3wSo5z%?$T8b7IM9drmm~7+cLp0 zgIVfAAN$H^AAuvUrtsvSlkf)ai33 zo-T~?>Ry(j|AOz?|HwrFJl#q@*}pD0)_F@Z$9V_)=N|p2#_`BN^6QdL_sfO_H=MRF zKfbB5@D$sXwV`~j#^?+>-mz@~l+;mg^Qu-IuaTOjv~5byufOX_@cOd#7u9 z#I2@0$C?ALxSvgqtWUb5{mbul`8&JTgXX$N?)3jsG;^!8eyUQG9_bRf^;uLHyE(h& z-xtnaG4EY%%SDRX)WYjm?Dbu_N6}fpSz~#qcewH0ri!^!_3h?P>J{YIcg~x4kMmOh zY5qq$JzpCpuDNvQN$Hlo3G-Y{ANS3FxyoVr#qTRW$Jb1ow#MqG{?gW6>*K#qn08R3 zqjb%`C)e_s+;)_p|-=6-$$lUtF3KryZL=T?=z3bb`PkGHUQHQ?dzuwfdp8@d?+-lf zr^Qn@_m_sZQ^~m=h(|+9wnI_&}{zBx)t}yl+)eff}r$qm|r=F^`eX`f#o`U}g zQv!Buo8#OjBDUIC>~xp6kbShMi{{sceG7sGbeC^?e<$~v*QfP+HI3SKPC9CkVd3Y# zS&XHS`)0_jBjqJ^PmFH~RhM!lm9A!1k+W4+7xGk*^`36nbHo0oRu-nx=5~{`_FR;I5>~r_0VJgVt

iMlOjd-~Kxsk7~?&hg?2uctQm*?6gJ30QIAwt?fG8?P7~U(U$2yzY0X$4ARo zIx=rZ-lf&EHulS2V+phf+plRX$Gv6ZMLEyeH#f#}C+(glbjm^X;)*)fD{E74s#NyQ z$&j#5Hr?9rVWQlX#hLwQX4E_hlWb;Ls-SjJXydl^%v&-A`#(KZnlNeA|AcS*Zbuz_ zxM7a&fhLJL5r6h_`(NG7XrrVpC*Qf`+LCD(!@5`8R``~?De8OYixmcEYPR#Q+xM7# z$-Ilx_mv+O?Jc}saC)u8(agt1mV$a#KKQ!U3t z9^2kOE%nnRXN%jYBnuQi+81}sVc&vifytR+S1zAYVE%pO@Y?HUN8OFzyj2&B*W{Al zdoQ?6qG`>784ps|nL4uQ&b3!~y_P4ku<}H1*yS$nNU=4YH(P5mLIqg!{dX|jE>lZB z+ooa}IMuPDf@5FRdi_^dr#(8lVWRH-rg`?6@pl(}otnX2yY;LttBd}^lbhIn2V8U5 zm(ZkeZsE*DOJ!!A=m^l6aZi5lx>@aKt*=-$bV$9*46a(7Zl3A){QW7Zj*r<#?%iZ~ zefSRl1LN5}@-DN^ns3Tl<$u8^Qd*58iS6sgz1LqkBysMpxTdh>?PpOw^;Ms}_N0BV z+)>u?l#ioTM#s+9J_Yze3n8=Kh_jvo@zC3z_BHHy-v(k2!a{^~lk-#YcBeja6;jyZic~ zhVIupC+!#gwD;6O$^9;Crp;U4_HB}7yU_N@o-#+*d@N^}-Nm^iV#!sJ$bC!B%7#>J z>pF5$J(TyI?lR6tGG150oYqQhtjPNEKR@`{lPAhQOT;c27X_NyacwkwxGFDJNIUa> zOJbMt&J$`2n6xf_2)}rLv*+|9)5>IO@0exoINq|fNO2kGqcWkR+HZEuJfZ5dLa9{$ zNbseX7qcg=R!`=0nwhs`h2??_lxbm1b%$~F|XYQt(CvLYz zOg4C?^zF($!vh-~MHjieH@xd8a@@tyXR-LfG_7+yE^=%a=FYLTeA=ezXQQg|{eoLhV*(FMuk4*&x@89#^k6i2IuX1?$A2(#Uxa_@qRpXv_$MataXjuwRWqX^uCqF$ZDsOS*@$|YolaD!? zzHQWLlfQJcVdMJZ%dCQ54okB?u)f=<;q-?uZs}j8!UZXQ(vIg}c*fP8H92;nDeFoU zQ$Cg)bhBc>*sxlGyuUM{*}J(uG_$Ge78 z4jd1Rr<_@)lYQM=ZsGjLIlnA#Wlwl%dq^uyB9?PkbD)b=F7MvO)&l+?#2s0te(k)o zcVoz_%kkw7TmrG1-GUD z_*3?=7O zO)$IInYQ_H3A=lL8M)h}}S9KKq4$JO&$i(oTy`pa6jdwE_S#xOr*sahMq{nuN?Q88739}2+ zF7lq#dZXrh)w=)ZMv11hv_}c=oL2E~o80x~fL!8>d8H11}uN)eCJWoc3nQ;K`B zeX_>4rRqh+QID>22}~3*?m1|3J2J)D`Mt%g*t<)A*p&q^9?-hAUpUk6Rh7@2rDA)< zj()NT`?&XwQu8nO?yHBmX7&8ZJ5bhV!sIEyeR4tX|NO2$vn!0|dmEqZOL-gE@3Kj$ z)~0*bosP+OAF~>totq-1uH~tAfpdGFQpV33)u+X0)ec%Mco3JUvZpb`U%MsK=BeMe zuq&-A)!zQR=NB!fW;lmsE$dgt45yjATf0kUh_7EA{Kf8;%jSK#Abf3a~|&9X>g zTFd03OEqV`r5&3xFZrBwRDb*TgkJ8(Cm-kY8W`%FYArdTw7GAO%-e;wN&TtQukZ2P zIdeg~O)%3zrH(Mc<>}>@&wSLpdr)Mt%-J^eOPwXFS`4daW}H=-sh(*c=4Ss!ZpzmM zN8;m|^k>J|=LqR^+%xR^Hc#yZ-x3K!liNFIyA*b{*3RYH;+c1=$lpxy%EiB`+{qof zJ+m|YH_bV6Bjr%i3s#+ud!LPTcj{f0_23Lq-6-d}$l_-Eu|4O5X7vgc&E0up#x6$f zNz;>m&&>Uy{a0V+lvPJgI^QFw8zqlSTYmdmxpQ`?G{hZPtdu$_$f933YaJk;T)vPf0;<6a&lC?K@ZMEV$tO83@ z+REfqbN4?w`25=9y|=VqB>8I3-`W|nL?SF9ki2j2w4-10Ms9j&at~gd+_MZTsML}|J zV#VTTS59&YSuQ`b?bAY0gN>b6^yd1D2p?H? zg{x%4{I6dly)+|r+UyQBWz3!Y#Lsx1q7oaOe9}o=~l%pEkeF{}z;%YL{MhUnAM0Uc~v~ zx_j+>XCyc`D4bHAXm)@7BIYhR#;6Pb^*4Rm{Nc~M^pD5>U9Yur_{dr~-@3f#!2I-o z2PX3#Q&dtpH0`&PgWXl3?oDUE?rP>#i4b@?*-up9(XNhvQ)X!=DLwvIClt)@v+eJ` z%5ye*cD)z9ZuIMCaom=_|D5C)MxtXg^L!7aB8T%oBiOF!zA`mrLk{&HrZ_!1|F>%(*(j zaZCSx&CcqUhiMFPA6*}%-%4+9&GFlOn%O+X*a?`0_B@^BD7N-SAYP=V}#lgKuE@-7yirm*lJC4e~ z-S#czonz88%d=O5J_l{o`KcZgbu|BX@7krSW~SV2WYN~?s5-eoOE9p^wDZ@2AH2sm z_rK(O82@MDKX$#Z=h=D|{r|RAr>!c{(WIF-h0m}fYti3DmsBmkFL@{Muk}Ot7h{+H z$s1GN%U}5G^@mZ+W%G||G8y|mwcA~uS$XNy{H29j*YnjPUZ%*KJEqB6Ci9B9d=@zU z;x)q_5A{nEcQF^aZ*yX1kM}(5ds((gea)gz=UnbK8vJizz33Bka!QHLQ{8XNuFZVw zyQ-J3e8%@rg1H<#M?80~u%Gn)QMrw_iSXyRi#~xtD-{1K{n~BOTX%d)WOA#)XRiEL zZr3l%It98kJv+X|c=Y7m*>2Zdes1{Wang6==WThN1uhG6ca~Y@&Go-6^*(r0?Y=$*iPswxe%UYj z|1NmTc8i{Am%hH;QP`o(A|Cpxv-0@9jgQMxFBv?OX}MW{p?JnFsdL+YAM2@!5MXXU zd?hIOi$tD_`lY>^4}Z^TQY~B2HQVlLg3YvfF-==+{=U?!)%qS<+Z}vmQ;D$Z<$Q(9 ze10qV1R?~O`PJuUyjxV2>6rTJs&*C6_sKu2^98RTO1*FYY`uFb?@QzGQ0Y}k5gY&f zojU2My~<}+HHSn;9zH*@%4w?g}-q&k*WueJX0?V$Na`{MmQ4{H|HmDJAs-5&YG zWOK)=c8f+Uj|4{^yCau=O;oy;-2B4f*z>kGh6@d6-1t!%v-zpk&WTkAvwPJQ?{Q92Pk({{00; zPT?gR6pR#>i+VR{%(#-mWwtEu$hIwOixz!q2rFFsF>12srnb%TXC)UWIHowXvu$1> zIqm$Bwqr?|zvmYozLvf5$CLg~i?kcN5=9L+eT!Y^SA9|OqEVa3{r~w!Y)OB_#axyN z7&`Cqd14+dxg;jSvgTqI+ppXg-MlNas#bKjdnuEPLL-9DKeudYMF%%;~?!JSGORgAR-k-E?b- zTbs+SZ{OB=>=5FABcqxcANWSx&#qkS-0FgTJp$aG%fIpR9Bw-Chxy^PzcbD-?hbv^ zXFB&<;<=0KKb}~}K5@$2CtoFUPi;MV_^aH}r=jZ$rgD3^ZOO6Fst!12pXivb3G*geB=+O{cT-j5H4wmmFH`ml1oVch%}wVyT4_=B$x?R=+{P zNVRZM6^s7%piAinJHPz>_-4XScAbtM>#)h^AF|nT98+o>RdUN&HuT&Qn9(x%o3;DI zwc>{>c(|1wYcFTyS(xUtOlI4h1ud_S?qW^^9f@#^Yth+x1#vH&{?6ZhY5vpP9>v~C zC#H+(JhWFX=4$)MDduuatF^Yfe(8SqNzFYLXC}P68v0~jw`Ome*vues+Otay@}Vc&EExx;^$#a!G(+cj+6V>YrH?|Qdof4N}8F1d4Z zttXE}oofEWdBDpoB_ZTc(}^1gC1+g}J@;x6gUQ0W3rYJo8l6?R{K897{Po)>K8Fpq z9cVi7!a>C+|C%hzVUc#$iHQ3@7{yv=|r|BuiW)N`mv#O3D+9QW?t2 z%k?tzvWt@w3sUv+i_&Mmvyoz8D3bSdaSW-L^JXvS9$D9!|NehF%R8H^(V>X*gL;l* zOAeDKN8ckSp{YkDW#fyoholStF`|zab|CW|JAC`QL&fg#UH>|vLewE!X%ZD|qf0x`}{;G2IxwX}P>w~x! zy;^zueEr#H>vOhzRGF$W*+JD>t@D-~Lm5-US;hyZTnV3-{CywDup#y8_0HNp?&nGXvEivh1SGUpN`I#yFl=&@kO`xzE~u&*K@Hrx_=;$4R~0nSSq!^L%AJomEOlnZkDFHB4ph zE@Nykx7%FxFz&qFwgUgDD`JH`)R+#4NzdxNwc_uL1G^j}>}RH*st`PocEe`c+^xkC zQBPTGFCOaVwDz0Nk`f-Tcjk@q{bccHuQoPrE!yDkc=dY@(Lj_*%x z6+F|J50ptuUD@U^Q}oiBxk-y0*Pr4p?y_vzDazcN$aOM#&eL_jgjr8cn{ePBSHk;; zjp=>+#(o$zpi`ea+aZCCBp{K>sPdV=NRaVePL|d`APCi$c%R3FAAp^Qs;heKV5&& z?Z&bRDxd#-`SJI&T*aS%`3D$m%8g{|U;8~S|9w{PaeV#JJDKe372+&T1#N8Le6_55 z+bl~fPrU|~Z!fMjO`RIdk-<~{y3fi_@c88(D|I!SGdp?KNG@2@<`_R?XZ4xX1%@8B z%VrpDx~|o*-PYvGgWLaXzC190#{cJg<@*_b&iU^x{zamnv8Bc5js5R@ zxt8jEuHy9ymCDmE=9TV0|7zv0T}~5oXC$9V-5$4#cYfmaywhLIj?Osq>e9bCf0Fbb zZnS2evh&yZx6)P8+}BREJzgLCYxm)G(Vupo=C66|cwOCgao!B;pL3g!tX#x#l#3m_VR7#X;G@x)Be0u`+G!c zy`Ic}Yn#`h4D9ij|KDy7wdZ+fRd=bsOm_e6sn-3spMJZm{$`)pB9Eg?(-w;|r@#4~ z@n3R&ql#t8-LxK!X}gSGOs&0edfk~Hj_<2e(;gcdzc60&`JV-kOXR-eFSC<2?qhHN z_vNQO@86f788*~R-{*P1_WG3adAp~!-e>qr-~aB9d(YX#xBM$e zF0cCFHdEx&n!C{#7Dqq-`goSqm#-ghl&12Z*!sZd&SPCS{`@~P@9WO3zjW1m{oagw zS1QHd?K9_y%|J z`S-RLJ?II(y296tUbi>p=Hl|&pSHi{zjSvdt35lfDYa^yZbCMjc;2?8my*kE zthYQ!kua^gylVY&&QBlBCjBh6ShHk7xYc7e+kH$2?rl0;UuKri9&~)^%$EMtJ)t)$ z`DNG^`TSYy6y5)Q&;OI{diIY`ZO*U#er4x;yNp+NT93GzhsA~;2)kPtb^fydy^oEi ztMmVbGu&u=w0};>E@P6LTMVtlg*ez2aEt-_QBG ziXP0GFK?83@8<6QyDy*J-FAO%c34Vm@PTVQx;JuC|IbukAG!Af3&V|NuU==)dS#To zowrIL^VzJwtTOanD6ne7}AF|2e#lWyjwKn#F!r6}RR@zo^b^ z+qJ0SZt+*^`x~EiM*lB9tbWkFGHg0a?X%m{6xCn64n89-^=0N&>*={40vnee&(o>p z50iWUd3IyAcwFgo)8}=+V&(tec~`sJ@BdB^yU^(*E7cK_!zmrHN|%sGGd_lxm@ z(<8G@w>8|YDZIZ!r82Y5NNZ-)*R3Y8o0w%M=UlkG*=5ef`Bn4ya`Y~DSACPJu=~MZ z+;x&=ukGaTGd}OyB$s}pg+0vv&*{hdzwWfxRXmv~t|T5@ZMv?(c6Zgm6{3IB>o&5- z*|92g?&j8>eCD=C#fOKh_3aiPVz%bn|ahtTZ^-25je?O))^UwVBZM}?Dt)Km+E!(m}7^|QByIfxN3oU*(_P=kSr$CouXM^~-DAJBuI8Ir zcVOlFxit@L1DvmJYOpolk*me9}+-Q(GJ6NAAwOko-N)ZAS2#m5%4X zpE{cSda7-b#}2vZ&2kJDPfl6?|8R{xO!xnVmBszLn=A}Gu5M_U%l19)`(1Cw2X{l` zJw@^j%oJbTX1~4Wo6vcl{N}s!A8gBcawj22c-`@JIx+p+8(HGY-(T1?-}i0(>F8g} z|0OaOF&t-)-^KRb4)a!HW z&i42HJ9HC4{p$A{UhZ{1t!Mpr7I#@?<-1So=d&$W)IA({ zf%VPHgRFb`|JR)h)L(b^ONmE9&DHC|UstK5zfEPg6_tDWdopj;USGpw_1e0BV@$O- zZ)(10cgECh&AvllZ_Jb2^J-^##?Ae!x*ssd-+tzPyyoNF<9FQVYzShFJ`lD4{jTr7 zob`OKEPKEDSJa|gS%=o_wRAkMe`@KQYt3sw`di;^GbA9h>`JMk(UyrGHG5wj` z-|8prAv>FN6Ut+&KRmCRq$0LHp|#!e+$1jvxserVXO`L3|RXy5-S@(vq>Z2caTe&^BM|ETMz`I}GoUb5S`&RHPD8hPN|%=!8>KJ5S7ceX z{bYJ#^&Ezr%A*J5>%O(+s_#qh-`9CwSUhA`lXgOReD#a__Pc*QyLP{_cipe3MRB2@ zo8;10ZjTc=$hEIm#Jq5a0l(a9pTsj~ljj!R|1|mEUA?biiQk?lC2;O}to>Pk*8iiH z?)!?*@9R8ROCmmuyQ}{xvV+{_o%E5* z|6#l3QJLm>TYoKz*txmv`hxBAx1K)p;;Y%w89pex+~GP0S* zTXid057aI{^_YckN6pFJ`hCAQKCQ36b%#IBTjoXVRIv@$wyWQ-zI$-G=GD*lwAL%1 zT4R*<_9~05;f0G=#p`4uHm{qrHC*INjr_;C?K6JJ?KZNPoBey*;^WbGjJN!mzWnU| zl6#rOeCywQ7S`5H`2O$v9R5`oXCHecy5-OL7X{M<&U+f{;jOEm#1qga>u^0wJRkO+O`*F#&yUe!c*_o{_&iSj>G}wZ&*w)47 zKdrt#?&R`xWuF?&QF3})&R$=`v}lPBk57HNVfx~zP1uc(J&$!}-diRyd1i^ocZ-=7 z?=JKId;QShv&bXYy(=d%UHfIXzIy%NXZ7lym%@*}cK>)~>h7;R@iUwDeswbb9BpRi z^C)*omwmLpMt?W!Uits3Hf4$H*N3Kg_7@(j@lEbxtF@3zpK;3GPwU0m>r>KZIDL{xJbBkyd*A=1+4_}F zR z{!A)3wY9Rt@%;CQYybZkET6IUc-4%>tM%(Hv_C1dW1doaPrjY;Y+QNd|K9x)VOi%A zFW=kJV-eu9RAhsC_`L~#Uh==+%aZhQRp%`0Y<=w)j#GZ}RZWlF?Q)@A^w~B?{zYqM z99KT1UYhpo;pxos-|HJ&%X!ySm7KeN?`z%r`)^{r7=OQx7n5b-x)*ya+f`K9enV_O zw=K&DQQ6X%;_a!-r)CL!*>>hGC_y^Dnt1xaEXGqg+wlGMwhdnK0N->n0r2ly{c>F#QNF&#v4nd?U)%srGV?+Lm|Tb zrOSRC-&v(|(fo_q&oldv`)qqJzJBMM8|%;hJ^s0~nfr7#vnn$NSoxOw>dYZp=R z@4Cx&M4wl=z2JCwwX5WH_snOn45v39GiZHcc+O(A?V8%a{-TAuv ze)b(+c0SJOPXgD;Y{uQcZ#nk`UHHsz7q9FUr`X!C-yr@xPkcn=GY9jz)@-(CZ^*sY z+4@V%`o-UhIp;yW$ODN9$IbqpJkaEQZ^K)@-|wz&KOEnA?A)iY1Jmx8|2lV`PtRud zp5rSLQe>HS9&Z)=l5o)As_GfdXEI+>W_)h8Q|Z5K&A;(6$fVCn`@Er3 z`Rt^W^YuG_ecD`C_hq@?k@x#&>=D_poL&5jUD6^_==WT^3h7o(j6JN~llH25y^W#{VkSUM(k*Vx>>g@c#pKe`L_nvQlYW@et79FIFc@8p?V zZzGdZ?6#*S$F;uM=yRd{`9|&H?>EkTscX0^-!A&0;!o<|`aKKT6OMoPpZnM5;uWt& zpcYiq+PJ#ZFYgr_&#Ii44qf-7@sK>jgA;Gqm5(W$o>F_^wz0#^PiMZEfpTE_&!fuM zlhx&O)fpQ$ZL*zKqkdrV@#Np^;dP(>20tu$Y>_t&$$M+uc{IF{KaK{}0+^;0{3IvQ53+4U>`U9@+5Sk1@% z)AWA*wD+0!lcDf&PN(RG-|MTdTTha+2zpf$_;0HHjM!$o4~^>zu6J^#MxncQYM z7fJgwnrZT1J|&*}zjwWVf9l`mMvK;_O!gMnw<+Jg(OPE1_ZH5dXNy&W!2OIjD-XIp zfAZzPiUq2ci>LE7ezD}{E&1YHoqXZC`U}~xv^Q77D-+LgRI#l-mTb$uW6j~?=JESV z4i;AXTfB*Rq!uZpnV|jqd`_+O^K(MayR_Wznl9$gU%@_m*M zbDkW@5!FjrEgxG`@j9t%p8BesL}txqZ#?XZu|1HNLIxUoZRhllyeOwLi|Ed|X~Ja|dToE7y%#3t87!mSr5g zesX?dNP*TbGojLRHP4J$IvUsQaJ+xVSH?_k_rBgMmGv@g*L>1g8-F?){&}ACet(j& zgWTZ{H%d!6f4x@SaC~XEc*;Cf8S|0a=M-q3~lXdb=ct;Kfm!_ zW&MsnyDf?vSncUM; ze9rXk#NXxq5u2+HroWUg3CVbMYJvM@CAL}(x#J6_e*$$M7MApH|MRCby!PQLWxH>g z#loBCaNkJYKIh|kgRC#h)*W|mT~feab!V-*?3ag?Ifrk^Ew0xKe7#_M!Vl9#oxd@G z{hDdtD&BrLEpPorJw4{zYyLS!(nWQ;Mm-#Y8dN4?{2q3Sm!WhEpdjNe$D zmUuB^mfV-1#60OQ3WvU!X(%U1IO@MQeA~X}_%oRW_wSwm7h`6V_vw=M?6~TavfbaT zlHSkMp7_iyQhbAGdHv??+n*nu&v@Y2w&QNCOD8nlo2P2C?8otq+ft*AY7NrYH$Hd1 zdGwC^h1`QU;M&BKByNwphNB{ZrFPhIj>HW-f0rfK{b~V`ky}{eg zc%)bQ>lB|251P9U-(g~kfBW$K&$MM`+t%pLU#PgV=}3I|wdCUe5A#m5<$ev_dVC%0 z%C!Zy%VzFAuD@@R%+{2zi~rl@-YLtT*zKMgdH_6T_PLpRe@?Mah{Pdwolu2Ol684q z7q7P%I-KK-Y0H~?y?-(fqh0rGi>~#03_HcDScQ`*|gQwYXm)1p}(!P9SzI*18TA#$1179O%Mua-@EBWLH_q4wK zcxj=#U);8wo2kO;ek$)?ghq;P5Y5i}t7FQu=upT_NB%`V`^AmDw;Gx}V6VNwyf@&2 zbmR}m_1iU%eaJo(@oVP?P=8HscJYBqVfW?B?teM+YP)Ys=l^DzX@a{O*1oYXPGj0& zzr8j@;*%g#F00I!Q@Sj>^0v#(HZ^_mGw$TOYm3#JZ_fAbkh^WXu3!B7{a-iv{pKo0 z9|+_5b31*K%DVlp)PG9OEx8bEyvwgu@XMdZdFM~@S9M12_P((D+^w6A>(AskOACtm zT?l`=ac}=NIrYzauj0S7DK9^_hy7>b(j!_4+U)0USa?oyw#Z$4D6lxK#7ukF5#z(R zAAEm4KQZ^!%Z(+|O;Dr?^_b{J-Sty^}yK zz15w^_a1KlcfZ+(G5TWtvyUI|9=77*{yjX74#i{yv?O{@5Aa#}^BI?e}gA zt4+K0gY$f|V+vz++E@ANYir^vc&@5x*<8(C@Z8vKhW1T?2{X3tX4(6C&EDNj^Y;H* zG$X%%X?U6tk4c-RLcjU`f|rZF*H{0!|L??=Svm>l`R@JL_CNaMTaT@sT!D9O7Ek5` z4Me5CwfOwDFw~L%*77`tsv0?F?lw)8{@9tjk3ajT{PXD+?{HpOR)##*oST!3`ChzT zSl1D_kW=1*^AJPojgNPQzWgW_;>kb!{nS*(Q$OTB|6MWt__{MklRtknS$wWjlc#^9 z8LREQ+6VmgJC1HWF8`m0?IhE++mrW9eH8ex>+-^wZolYF3lr^ESg|ctk|>>X?bz{a zuXpIqE}wHgDNp)YklgayA0#;p_WByC8NV#}vcJ4y^21<;2cK_fOK{&fJ3Y^GH+xl@ zV7R#Gj18*0|CKdpd+UGQ$#dh(7cu8=|7Mw7Sh-+(;wQP!k%j$JC%$|h_g@AyEOC4D z1>3U3wEoC{_3xS*Z~uSn9Q^HM%}v#)1M_5eWtJ?e(qn0m>v3;gyN>JE8S${Pd7Iw| zJmCMbBl`T33(fl~W(1$P3Tmh(a-DqqRjTy*s^j}FM$_Di!)4)b= zwKFScJ~*-ebn36aQ;&ig(c4};Xukh%&L_FuM(cNf4f$qyxiALS-k$egNN%^mw&N_( zHeYV^EH380@v3#LecU~ldOIzjpAoN4^OSUXvhC&dc9FQoR{#3wnMZ1TlXIHwV)|oe z+;pG0UC=RkM$^ZL-4p&DJRWtwYtpZlUp|b{SL?6EeEJjUc*^dKrkEec0>}QkjC`J| zUvnN-o%s|fs8cFqe@0sPi`z7N$uEyL{=9Rvt8&wMR`2|~znmwh+ZYy^&tx*2v!e1u ztUK$9RcR$Rvo-1$oNjA)Z@~X6KZnckg2b|2EtW^`*e*LXNB)aqC8S|K{j`yK)N!|i zw*uLj4x}odU&eK#YiW8OKf`XN>jI)1PVqH*rN6aYt>4vZa>20F{epGNPNf-T=O)-} zoBQGF(Z_-hlkc9n)BSVxn?tJJd_OL1+53@+&Fie&Iy)@~)68r0@;&4sHob#`qZPfOT_pXpre)CmaFY;3Lmo1s=8{!U3+N$sG z7N@J_@sastUBFECjpr8}pKOBM$E2hn}56Q=Z6`m9g=6vX^xv28I{Vqcgyi-`$RV#|H%_@t+L)N?TyZQx$o?( zzvUN;ZUVGHFEyTgUx{Lgj$ zv0$j^ms2O0K1QT>CCojpdh*M}`g{%0w1S`GEjQLBe>-o29KPLC*n$+ITjlUIeV4iH0#mqhZD-uHPt=W4A zG{uuMEzCCkZCm5Me>)4B=3Ej6eoD*<($rmCv#4-O-YPLSg=*f!syDadFP|5ceth_C)m6QO^XvC11~Le*ywSNT%t$eO%Zq^LPxwR1W@sOO zd}1oYzd8BKwjX?=Z?Yt!Zo`i1t^WcAdkW7@uqjK;ZGCO{eAb-5=^L&+p1WG^_RkwD zFW=tJT%Gk{+FYTA>w@Ltp*v%yzxn24X_#QVAz{WVvkQi8e{Cvt?zWvjQ1Nb+?KGY$ zfmEZ_E@$|=e%?P4_<#GK-{Jggf6spUHD6I}GSAyZOab9+dyV)l;sU?Pe)-cV*F5j| zt<~!%H0`sK`&`Tr+&%9c;_g{tkgL?gM~8^8P&{ZtWqHY)r;*q#4dJ2`V&O!`+vDe{###GRif$Q&Cg z%%sR_8(}PvSS1~OZQAq5YQ5F&7iOz9-~7#eIyvjmbbHA+JHGcF!Oe0_?< zz3m2UJALvepD(I8vu59i#|=rGU)KJYo$+chf6uC*IezZ4*Qezgco?3(5ctyUTl2-O z4ol9r&b`fUymewqF6)zx5ASUId-ZN|@&ARA|7INb$*nQo+Z*|?%110`>%s=r+k)W& zp)-Hvi|)|>dNyDAh_B49Q`}W&_`}7%Lt9>V6@rT;?o)&fGf9RRsy~upM%zS!(-^ca_WB<3IG> zV#lqTI^NoJ?^1Fe-<7k;3vL+X+_hdaNp_OTv<(4IZgE~Z|1-sNrOvx+j}1?x zKCF84ZYs~=-(truah*?=zQ)imd}EI}w%sL;e*m zd#oB6o!Sa+Uh*DLD1W_D$5{6L?3cM`xUMJvRx&8k3-6bm@hYeJZntdkf^9EaU3{0+ zZwO31`8BuUOSJGdp51M~SoR8~DCRy|aqIDirnpn}n~#fh&$ft(Xku{e7wvh;>S3n7 zPJNY8tMvuf=D0#OD+$RCqXgrbd^CbT#9E9t^-&Z?CLp!vhZs@X~4$ri64%F{ij{oPsdAwu-) zjU$tKqxUmc%hm>j%q)_9@$z|=(S^`Dj*{En@opL4Z$j`-I0k+Nz{8e*QBirg;u|2lvzJBz*|0Ga*_jvRAAejN&6*?p=+t7Z$tlYxN`<`SJDNB*_fh+bSCe>oJ^OQ;=Dk(^HC40cvBcyC z0oyWNkC)F6eZDbuXT==jYt_-LIU+8hYBiSmb*mNLUDG}{#njtn#?9la_9<+1>F12d zDHU9Iye8~UaxveDKUNO;hm%enITAL(cCX9p6blV=twiFp;RVCiCmU^7yBn_8dU3D`w8#ZiJn#H}geh#|BZq!R`Ble7 zHh&8g!iu^UF^?2bk?ch7M$M(ES+1L=OrnTX- zvw!=qoNd8kdv5lJqfB9ok}lldV3FPLxIRAkr(?W$Kgi1rmg+k;oasIx^7+fl%{pQm zPN^DS_{(Bje=RLVYt8x zJ7VRbX1n>IB*xkE&JmP>?@W!S4cn{y_JCXcjXYP!jZySEFcEXk>T`+@bhnsL7li zmQ(uh-Nv2&Pvkt}c>ld_W5d)1MplepcO9Ma*dg2qlpVXvIg$_Oun-FzHGCg5NW@er7j5`pRhW;gIQ()xTz);;;JrN?WI2 zoBLNxn9Z5kh&x9FU01E!Tz325oTS$9c#+jwFJjZ*UU6W5vN5MWwaE@VdcxoT6f`8c zVdg!y>?7}`jpU9qJ&UzVe=9QirNTCych^?OU$|{7FE=}WyW#GyGuEsvuq{h{CpSA% zCqYitc*P2X<1^nnY)>uRKF_lz>`L;RJE=eVtCn)O$)=x#j>OLKT=27^95NDn?rONC z-1ChQ2ljB)cOUo@=Ez+3TC*wecxuG++DDsh_zvHCV*KZh*_rm^F>F$YL#7*m+KSKq zUA}U+YS$_0$40ANYStWQdghzj-mrhcc8=oI`WO#p+2C@Pz1}m{*N6Ok&T}&P`pvrM ze;=;8_E>Oc)4d~q{^VW1vweweAM2UfuWYl=^H)i-En0KK;k(qA6FZ&1fs%#vGZF4g zVfUqS+H+nWpRM}3P$xkyWV^v~3F|Xc|2iz6@gCat+aY&3=kNJ&#q+EpUldM(hWxhi z1YHl>c${@+&+#-d?iu1I&)w3z))HC$xLK~&PwB-^X4{nh zE4KL>P7A9{%sVfn9##@<_RL-K_NLXmja8K~Tk>yI=cHBHpNMmwA-8+emtsqu;=-pt z^FL{c8Tg-iZxVE-%z)`s&S9IQBF`;AL)|{P25l=N3_J{PU+~;s_3)2Tb#oxbge zxX*F@vEa>b;VV5IZfSQt1g)A-6Ekr92x{j^N_H4*NO)E)9~j#t_j~5?^VgW0uS-iX zy?*qu!@yo6?EBrSG&Y;IKX=8|x)_UeEgt`t;GV(0vu$S%#}DZjD-W(XCGp}wN@Kf2 zug$WV?+w-$UU+V>w>NUrKkurX!zngqn6gDKE6LfbP_(jnzkUmT=!Hx$B&s?rwe?U5PG-i-aYrO zpL|u){kE*Nt#Z#<_jWFw&wc*!o}&&r2}!HA2~Uyz^0Hb(A2d7@QJnVHuE|bVj?3D- zOh$pnWd?XU+Gc+1>%zDTvuB+A&zozsT7PE$?#Z7l#0(fO-Yz;g>E&~??JJ`%+}?b| z*n9iO&0nRGGh1I2?)xvoeLcB)eOHxFYh~SqW{t^TZnXXUY$3LxX_1rq{#p*%uC!x} z(|>wMo^yCA-f_?SEPJoD5Sm%?`_dp0Tnwf7y0J?U?@9?wz~A z>0`p1>AyG&W#-SE{^q0d^d;e)GvrM^KQx^F(KhYpdX8nbUZ8+{2oA=wC?-?xWZo6H4Ue@K0j!R6Vy4eV7ugtgp^-z+eMxkzF2zbj@b*xmY2C*hJUIIr`74% z9&`RMbGj{j-Yllj8cQJnmpiGx`XRH}j7V3m&f zNqR@;SU=Q0{5ax(%+dEO3MV#z`q9hhufD$KuKR`C{9iuUp8G!EN-w5Aw)x)Cx^%Q&(>cqyd$Fl3KpHKQyu%)?RzEpEUxKZ)dKbx z8|!?#60Q`ke17P2gw+eperK*8YI&+If9&O|Vk&dLaQ*z_Jqwoa z^|BY7bR5)tVfrNa?5GuoT>|&UGvUjg$hY5bu5a7AF+H>O`Nne<%(1)jL$*fyA4`^8 z4{1=nId$gxqesa)31LNVO{!*Zd&AzaQ~1c;s+(rpKn3_Q$pvR_CLgu-+uy#c;?3e0 zEe|$oM?c^AR6MMxcmYp;GGp>8?eflL$2uvCf3N`mAD6PvxA3oGVN1rpFMN1 zwcVC(XVb^pqj=qKLfyJF=Gd8$ch;&OJ@x&*%$}2N`fJW?dgSO|Dbu{qrN8$~+N02U zAio$)yJO{e=g7fihYbo{J3l~+*Jt);3ZLD6T*Yo1VQ}Wn1e1?x!aqUiG5DmT(*3HyU3*Dem zQ72KP>yf*&jAt+Jw4H$)etnW89ZT6^_s00!{cuh#yoqpu_)z= zrD5w6!=p~KHrxac%T(%!83aYJcg|mXJlWNG=j4a2a_OBMkE?usE56=1^W}5D&3>&f z3@tWYu-*230~Yg%GKeup-6EW8(sSyX?g?ZuFe{?C^dMJ0El4GwT;i)8F1%@O=HgQy*?j zTPnKlxXEYPFT46hn3b8c*_3?plan?!WF9&!XdcycasBKxU;e6|pEs*~4LxkV3^t@S zY*J&fJ$I%qz~@-KL*!kXG{fY@MmEc4%>FM>&z?3dV)@BbBiG~q=Eyb&3HDg|S-i+) z+xY2#!PK`K-z;f4WXFA0U^7>`gD3`~5(o!l@ee+DhYH6Kwk&-z&c;m527g zc|QDhs{l8ojx_I^<(DA)j4S<*q^v^!U7;^urCvzageX{K=SN)FT%-`wUz#?L$L#gv zcS(%V%eQFGJ%7{q@0RmHuu{pP=qv!aKm!HA24wt{O@=QPDFg@ngW4Fggm8Y-# z_2$W;khlw{*C?deKHEG+p6iTG>D;H)F}W9hf4(ubbxY^stp3kM3s!SWEa$kAx}f|X zb9r{36W7(|~)~BCe#Z@IQ7o7M`4B9NaA^c~b;Jo9NYgTjL=y2kx`W-2K z)aq8sJ|k7O+9+P0s;93CIUGTgb;+l1aa~WAR(ouyUT(m~!JK|=E9;s2J?rk(Co4;X z0$6ewy*vCY#|%HMl%5e|t9LBL_T4QeGo|^)ySAO;{uMLr z=cy^@%xc8SU+>&7$!6P!z_z8gkKC=YVcV$_)Bkp_)+Euy_)W=d*K@ba9TfR* zu_4V!n`5*5*MChen*D2?6~3nIkh=_OJ%=&(m$sQK>S9>3ZmAY%ZdhVhxv+a|+S_f8 z@ihyA*IY~V+W<~=`@fRuda~z)d9Xg82N4sqYj2E`C@Zzm3_Jt@fh8gikXJFNiL=mGa(Jr{A0RmyoTV z{)?~w4yVs>`ee~x8p-(hf52|l7jGjU&0Ko>NbXIfa2I`M%Jb_?cVS<4+ws`o1FL4p zrp)w7Oysxtq8xKu;4f4r*v+C2uTsSWYDy*@=C%n9q3Xz{JewA!*a5{an#YuK~(>I)__C3$TQKgX=qPxzL8F}rm+CykHMetw6qA)@`p)_jkx8q|Jc ztCigrS3DEEx-X*b!A9-kj}oUF{Y>{XJXBn;Ci2mdM|pd5K!d-v7p6bmctyfospX`{k{G5?ua_|30%cpKi?^^roHVc zKDL1W{9~TrzT=APvxT1-zVQAMk$&P;x7E4!<1ep2HTH3}%}Ze{=B;`u8M}NB``zZ*2-Qat00g;{+0W3C7f<6W1U;tH)A zd9NPvT$9bOaM4W>2};w%#Xj<+rr9@_7w)+m9z1 zt?o{+y*3jx34BbWKUQ+t1er-jG$z$8y!wkFI?G;k6yFYn#m^=#L;lf;O7U> zR-}Yw60h2oBnsb6u$jjDtE%m1#~byWDaUKV^!v|d1u$ycpV15}Q$HXQWO#h$@dfe! z|B}UDf6n~!?Sx0wt*gJW=A_*0)V1H3 zT6t&f@ni>sH_x3myR;PAPD`6|d>xPQNsfrP8NoVLK870-ZU`$@zB%>A_FVW|?iuSQ zq?O!jiaXjLqjl~O|F67{I|{(dN%TS6Ca&fRvxjfhOZYnH;??Tyaveth*7PQQQ*_`h zN!WbJCX8iqpZDMJ9dgAp)*OGxVmo7o(u|K537fY#eVMWRme_`Myj88n_DAoOUzl#6 zy_~J;xov?2Xqr0S=8R^Tc>iqiFEY;#Cl}w>`Zc%JY*j9!clUkHKWA?Jbd1k%Yp=`5 z7jE7+tMQ%s8E&?{N)lE2+jv|ez{7R&a@H)in@a6e`eP%bW%_@vDr7v&ZVW0)r`%+- z?J$}tupzZ>Tion;^OXKnHshnKK1y(`*Larxdh5*I6&l*sXFeM%#q@W7<2{ueeRWgA z)Vm8~xF`L!>}*_RFfH$~)R%8O0n2O*d=x8l&aW_d=hq(_&N;*B(~R?0w0)o*p?cGcn8WLitK)uK}d#aEh(j_qDs;UYk6V zrRCwq?yT?wU1dzK=KNf&E#Dma|!HnE76SL6L15kIT%r4%4}+>ShG5(Svsu z815&}0!^E~vE3HG7rnU+R(fY&87+PdYa(~^mvn8c4PjC41GR~Dz=Lz| z7hJE^5&IHa`R3HEMKxQ(*K$ShRLu=FzR-MUZTiUrvl=No3di5S$dCZ1nW{M`8Z zgw1aQl+_=KtDH;NqI+Xqe7C~WsVv4@4@#)Lm*WOE?Q!5({b^t2Ey`R(~qlPI+$-%i=!c zd-LW0e?F`p)1Ue5aI)U&{^RRF<8w7%SxYq&><-!dSdhMUpT;xm7od>7di1fx7rUmo z?;F=FU>97rU2*2dz#Ve84NkdpUR~61wRFMpyH%-dwR_pBj=8%UJT8nC`EtS(Gz68b zbtboIpN8@GW&<0&MGv@V%$Jn4Fx-&#Yp=n~gKs6i)Uj86lRE_3C9u?H8}F|zM{L}r zc2>=KZ(kboQ_frS1=F+jw@$O|tyNc;@u-~Xb3?D=`DD?`H;LEuWWQWlv-isa{Uc1g zRiRhv;{un>Sbcomnf4~VgqM9y(z&NCZg1M?_imrA_>DDj%y;))cMT=VISNO|A{x4sO*U9bXD-lQmRoOq+#L2Y_eR(D$AamSu zwSIVi_r^~))50dOuF-Ce6Md~vDBRAy+Tw-i!4;3M{hw`pc-`N;y-q#y&m1;<=(0So z9qv~7=2VTw38rV8g^w&SH@w}bC-aPTW@W&!yHz$~br-xVbflksJe^tDlNIc9P}HDL z@~hN~Z^l(~ROd`P5+Fe*q9ZP{tGRr7Fi z@$64F)4~f_b6l>T(a3jqmC88izO`JOt-WADxY=9>r5D}& zU*7zk8h+-h+0ls5qj%0ehP1Go@vdj@uRX5Tk^b_qBIfIwyY&}-f4?#B=a(BbdGqCdhdHpWnFrd~GF|S)&H3KO zX(bD*bB>05D_wfX=I^dd&?2-#$bz1N3+Z9;A5Ukhin=hqK9zfuD?Rh3QH{axzQpEM07oJr(Vx|!`=|h z@dGqy@JDd>{!O-Jyi2|m+u1IYxL=<)!~3`YSLQeWR(+k3G_UqdP0kei{-wX!%GVSZ z9ayfVpP=le-?)*l>AX;Gq`+(ok%K0!78fp`zsa_@9vu2_URT{cvT(y=21~vhpcS4c z*XSMkykph<-!K2Rp9k$8nEx|;DXUJs!SMzB$}CzXdB}Y& z|EcQ_yb}BJZSI1{r5D%xti0dD7d*LX--P25^~Z`3@YJHjd`y9nrvb`lY5RoUGatKnf;m6rnvmz4#|J-xUJSrKHQLSwWab;&ff5) zKNoVi$*^D3sfX;QasIX=;=*$SSA)eVHlPtN!?1mi)cX34TP&VDYir<|TangB|5UuO zesP+=ILipKtN2X$h20fr(n~+ikb0fE`U+E++l6+M#m4`6EY75Jh)BQqY4?lw*9054 znO@ru_PyR?&&M1XrgKJ9P4r9GS%%6r3g8l|him(c$7`gfh8^f)t^L6)oBVoRu;+eT zWx+3@yd^UwZ96t?V&8Q>a(B&zX6Q!Yb;o~2Jm0jdmb-H5stRFuC!6D%0?!_&ZcAl` zuJ8j_!=Ne6=3@$9Poye8`u~m_ES7#pX2Pc%bC|!FCo`Oza8+DwzT9p-woB)CZh94x z#Aw{!@I^f-n1^M?y<;bMcx%?^y*++hNV^(rY2IP{^hm>5hh*?Zi#b>2uFZR$yy+pg_T@7* z?7BDegc@)4o+V=OwErtlzil){(&Z0xv*8oULYZLG<8+$4i>xHcBe= zCsxEvUvU1%LJl|C>mM~ht=*%(GUracSEx&SE93}WsL#Y%<^8OFdt9j8=^yLCvoR|T z_%&Z#{w^@>c#YcQLXA&R2R6vd6m{yYkPG6S%y?!j;iXkZ(T_T-&`{` za)`X(INvgGD`<4fH?V(oSgv{D4l$KK7N<1qf>*X3ZMxT1x=phgG+C7mAA%{?1u_{MkO$t)0pyl`)MUtsU!T9KTzY@WREHqqvLp??mse za+wHYfyB(#*Bj5a=!2BAHr>nm8anUzx;47t|5s0+&J90DH&z$cf;$(+ z_naf-esla1uz6;@GO5p4i+$5~xkbklJ@zydZhMntFLQ^d#$flRP1cUTqEgaIzMXl~ z4q7-1-Uk%lA-6j&AG{deAhQ4G8l!$|HQgVD+x`XXxbz>MoWU4;V!!2Fz4tLDli!$_ zty$cANV1OqC+qCAZIc-sB(H0Sr)7bLrw$~G9foWnpSoA~_LXDipuhyR55wE+uQ{6D zKf<`uEOVNGPVJncq}TGGhQ$fdmcOqSfzODLv}ZA}*LVh+vKP5`WFbde+=q%nd($=xxpvKsOlh)A9L#}jvG+R;aTIsmWZ(O~QEtD&#;pF8 z2HQES_8sSQoWHYu7h7#pLxJtJnUzKv?+Z_?I;zIk6)N?m>oVto&PG$v)EsEua4*~5 zQ~UDn99gIx)Bn>^|0!e@d-YYhcI}BwYQ~G1)!DA`?EdzPrIv%)Ht%#<;=^j6jq7-; zp1v#G_y0;^qR8h9PjiLad9zoiZ7w^!Jnsizm6pw=@8R3nOR;-?uYo-R%zFK2g^!HVtO?uxZDw9zSC^e%v5*M?>OVhWU&l$yZ+A+_25*eZaTt-uxlAnHV)z zcOPHJx^nF&n`P&6@5k3xygBv3{f*qIZ#TM9=W$$5zMkawGlKo7uZ*6LY5DefITymg zJ2HB{O#CtJt&yF})|~#>aB6yf-Ot#FFnmg&;ZVgoFIGlYr#ikra)`f*vp*Kp2w(HANH1ZMtjfD<#v4~P?Yk4XJNtrn zyTiBVN?U&|f^OQ)miZ#9cI9aDoTuq5Q+Z1#O!t+M0SzyCas8Teb-FgU{)?ZRw-wlK zOJ68nd~-qh{H?*09eorldyKCy(0{e@=bg~X7;{mLgdH~UpN4sDRoYG60=Tjx8Yy8Q%RsTW^tf7(k;hB_yUd>f>wV#NxsC$ z^7*~t@&8pPb~XIuXxw&cmTX^4H|ynX0m;vgMcg^^@Q~K086PgTJ^ZWidspwxwa<^J zG{;>?R)tp5rPB{{`P3~q4sLnJO*dG7FwfT2;E}~%UpsB9-^W5gE67i`RZ7V33@J0* zo<8IF*U6!b+2?xBB^hWpO|{7`=dF6#cyA>*jH+dlE;JtlHLlJHJyANX@$6ag^tXkv zPk&e&ta#hwS9MLUd7qu*`EM4cnT7MWnj5_+o-x1h4f5Vgg+s42PcyZZ3v?ys&JuZM z;ob54k71v@oy7geHEbHpO|9S|#T$3jmfWpUIO4_m>rA&tRnB?PX(**)yBmJS#BO=2 zZ1SOaGdshtshU4tKe8A9?9jjatIwL0{!)3-gB{nm^8UK@{+jlY6gAm%a+@U=_!J#d zd%973^=_T8gb4P9VN5k*=7l>9W=bl&6FvGv?s436L(TreWj#7;1#I=izSP+)lenIo z3F@tfmN967n~#>&V*caF%LM||p2e&P2= z&@ipl`^Li#Emwngvaacoocr_Zi)^i99ej&YtUC4^B}cXo5!wPf(#;Z(=`Hy%cE z%hz|Fzhk>C^{aR?>$Ca=#~0|^T`;_=dPeivyN$0M*dOm){Vl@ih2s>_&lN7;9QB2t zf!aVkhm)hPiEQP%ahkzEs`&?l2aDta9_x8YU%n>6_9=*PeEw*3dlO@7Xk`!Imn)MO zR_B}tO}h3vLbnM=B=(t{;{R1(`|LSGDgV~Xx55~IuU^^Ed*V1xgR%95#A(J3OShl2 zPrsOyXMMp?YMbLGtNFZP2p3Z)+;$%suqCbKTg?u)*?N=Ys7? zog9a6J=y$ar}qCQ7tq>?jf)octc;V3Hk@j^z4!P!aND|P^R2_T#J}FC$y;8y=F~@v zTHhMBCY~E>rd4eWZ>%zN%v+;f{#tX+Qqd~Vx^@xm>IKQ41iyeL?Dn42ez<^N;mpdO z7kk+Gyv;wnH*7yyHRrt4vguXA9`i2G*R^K()4J1@L&NX6r!i;*KwtKYlH6a&kF-AeC~xoA^O~R)OroG>2B96>vv=c ze9=1nhrPDe=H0Ue)4ziDA39!pyiCLM!Ou9>lU-l>RUQBJ&elA=A^QOPW)^1du-pr$ zc}l9E9e!LBW)508W&_?tl$YaD1Dg6P+{n3~|G%`_(s?g^g|<4yH#QWOEs)Kx*phn1 z^5Sc;#BH*t<`s4o*)dP?WUG~wE1r3e4b*!<-R8-1@q|&n{50E*N6zcMF05OaKW&of zUay19zjVWdzx>cyuW|U+6044f+QCKrrxQ<|aA;{fW?-uRg1c&sajISU3AyLS4v~-n z%9)}qd&5D)zT*A4>W#eti{`zNWnKJEzU|?m=9x)p#^8R}`ZBk4>1SW>9Z?ix@^D|# zef(wiCeAAP#JI%^&i}a^&L$lh_4H@{Yr}R^KhIAt@4r>g+3;s#_5R(2Uaa}m(`i` z`tH|G4-ZdxeYGm7X0O$ha~z*1^6Ol?9l$y}Eu-ekG zKrJu3qO#X7drmdJ+1vi2u-s$q>d@R=jfC0#^*{L}8m5ObE&kiG(}&|lZjR)VbxSQl z&FOaunjPR{vRa=P&UDP@`z+AB@5TFl23Jo%&b#zK&${g5iT&mx8w_jb*E2q__GU=9 zA3obm;YFwijbwPSh{qzVYK`xyFxU+zcnv0=_R$zZ_@w;w`(?(K~EapvL5@r-eLKEsDJw{Xbn5 z{v_S&y;c6_+Dg%|RSmh8b^oU`K5%20alLNYyv^UfEYF+r(cJ7R%iep-BsR;wXgqsM z^kC2NpFG7g7hJzBl3dfD+I+8k#_zii9=_VBefY{**VVzszqif0|MULds^;sxvunR@ zUj5%fAJl%I#lCmq$Gu&`eLc=vZ4m7zSCdK zBs8io2~IkGSG%!mJ_s_5UGmSYlz2V2EIcc4;!4}^7r+50qp95{bDwOCj z*z0SzS@uPcTyW3B{Y^dnQ~p;t?5JMTQ9LU$N>S`{lH>dI9`A}bhhl8YQfFvfQ?07G z)}ot#ef|;d8{6`K9S!c+|Ha18Gl{qOHh6|b^Vlk`s?$mD3PDpVYNEVV_vNlzzi^+o zqWieT>GK??9_NKlIPDE%I17}Day}Z^EjH}L*!gm=`U?p#mnARvDwyN zyJqURp2JOc`@j2khvy{OrMVotckd=yE@0N{Hk2}X1^ImFPGo=yzGAC!6SS9 z{$1L%`E>J81tUZ28uy5{vQwrnjmyOaQ4zC|Cbu0gOV0U%&w%m&g z(<;9`*NFmcX_#{SrCAM|g!6(m$3-U3ENOYSk-PZojH4lcZMLOPIevF#;?-i?qbHfR zJ&v0%_viDY{J&yC79~%1W;M-An7Q7^Q2%+Z#J{qJpR)|a%3se+k^YieE%W02-x;7? z=_>t&$0FY*zU{8cu`3UiE8lEqSUUZ)rk~h`X=Ss1t^6z}_v6~aP?o*@jrUece0eHb zyip+F=HuiGm(TPc-`BkFaYf44r>`LA6*0FrmS1BHDtPUAx%~a|Sx>|^=>AUo_iiC$ z1Si97cJm9O2aS#&jjXN&9eHHN^6BuLq`I`Xe-`kce4Nl^XMC)?a*mzu{LrnPi)WiI z&etyb$~uS1EbY#}?BhN6-@E(s-~IHzY1UTlIiTIWt%i#~%YM0}xZ&R%t>3#st9@E` z=3Z#l$j-WO{djU6U&11rcU-pRkG6PDe`THo*3LhheMBoE`pemUveW9SBVV6C z{6|zaYg&4--))2CVFCdiq5GoGPXd+1f$I&fo(3P^>vI3X?I*_nTTlbKl@zo z_T#*3`yOxG*>t%t^uV#c`8&%_e>C3z_sWGu`OA4%XKbI>HQVAt@uSUq7Wo`X7N7Ph zu<>Js^#9B2XQ#o3YWJ-;{;b%#dQMWE4LF@wKQItTysP!~M(5@7y;Ckv3Ox{YEA!Oy zbN^3o{aZ)9z9{F5!I;v3c;d;k5;w8wEa&sScZJtrvh!eaSbrzKz1{X4cS0<DukKPpj)A z|IYPiRFQUE;K;w|OgU&2=GPowi5GLfw`;T6o_qDUuub#Je74rt8|N|CT-zJ8UPZhn z@7lfkhugMmB?zn6XFX;9d;gc=fffAQ=TCge@+Pu6?dwx3q=SymH|C{z?X0@P>e#;p zGzyp76n8AyeA+(t`Hy#Gf4y{k`TMeIXBop-d9KwSc)wh~BIALz{IRnK*sIi;v)MF? zWnbKQebacRqqIOoAj$6KF3a-)2E zqsHUk{XZQI%lps%RnL3$f-CmR%9WG#w%eZA)v$HT^Lg^|zb|z~`$<AfcpV_Ov%~9kp=@#&?m0WjRc@uwCc>ih8A*l|=O9JaIw1bY!`5ZajYo)cn zZNjJaZ?fj+cCE7(1r-@zU&izPO?$X@!U3ND&|Tl+UlPt*z2^_-shV^3c=ERv{?HwA zx7lk0`|n2ll1s6Dm-cq+-D&X_RgaD&PEVf8ePf2<&)W5RdDol;0gTz1+xOc2ylby(`!#mt9u z70xQ0)~Wk`RI&#&`!KD;xV-;tj- zc=_Ic_mtE%Vh$Y3-~4|O`-3;G3+lceIk6ied+a$M{Ce0t^A>oG z=Zs?CP2j`${zbHaj$6r``rNQI@Ap3`?ptpPZ`W;qVaX-F;kxgQ=(ko&zwX|!IeI_$ z>&mG&*!FH)6UO4qzL}T#@y?$<_1y>0I80$Y1&WfJ%yYS_n1dt5%3q7d$1;4Xvd})5 zJh!x>PyYGgvn}~=`G4oPqtpEw zf}9!FNGd#M2@G@Om?HG0Mt=3T9TOeze|(|;BKOdpGnc^AHqY)q*1J|bf0oftl}_PK zkqy(n)qeiD{GQSO=5{Bh+v0LBUcBFz!dA7o>~&_p5U6upFhhCu`Y&5gwCp_h=lreN z0$+ZB&rIs;2Sv4P|2FU;B4?BL?&iMPvu|VkpBrB~!{jAXCq*zyTh~3?9R2<6_l?Kf z^M3MLlrn+50a~fBZOz)MbC9Us2AxN*{9-K18m$rC%tNLrk9f-P>`igV8?T!EP?}Hd-H>9cA75ki4&tARe0_Z%>ZOzWF zgg1)Le%N2BbD6C%bgjZ|(01wt@|M$AAJ6mI-_8?!wen5WgiGg7R^0Io;*9xpU?Th9 zkY9V^xNgWzw%_<=SsqavsM8|Q0UF>19d+wfwq4Hl;pu-dPG4@!Ykjxz+^#_5U&sEv zW-b1{@8tELdqp=)+a_oKc;nh*XDwdoug>4ir69?w%4%!6_;twSrh9g0G{X+9i51lO zI%n^3!xzjSZzvak1vTAf#Mr(Q(_}TDXHdF*{f`>n^{i`_?w|Sj@#^~0lk@hoHS9lJ zVaj|r&2@Unp%1@5pWk5S%T+uFH1mCpr(oKwEh5#n8`4~h8LImp7nX~ypXN7f+im-^ zuWtVmoxlF!UQnxYo_gKCg}e8EFJrjDWLod+ztn2VJvZwqC$n}(uUA>N-SD(~#)IeE z_$_ZEqHThW-nLx}j#<>jM9S?J`@Bf5eDF%l=q)e#*zBr6L=q-79&}x_?)B!k_zh%r~6X zkDhI{Nmph1uSA{_`P-XTe(<;~uRLK42sQPWdZ}{T!>h($gf{(Yp zHmub@dj*t#_gB2Di`RJl)k`};_pM>-i)ZR`_TMfXm*4%hUgW^C_3QSXnticUtEbU# zBO9CTre9k6FTNHpkNoyrXwmU+Yi2FjF4_OI5_CSl&$=}$gAD$x?)RPdWvNm8<||Cs zF5Tbr_fK@*o^LZ2%gOJ0yWi@*-;pQEEKOXWW5R4+#HPQ!EBfW@ha2~fGc?(4{&ngB z-%tH#7N_U)?5etB8uaR#_p@{V4)o+6=bQd>(^Byb)9%$g=&$?N{`0tBP11{P#)tVC zzc==2{xMh{ad1KS1&jWb2Kr`;`OVL0KC|mP`*-=ec)#1aK?~$|Rots z%T!s{EZu(Q=EF6o_4mDHW-#D=?8{O0$C9&c`$6bdeehmraNiQ;?53}ucKrNxGl)74G7l-^g(J_J$cohBH0gU&xiWPWg563}^DXHLsp(>m)D~Kl}TkPkO!Ox4X`( z-~VuZ-E93~solRa2RY-ty%$y{uM!qQ{=f6|B~|+$mMSOb_=z>ov%k=M zZ>{?Q3Gf0Yp~QD0U%C>GJ^oV->eg@gzT;vQXWP|C@w2o4UX`p(-dlIcZJw5X!iK!+ zzsIlj$nHO1U1$IQ$LeU)wOtFo8!mo5Q}o_!H?q|@ie8+Ycnz9>BW z^;FNt=)aGTeop(p=oNQk09S z$}a~^#JoB6r)8(h1@4y0c21f8&yow)eEwGu`m`_^WMFdto{D>Qjw1RTD;o~(QrdQY z)^@pycX$5JtpBrK(eobLU6C&@UVoBYCh?__y|%rptDhs^3Rdy>n*Ruyp0;El2rN<$2c3JmX+) z9K6tc`^TNS)_-@euaRF0+R9a1os(o|b1r?GUD#X0`a|uVkKK(;?<_vYv9`jogLTc) zy~}F9yu8}4|MU04gSYEg8P*@Y^+Utz+w&*wmfv=*W0pkK?y_15b$Jyjx zyJ&1vzkd4aCS8zOZ=c`WlXJ6mx808e^85Exy*Q`e`(?^5o|5{+xaI}npmi4D3roVE zy)zUA9i6^%26*b=Z=pnladf}53hQaVIc6^>Z$6$|{q$b#`o(R@OSyJ6Y&9u-bn&pT zy4>HVubG>x@x{uxyxHo*4pMPa$q{nQFSt_hq z7gax}$y+VX&ry}kV?cmLeaypaCwboKh7z+A2f z@8V~F9_(7`eZD&Q|D@u2K8E%6Q*W@<&NQ}4Fq+RCtHyF^RavT;((eV|ZL_D(y%_#< zqo{C1ZJ^JkWa(pHcPIuK|2j7B2`c{m`~3dh zTb%{vd)8^|I7A9J-J7TC=9afU?9O9`)EVK5)jGzD`OhbV&c}cpUv#zdj)dmd=I-b3 z%CoLcPh5RVbi=e|K0iMw``cc%PyhMp_PG|7%3$@!?$}vyT?9x2` zxa0a!5y(kZKWs(5UMv%0(0E*>`gEgqwzf+D>y=B=-&%_AP5$p6Rx7KwDwQ$&rEdPu zd-BooA8Njus>|%H`a1VNWF|$^Tv+$($?}UaPonRW76S#XURk?=Sc`>9aV$ zj_v-h-;=5xI~@_-POq5}5#Z6@Q?o$#bIh2}JVR>ptE}2(5BJYC$lYE)Q}o6C^1{&H zOxG@b$vplo|F(JU{rI~-I*rXL1LvAty8I^nEyJ_NG3-_bk+n_?6N;NOK)qkkSp-+* zHaFe7p%K&nRc3+2)56@PS3X5P-fsKo_~&nt0biGTtPo^fvvl7+^MCK%`J+GhUq562 zv3zx#*nEBlVYi5ziW5NRR9$i4*L>kPg>m*va2rY0rhjSEy!VFbLY;;iQVyh5&EBQ$ z`Cya$`MG~L&a6$Izw%eqk}IHvo;!>8m4AKID1SEg(*MQxnN^~<&!5W4F-O%+?X%*l z2BRF)7Jj!HgXhK$l2S}v^?P))z0DwJSB3q)TjilF`Tyr%)4wq{<*s|3cV;?%ul$Ll z{n}qnORnw!1%u}CJ6C5P;M10iuDO)l?CsagVwOGm&w}lVb8}CnRZ08&b8_x_%HBOC zJoDImmX;?QwX?l1B)e`nQ@Bmrx&~RC)*WW+T!^)G`@r1bW_8@UHiqY zpVh1Tw{3nGQFBjUT>tM!OXd6eZ7cN>{S4peO3NAV4?S}0$ihv}uiZaqX#6)<#h5t| za%aocDjTIU{9U1YqxDt#Q}1!EXq`9v;`XbV^JnaPz5Tnad0y^S@yFi1x(Uj+s(%&o z_ub8$Uw)_bHmkgpOia--CJAr$y(Jt=&TAT8NOfjd6Zsr8qvwDA+u`pwB%XC8NQr9z*H^~a*VpEQwSAQ7jwjQn>;8Je zZWhlh-+E=*?`Q0LpE%qXIMtQ#dj8|{x1`0sgc<)@b!bk~p9RxDf@W%zabhAdsDlb}57g8AE%ll%T3J^4Il!+-yf z_iEP}4}=wM{<|dPS^8_!qY6h?@mAH%*v)2p?$p;CSG6_&!>=Cjtjamu6k8ix^eXzB z)vFhs!r7lsezd!Cl{Lpj{^x~5N4M+S)qlGC{qLs_;&DIk^J_JnTpx6uiE-ug4F}&e z&%EWaX6>@QT)zx z^7maeVfSaO(~A?}zOnjN`mc?vzs;MncE)9 z@iUwDryfrKvz_@M((10S+yCrX(>dE>)ikDx#2x8xUDe<1JifmcGzb)4YpbX!X5ef6 z#aH&;!u(gM4CXOq+5eU6i=+RmE_Yyh&(bh&QS;2CXN=Mj7nB$8+OuGN_>9*+#~yPv z#r^zxqfWHd_W9+-ui#pCBo{tIek`VU6X5$(*hq-J>%Xo32ws|cWsvao$&QW*`e^pdD(k?Ef(j$ z(f6NW_3{yDkZ;YqphMw02|Q=#{5r^<=J)qO_`iJ7Z&O|WPJdHd{r{-@df!Qh856ue z$S^%~d};pTV&giW@7H@4Z2vwn=^^`G%QMyzn|>|oa`^UKXxedwLwjEI%@jU1r~J?p z+d8Yw`EP<#_gBPoc=oZ%kjy8o<9Ie9~*bTh*6Z)qQ7|q+9CW);Y@bY{lE(YWp|T z-j5V#Jm6~o=b_?k+x;)K)fvCkh%&e>kACFXzZra(t)lu#&@rf`+Rwhhddu>1-7n;> zPOf>;$S+s2`n`D8t|Ug`3m=ci|72pAv$5c%>hv`-dw$pU^W7_zU^o*r;Y&lIz-(uQ z6S+4}eY9wLnR{lFE{{yx{M&X6KON7151jY%`+=mcgfqoT%e4-i`W6w=&)@v(s`|2b zbw5F)ymi-FbfteY?oQPbGjM!s|FgqA(Wr)<# zf0e)at8O>ueJ)ct`sBXbs;hZ>7lcokCb#;NOgfH&p4U%VE(Cd}~RxA1bmtlG~X zWsSGVR-ZoeF**67P?%1_yf6Q^m+R?rx+F@}`Frlvo-MW2)l?h|9%8CKJtiokH3I9K)0LSzKF`bK&Aniz9ms7JNR(bHd#2zGeEk zpK2m84==}+AC7n^YIlv__+;Fq%!dk-dnWa8B#S5*v>PtC&i(AZoMvT!1^=#!tH(1w z^RVshJh?~g%xu=ZQZux_-`o22+p7MH&mxmnd=P9|ESc}G_2k{%nIAire{)KHoT_dx zS%g#PqgbSp)z&!=?tfo$*6eumW&R~ezh4IN@HjFZTQz@Sq##r8;XTvR^H%Sbys$cV z>woDB|92)VK3sgQccb6l_0|mOoAVx4TvL{5RJ4BkcG{su8R|NdQ`I+!X}*hoYF;fD zaqhftV)J|Ld(UKAXRnbzFrj3=oSv9oQ-TERLH1)dS>nO3xfqY$3(t1okF`~fdYfhQ zn&)69>)trSFfX|*8`YP>kGp;p=XN?wpF8*M^X$jUdzji-oU8=C#2uE!K4Y+TNq&K8`Xvb{B3l8J*!VpqedW7n6Je&}U* z{yp7p=8>PD3@7{yv=|r|BuiW)N`mv#O3D+9QW?t2 z%k?tzvWt@w3sUv+i_&Mmvyoz8NM7#g;uumf=gnO9iV)Z9$N$fsz!IeR(yrgB(&%>#^5^!&&!#BpP3Pu0Y{0Wv+BHP_fC0~8gVsjYo8yXDHJYC6}P|&yF4)cONou98QSkB+z z&+vyYL8c*|A!SAe`{4*VF@v_B=8u+`vK;6w`4!}8(2)JolI=k-!(_%c;W^@mBbdYt z+8C7>ZCDcoSd$|J3%P1$%RP*%F?7EXw#uw4G2{@_F%~1fBEfx(3pQmX*Eq}(ntoaK zrZK~#Q0XHES_zEFOf#o5?pb%M!|Bn2Y9m30OH2m>ud*5T+}zM$*ytf?ReE>*Edgc? z?{zMM1)Cza8uBEo=_L3lC{F6S&-7u-S-am#{JD$`OP>VAtiPquX2@(^+bOwB;S}Qx zCp~j(B_*}p0Y+aIGwch^6*zpNjkSl9#ierj{UWiiTB~>s83P5M%$C`>jz2+yVR=@w zgqxU7LeJfo3=hH?3|H|Q&P-kmoVKPC}0? z$63q9y~}QK^a}7A%d>oF%yO1ECJ*w!2bG3Nt9mc=sK3qLo6{9;om|6gaPCc~T(G1t0|FOK^b5a6qm8@Xm zgSBU8ocQ|i_<e~)gp)cKs-jN$kH z75L2Ln4+m%ylm1ILkaz2p*BMyklJ-8BU4tNoO8hRZ+dn|xZdw?hfNa zmDqDjEEd*D=*jys-ExPwit_#so6cx*wOe%B{*{qQwqtH;N;#{fB)Ga;+9Fce02~5V zitjS=CYt^FD16|tKeMD*Zp@wn!I*OXrsOlvH~A&;WmRP^e5`)>2|u?)a_W)ioB0~} z7^)avc-ijnQU7NUv;9li^XkZ1PqiQXN>GtmzgH`}&7AR$-V>n%_rJ(YF_k?0gPB_* zIn*WYQU=4;vop?^R<8PbRC~I-IM4|Gn}TBc_c-^cpvJF}DX*;yMU_J%+5JTJJVm*Jm+ zI9-O$L87p5BZzyCCA&Hv^gVXmVeg$vHca~wZwzG_D7mxfH| z^V|$(%rjKx%x2(QZP3B#6LCPom}BAG-=$NIhsJJo+_~dgKEt= zE=`&D{N&Leq7Cs39vTnNvsmdQ^ysu?)G{8};yuf0W8vg1ZZ7{Yv6$%XhcE71o8_(7 zULYFpdtQr+>EfG36E}jCPYhYq`YPa$LT&+Srt5KmxlT$N!wW|6OF@~SbIK- zUMyQ2eopUK9K+jC>Br*7`&Mr|P~VpoE8}cpDLi-Ue9?y{A)NosueurjI>2n8CB64wfg|S9GkdwO39|T&4SA{vxvJdTvB4$P#_< zK=JA{k$GZgL!%dP30B#2G`x8AlT+2TIJBRknN{b5z`_sj86|wab2s`P|MrV(#^J=P zPW#e=TJCh#YTiiRm(?usIf0Q|r{#2kS%Y?{bduz{49-6mky}Mu7iWboD9OAU7SVbt z&(W{UzSAu^nptHtcdE3RCvP}QJlxp&EZC%g) zK$$^2MEb~yR@O8Bt}Oc9xbF(ryp!HPKRHbIJ0M@ba@&#eUtdFW8$~s}<}P%-xw)L7 z=D^P6Z>_9n_I2ESvAE>E=(<}H-o_OZ>X@!=-fkoW>6i# zdEiuo7PBgI;c}A?atT*5u1<@Y*TG?t=Et1my<(e7`l{e1Nyc1X5=`1yed>?+{wopF zPcFD)&af_WbK_t3klCN&<0MW!N|jlZP^vcf*rrhF!y#n|_;zuO~jQ5<&7j8fTu^`n?GmX?suB>Oj{^jI>hJ#tzs&+pmWc=+D-L9u@ ztk}N5dQn};uI=gS@BRn%R`uLPb=W9jmasJtIJxHm* ze0KdirHtkhb5ZttOK&M0GuBD?rLgLFe?V@F=O2y-2fbai_5G^OW?$0n>}z}cTEDUI z{=u*B*d3;EPQ3o`=mQCRzKqPM)l)*aSawZ$JmrsdW6=sVMvc^Ifm{N|@5pU26gn6v zrNYW)rjt;0VY1Zsn=@G$iWz#oZc5!_F_(Ag+|4m9zu*0HbXYy@Kz2UUYx7ex)oo-d zUesB*XzaIWoY~Hyeg4CTS*}mwWIRqD`0BICmv!q&*IH|J+h}PqgV)Mp-4{i6B%06J zV$iqD&TpSc>|OumO|Hv2W?a~q=G!=b`61O;YJIZoYyRDy(daG0{%(hC-)}airCw6T z%f8R~@K9wo&x7EqBbrOg&)u(|@k!uq+lq|MEC+fS`c|2>?X=NJxaPXU_#Rt=*N>Se zQzmeKh~0HsOJ{Y_IoT_+^jMdo}D((d@wvzF;t^4?Ycg(vC*X!#n6#jj1HQep+c12TD z<0C_dotqqbr3$XBf4y#1BI7*M1zy!`S0(2-Ouhep39J1j1?kYq(hD_;j_rH8Bt82e zQ=09%WxwYZt>QJD5PD_tOLNZT<$(>Sg_Z^uJ(}lpMRQG9=uA$#O&1=#dZX9HC+hDh zbo_XZQi|%!yl-q)xkUx-t_h5r#kL162@ky5CUHcKVbP~IF(*IkEI-zISmk`O__+Xs zwlg2&7pyy>|8&kC`Awe2p&$RwVSkXei$6u=FjJbW)}_4@VvoE|f5ntL?etULtOuFx zvio!F3^ffL?`*VXO<-evd1K*|$nZ~UHcM{_w6UI%KcqP&BIMW#28C&-q+R2MM4vCI zS@yYvy+q=ipJHJ$R|Bg~{UMF> z9xkD={z}Ovt}CUsotv{*C_Fww#mW25_7KhuQ4{Yel>M(=k+9xN$n(U$Mdy1mw{GH5 zn{kbOv4-d4r+u1xB$blu#2QvIlu21k&rM+bt^YMsP-Gon-o7dJkGRb&)6M^zs2+}L z^|4CrklDfLC;S9Gv!X|-`aSHkT+ z2T_BK3u=8!A4r_nS(sM8t2110ec-&idsp!qc21sBQXREbvGs{<*OWgO)OIEtaW0nG zEjUF^Fd)>4$@jpk0Ou3;%vSO!%vRy5{&(TH*T*pDE3c&)9H#WkFD?AB&3w($^V`o= zy6T;J9_^z3B5W1!CX0t5!e3HO}hrvs2uB zfnO(cHWpo<*sj0w$)%80I}SbZH(a&xS3=){+QqjdByWTskh<_Lr2Xg#E}!LdYZh#p ze`lF4bmsJfnQ@iIzQg!m_O7o@0GXVNHH_%7>;YfcyQJFh(XlFXHppf~SO*2D$(m)w%*7M}B5#msN3p<9wU zBlmJ6ZQl^-MU01dOOqASKR$Ohn|o+kkjXO9A00|OGwYS3guX6W@_ZpEkvNLHDegN} zev`?Xb4K$!77NRp#;ad;=Py;-!xJnqN2F2G{d@A8%9HYiLbE*%?RVR1=$4?)SUoq! z-sj+%g&v1PK%)hXHO_xGFAMr&SX|i^6q4jUc~_D5Q9Y)zT83-8w0UNRXiPoYy;1q| z%A@E1uHxNj@$|)al_JH>A<~ce%LF$}G+Vt&p<95?DRd(CT4%8yyKBL=C|i$ZbZ!^(N#MZ=QjJ?WnIImdu*D5wXwA4@t_G+ z@0jMy^s|uRV`*noTvWoMbh_oR;FEydMG8$$nby%7r}`5AJdL_FC$jlWh{98q2{UlnoXb(?TWi&{qxE-T&1ua! z_iDwKm(OoDe*LLc(5Xw*apte30ZVgPuk2bSwQAe?juS?L%WnH}d_S0wrFqz3SMU_4 zBUj%FoIiH3^n?F{7g7(dgefd-u-fJ7&9GF8VPk1S5bwhkx4tgguyUbU>FgcuQyKp) z*xb+?rWp5}Pc7l=^w%5f9)*3+JRNPMpYrg;ugCAwuZq9E`+r@`<4YN(@xd{M3pfug z@_GN^quA9>oBVG_c#;jyGp@+{wq%{Kbc61F=Jsg};x!plrqp~AxA8*cVmG{3a674rU zW8QJCo^MMfU!NK?W4k8fwYMvc&c_(58FR_9u4#-EeKy&D*@M-7!V`;5&DpYY^Y8PI zZ@&vJJlmmt@$1pDcM1Y0T>|c&XxpToB*DoND46zh$DD$*)dzM2Pgi#lIPvNFgHs+& zi?0ShJ5zaa>Io*bnY+)-cyi`m@r;7kJeQAXi%-AY+PYrv=tf=ji_dS%aqv!m7nI|` z*;{{NUPmI2+Z6{L)oP*XJ03@U5}L4Lf$zOr{SUeLS{ENZoOWbI$?~KZ&r|$UM0Rn` z=rF5t_@?4+KIeeBxsPG&o==-nZ~3XncpYijSt}r?z4!W~%Q^`?XSfTZ&ELKJBRVlzaG;|s@r{APCso~ zeERk#*Efl;>Jl2|ZyE36ys_7ay+6L1$NtN&n?jGKo}O5G+;HQ2AD&-Z^_#jpen05i zahEYfU}oYPKbF$-)7rG_BtJYaz7}Z7sOzx=M-x}@UirZt_X%%2qkEXA^gRQ!K312MqzzNJ>{3@8 zxuPf8EZynHbS>)Gf|;3-5yolFJqvl$&&#uKD7;i=@>+lGk2-0sFQ*N{mKtsKHI%vj z^6ddBU#1T(L0|hc3rr?WW4qqQdd9*fvn$JpO{|pnyu8;G!z~YYY>J&x^j>DlI`Qr< z-6d0IPOLh4%z*7eXq3~HSNsg=%cd+`y5U>0!<0E%K_SOpJo;|qc`(9R%lZO#{#C=O13goG-o(`$c#-Iv zeEiMbO)P8H^*Bl%V>r@z;KQQ@>yoqezrDCL^)EN$V^*Fi`Nx@_sRxQ&%hXWcHs395 z7OSx6-X|Y+F%i)cAxgJJS^4B#}pQGY{ARUmqu-- zQ@^le+BbjUo5d5%(epnh?Zy6yrONN0Z}<|mpt-?wr9=MHQ=jiWI&h-0S*6S}@+2XZ()uu18JNTH&e2y)+_*AK|=Ch`x^X#@I zQ5{Vxpcs&ssJ|#2Cd0wUyy@$`&6bCF*MFVRz4SVhEQ^oVw78Q|stv7G?dNyZvpp7^ zYx|Y|bkds>l84<(EE-;D9oTudjXzh*Fld)Ar{8I*8yqD98+wh{)?V=O3)(+3Btz?I z`h=oKsS?Z+EVlrK0Q__r)7rY-JA$ZL(|Yi31qd{4~r z)s(;GY{UlA^ZBOEeXbjSyQeJlT{veBpRv-_E3fu4mI@d=oMY#>_6YCdkhzoIIq(Gr z2e@m+2FmfZPtiD1A=zxy!zeS&aK+-BU|H+Nzx|thQ+xL~sz{xXy2DW-U~u8w=>SII z?E0(UrV5zX&!2dBmfKanOMVmhsO+ub9CM$MZ%U7j*@pY4C2n|>2yQ4fV#_Uwjy)s0NkVh^SFT;FukTgs3ZEEM zX*J!9A#8F(Ela|xGbbg~cgcMMr+neUS^p%!FS=^_mfM6(j;zn$T{j?PCqxLi}{hr<5gD#E%w~$ zdD)}5#zKbo-2~HJV%>}Mn6^!C;^x`QE?iRah-=NS8rezy%cN|#e6(nYc8h<#Z@2MG z?UyEMFDTfMq{p z#f2A+-EQp<+rIBgx^*DFSMF9p6mL#bS^t&*(^`WDM}{iK89WcT8D@t{9=^d97_`j% z?(S_NsV$3hjCWMkaG$AWII*pwq^L8hz(i6byJX>m&#An5Dps5~vLu<*G78I;w@ZKg zv(QEIuyix?i*K7A7V5Mrw%u!IWLU?#A!Ms#&$W&++4?6r0nw|?Tpp~r#k=m-b_VO^ zE6tWBMA`7Lcz<3ecV+2XJ1-tz1{Xn_KO^>FpN_c^AKR)(u#+>dFD$ zc+SPKvp7Cgusd{RHA}n|kX|rRsl{ol;{QcYlMmdhyvDm)KT^IgD(G?4zbTeGKvgFD z3z2Jn#~z%0m(lQH(out-=Wcq~X0KgwMC+~Aqhk~AhRiOsJo4_Rs#UKo{{#1P^2vJ^ zu;td|1?HZ3S|GBX@A_4F#?NwF?j7Ih;`^X&qr*glnVWs4K}>FXpTap~!MUwh3*YPS z{c!ec@PvtzG_$4VYXq-Xc{leax9cIDzhR*)ADCUW>-ZAbL|M~j>0@+zaqC9 zvOZiz7Uv1xcbr)w}w%s({xTeA|efg>4cUg31)*Wi=NL5OXlZ%DktV> z#b?FF@|OF(>`XeUyEpJ`|EVW_H68a>*M~?S(QtgTr|nX(&sM|MgBQQp?QmJYcBwvd z=JP8O7yl?)+_KGgyPo>c^rzQ16}LsR8#wLM%v-Lo9RJjN`oYJo%pX2Cf8O}@d92a$ zU*b=trXRbyzRab0cDC}0j&He#)-S2{o@oC~Ni_9ZtHxEfCkMT@Rd~mLnkF%Cccxk2 zEptZGbIQDYh z(JPF_p!WBs<<2vIE&cG}({4erOs|iOn@^mT-`!VK8@Af)1otn7A3<9MD=XTRzRF!# za`$X?$*WEL2NxD>sbpKXg6qKi!%xnO3-P(1*A(aGoqfE*SlyUOmeuD&uO!nn@AZXu z?*?|QKAv;n+s)m)%vhvM8p6cfK65+`xmsGep$~s3-<_hzHUWO%K zmw#a1$aup~weH!Z?r(dt7JpK>!}>k&V667Pe>{#;@`_xiu!rsCo^vzr56i=)jaIgd zfl8}fbRQo-Kfl*%*RN7hgN+>PCP_}5G5vv(|W^v$X%G!TqG4H8^$R^CB0=sBj17&IP@*EAFuy zG=5s~O=H_?;l1-uojNk3b8(jV0C9kzbu_IR@yuNR7<_^`}MmWKObicY%h>6)4$W7Jlp@_mv@^zY=a&C z&0y|W8z&QbE5zY!-qnD$&aP8d$-d6(6>4bFJ|{1H{7#+a|MZrQn}&sP%ACs;W*!uI z`*T{;tA|l;;!;hAAFjK__|anXL#vH)79ZFP-sC^{Vy3XUK}X2lKpTWKO^Tk=EkO6MH5+ z!>20|m!2{O^)YLzKRD&VG-syxS6daKz7IhT zA*u>DjIs|3u5w(dYQ-(Fg73b8md)zH~6mO;`OQ&%NkZ@1rR}haPPF z?;2IbCL}a@!nApvYu@PWh|yzVWmVKMVG(@zu%nNs{QqVarxhlbw}w1=UC6oaXc5D` zrOTGzYCqxS{N;I<&igu@IvvqiJF91Np7gIaTe9JWUc!PstPl6EU3N?0SiV;9|BY%_ z_)lrvTUxE>eDYjb(%WTkZ-{N@FZ|7y^mv=a9B&V;5ARvn*%yax^;!|VnCsQGHErkK zMlJC1JZu_zXX4t0)wYY9Qddnpy>E`=W5ZXK(di$Xd|A%8e%G*Pog;s5U&ow{Z6bF2 z&i5C#Wd95<+W6BqsEF&|%{|Psx4l0$ZEp33=LZsFJiNbW_PlwM^X69dg}(_GjcPtz zhl+!m)|}A{xtjUe;FyECbrU5k564YTNrv=J78hX%R_vzjqf_bCrnS8 z%3p6j>C;0wv9&L_vKf*+&t3YvG=H}~cak_G^V@4j*WI0zdV8jmqF3I1uOzcxv4_oa z|NKO&au@#gmbEJr+AQ{`b!YeV$8Rb>S071DN!xXI;-jBAe?qq8U1P9a|GMW~ZuBCL zi3#!T#TRUk3;p_A`{wBD_bT5feG4_2@qdCRqy4`MGo9wSEIO0RL{IBnkMH;>&fc1J z-(&g4^3Nybg_o|b6N@z6=Qp8Bk@xiVyhHM*uRmIU_4R|2C}mYQmc}(VH=O-DMN6 zJbFFRg(Wf6>guzw<+|>kPrnzGEL?kX%i+D@ac|TT7Q|X4=NElGZntR9@{mAOZ3$Hb)R{XyFzGwRhmpOXk&vc@m?bH5Uccs9^ z?Z=en8z=nMsT|9mwW%Re{Gq?*x?3E_cIDjPE1z6H=cV1j8?1#*GGER}@7V6oA9HzS zuXWeN#7_%5vzU910G8Q#F>wBfz<8pHU1@D42X=hVEAI)RB=C+lQ%VR;(4*S3rKdocJUf0Xm7IOLX{pVok zmk-YE%3QK+>EqUN`{RHARwsRs-G1QV113`!HB&9o*2R+%JrVn~&CK*nj%oX&=u=Ys`yujKlF=JX%)hh8;v%cj2% zx>oCudvNonO$L9r+Z&`k@>#M(?80JQh8cS^G{e^3?%CdE+my!pdWD%gYqjT&PthBW z2mhXFu6gkG^B{GpOJQ3X-4d6x?w^;%?4vMyQt5m}aSz8M!JZ3puehls%rJbfUt>}> zDa%;r@XF24KmM_v|8RP~y-P&Kimc)@DZ+QVtADMVvv5}b=N0Q#O`3K>Eot`FOMXnt zwk%zlvT23Oq#LSvA3whDnpJk0(MFcT+DA~${#m=^s)0;8sJ_%I zC-_4!`GSmA<~gP`*;N@*u1}VDKbsnIFV%5ghto`s>5tZz?EGu7Q)G*)ie|UX`eyen zd3xDW%E$ej)@Dt8D|DrB=L@IBH{H)$ta^37@rcir*j&$d7I!zey7KMjm~raE&(e~g z(MJ9GJ0CWYSOP=|aX=(0hV1O_SELDXA=56u$nvk5%JkZ9DduYx+`uE?%D;u)nO-WJPT3R!vqx_owss zZJJo|bd!4`^SxC%3D*{ETU6N^U){cFXR1=j!>l6Xj+z;cVW;JP7eBSy^ZV!bWoMqA z@;W^8YO=uXZ;UO+LO%WYHDSf3jx`rKPgegoIcisb;>?*t*7JLh{|G!Rn6vlKhS(|l zE&R7C3f*+LyD>DNebKzFNBNkpWxLcyU(3Cb42~Vk6EeT zbYp4G=(I&4zug~jd|LB#;?`XXZ-0cPES+O?)UN)<*6#~;8>XFqt#i5eg^lXl{$&d| ztru@HjP-3TmTr<|S<`js_pG;F&rE0gUU5Gm^fNj5xlUR5O!L`mmOS>j_gj2XR`s_v zR!vY<`x#ux{Cn=lscP|9W0qZ+E@8$ZNe_@nY`yjo!QY^(RRFd(Zh+yZ46U zfsl7fOs)rtPM($DJu#;H?O8L0uTD~GKL5|Y$^0*`nBaQa)WsvjvVT3VZXl~_H2SwE4#34d#;b59l;JpXwslE^=;d z7x$JM9Zk)PlHR>d^V6FZG|xXI;?UWrCEXF7X@B=rRc7RrwuEQ(O=-?vGW(f+vABM? zVuI4Iue;`?PrWG|-IBv~Fuag=Q7I$y4VMF6+H-FAtda~n>gnq5`tCHxX03%fam(U3 zF7@TRhh5?n>HPHWw|v}I=Ag&d4?o@Vt@PZ#AB%4kE8L4+dRXPzWzV|lk-||^l)G5g z^fX!W9_3?NwyElfTx)T3qYl?Q0rid78EiB@8<+mL;i(h5+40(kdyh{Y`>AWcIAu5c z!-9&wJK92lLKC;{R?NK46ny4$yQi`EgR9HgPtQ~9Tf}|!XVcoFaaq5U%r-tbcxfA( zG9&*en{XO3z;=ntIN_ha+?o~GrS#s07^zbuvMFq5-=;--35rR)mv8Ipl}l`r+> zx~47K%Kx@|+TY&g@AoqYP7In=DZju>y-^_c1$VXRxx4eCOfx%WS=KZys(gI1_t>BQ zwO8sMiLTmQA-~k}hi_Qfw%>inyj0%*lXX3P&3$4=kX*dBUzTBS^`kGR^mS#Y&X_l` zk<~j)I(BWHeZ%t?_on>RXBRzJ%gQ|?DkRqBnD(r2cE6`5<$s^y_8QVx`WE(~FCAGuIU4?cdnk zuyR*N3ghQ9YF>5koUgv)j@?}J@zeg^t!EUB&dDE{x%K`+m(#liLXH%xl!UBZ=V8B* zH&S!C@5c28IrlcQtNYJ)of*{HcUsW>jC^sOt^TIjceO*@jI-~Cxt23>2jA#^-m|E( zG(}33L(bna#cp$Id+Fm;qjT>Lw<=2u1a4h>{Cd}}u7_9GyF9u!bJ>kBi5NZcZSQV% z=e3F(T)ddDWMkM(sj1nXsYRcoudk0~*u@=T+PZe%R=uW`3?EhZb{5sSEPPOP@88iS zPef!kRct;rg;`r5ZY8VC!Fj8%iEH0y$#gg0*?N9e&fTs2cdfR3db+-&a^E0-!U!Qv?OrBlwF3iSf7;Fi->C_%{y+c zOrI#So#W!y>WZ4&fQbP|w#jvD&OWC2c+RQczyJKKUhFU0=TdxJz4d$GJlAb*H9P#Z zbxtkFjTXJj>XRxxbu!oUYjbsOt@LNva4Xn%Tkq|!FCr!vC5U!S%WkF@RRkBihTroa0zNI6j{ZH5MMdD}mUGq0S>dim=GF0i{O{q|~BFkDc#^Zi-oQ@W;rmfP*U7M^N zvQGUW*Yj`OcUgU&o_pAIXy@A|_xb;~S-f_8^|bqR>y*9Tg0~j!57+1KT-mW9XzRqf zQ&k76)z8oQtdj8b?B-+FR>b&EF>CzHF1F#&UtrzMmbZxT*3I`irv53 z%w4X%Y5Eep`|kIQFsCn^pBujETIw{N`nqJrp`Cx7${9aPD5*{iX)V6Z+8-KXmb?Al z$|Kq_esk{sG4|S8ePHz+1HqjVaZ7oF_}7{pwaK@vyOi9g%RR$zMU4NA))gF~p%z9f z1Z5w~vxXfMEsuXLBx1Jy%j7jnk}dXT_3YLz4p{O0)w{sb^{&58%AWAs>{l~Ee1HAM zAEL?AcimqhVsPGCr+d-vhRVLsY0TT34BD6umkUo0IX0ShSCXWOtCtrv>&?GW#;-kyc4Mi#{=S*7{{w1+4 z#$p$z>uGbhi$-5<_nEEni51De#{b;hx7hq#h;LL?o4NjFGmZmJD{3}J9uQgZY5Kal z)Q#+{yY!4UuL)ncXoE`5+};b?tCcm5e6FqB_^Ef|)Ec3a)&C5RZfiPm=F2PF1jf(u zQ?t9aSuI%m(9P5WqzBH1-!PQ$I9yDqDqO!U8SXumdme&OUJ8^?%^c%QG?#UhCVAO`9id%3oPBd(T1-!Syn-HR^Bd zwd~*As+tmUfUA9*U2=Ee16J>hj#Uz}$(&r>P9=M#_f)Ml{>*gl&tKk6Lj9iGPfI&F z?}<`+Qy95YY@d)Oixu~b?z)d!_bS8;k~Iv@7#J;@Q8~>+W!XVr4z7=SSJg6~+nnwY zzw^E^yzI5S-YPkef2N`y!xG2;hE_&Q2M-=Raqe14^9v5Ak7|1Z zw#+I2ns@p98`oD4OD~=M)cPoJ;=H&?Yj+5&jSYz8b-t`W;~!I5*u)0bi|l=;J%744 zrph%nH3h7WQq4W>>3Z!=SCZ(g%CAx3Wokm(&P~djynag8*NXh|uZnz#Q> zuff{3f`?(NpKOdiRad-bqpj9%rOn)df?9{SZpl5ItH^h8x_bQo`k6th0sDM3Wo#4o zvi|#IA#nLcm_^f`-&v_@vYX$$eBGt?RWR<%;>C&6Ew9~>oBFzQo9O0Jaf5#Aip~uU zKl^w5xzDyB)KPy`{OVn1OlmRh3sqNDe2toZy0b-hvrgdW4@F;Iy}PPew7JHci3y}T zaEVSr>+%C(3O(0zzNsYmN?$qlUi;dt^gu<9%s&~kT|D@4UsK$LJ%zEK*L-Y#)SK6=d}H@3#h1&! zD`b98dTAy)?OE@uUGERrD8w9aKeO?~w~Cy1T7qH*t}Fh0ni62KeeR7{FIh`hg*!%M za9lE86J4agC55-;SlH)RTW`KRY4QC$%MP7aRw5>=q7ToW{&s4aT(pXVsuXZo;l zIn&uQsVmnMyj$B9z4J%wJ0>-Yl^zE|>Ryz%>Qt07Rvf?n{mm-AwI4q2I9U*>qo}j$ z+*S_V(9^50@0xSdlmI~39@Z8s5Uk88FEi8}P zW@WqX(>|7KsvXHM+!^9d-dwZCFDdPAxA*G>MYLHLgl?eJ0>cLwRY6z{`7YEymCdxy~|eSn_0ML+%Hghw14NMcRTYY zeZ9GBTX6JKCb7W9ldo^(dswvV@UzkdwWp7D^mR=z-BYl=pRqvQb^f~PrW4y%2pPPN zKT&l6)WVi>uWc=k?mQ(Dd1HRe>Ps0B+u5vQE{bUX3w-=jLnu~mqN!Eo*Qkq2KHd4{ zdBS3||E^fK4+rLMpJmwl>a9K~_$0QY^3uEe6YFkO?aZF;yo2xc zbu-;*4u6>5i-+e~W?z^+#a?X1#V%gab8Bl=MXfm^l%CF6u|jp(r^CyI*j3l>%XcYR zxcu9b^l8h=q> z{qskn;b*dz`N3+DbGaO40tOeP7djo#y0GWg?@4=IN_uO%<`rr23NGKkd;3wrhYgmx z54)!2KPjDj>c4+PndQ>ksVv{Wy=>%44Ha`PcmLeA;deN*LH(MQhjvCD&^hE+$i6n? zU7BU_>Rv4acEK5jM(NXIqPHp*Ot^ zUxb>d^K~gPga0d?uYT|jG702iHF3?msSAV?GU>7E`eou|pe8-Q^t*#!c@YGn#yi~JP!RCBaL8I3G`KljR ztl1#+*U|dP1TT^OOAEwl+`~fYqW0ru63Dpak98#2KS0L=Yno6o#|`I zl`Z$;@bm!7Q0=M5SNq>>Pt31hc1uK8VDG+pl@I)`rv30r=YG2R#$$Rpw{A*|Fwd+?XF80|KaDMY@k*&Ft zjxjoB78Cmp9Q|`^}3ci@T|=pSQJetE`wo_r~b0jFU9H^!8SJ zNiY8x;F^`x*Er(k9A`hj1M(!2+PT#B)QwzT4s9JMxdvdT} z;KqNw&*ctrzpv}NoPK__*>R(i*9ELqan~caN^V-=7}#~5dB&6D5=x;3+NYojn}o?WgY-<*Ku=|CY*MB06=Y$W`YnF2!r7*Za?T6vy&G=I?)p17_Jv=d$GJ z?-ZDF?TF#Tv@5<}g&FjpzOlQ^RgoKf>iH$DT^Hn?oSi?s3T_sRwqX;l=R4DO;L*D5 zFR#xpNDGbL$!lAvy7SoeOM5?FVT#k9wYJWA=bT0s?isF;&SyU}wI-bE-D&n*-t9@p zEggo++yBg#*66+O`f;;o!2$&v!xv>CI^j~v$NzP#(Rp3&V%EjBb@k)J;qzQp-gvai zH_%bwX@>7Y>rLLDj~~jo{5?tWQo@@9Sy$O4TJN&tls0fi==HvM^pk(reIdzj>nC{8c$Xn(iCyd}uPYIgmVM1m{`5Tm(OUoghv#dTuJn2${b$e76opWS-MjoTq2eRXZ8xqE$g zY&HKXvy=KRuD#V&<;NTLnQy8vytg56Dwq#pv zJ=k;q-+?!+r#IN&yQMqni010r(`q~ZpPTp2ZL;s*w5XJ_@hdjEH;a%+n@UArLd zYS)X)kF&}f+659p$ubvkY{5d%*SgFgxob$thHU~$MxAUA2#%=0WnB6p&#plZg zDS3^~T|Ku&m3}Qhb53wg$**$1RV)7Ut5$@6EDZ?M4p4EhxBbDV6S0Y{eoaJoX>{44 zr$+m~$A5gGQIdOK$UN@u#oF}e&tO9B4nERNlsT`r&9O7H&NC3S6W<1^mZ5`q6RSv!Blv+i;Gvt5jV?K0;y{i|<$ z^BE2^>{)(GpnhX_gKg^ZLtD<&-*6Wd^Q-Irz7j74x#YdNjZ&3KP=-3Lg zmWhWdv!4B&I-he!_X@oyo42fRmq?tmW3tdy{UgbWM^?V(l;159b1HCE)JGFmSKg(s z^m_IR%%5ER#LVpfr0WbNVjGgS><)`s@$JM$k(_&lY`eeDKYs4+^THPyM(5^vJG(6t zess=uPlVtUy-1#`t6dfb-kp?jdy~$qD;*D)Ty8pfE_nC57RTgem&CtXXYpx?z1YRp zyH=|3)UQUrmG#m)Bsjz0a@V_2Z?t}W(bR2lEO`LV?Oj(-M0q`#n zyy=^XC5M+p%Ifebr+zMsTY7S`r&O)J>v}u!vR!iAhIT8y|6X=l zp;J5NUJ9!EcH+F#p9Gf9IWpUHq2LRR z1jQ`}W*nL!zwPnDMDHc%Cs!$QAGVL_ep~q5;OFY>CFj!xp9Q6ItS*`5v-rr1n_G%x z!gB>as+y*4k?Pi5n`rm-?j}p=7sq|RKic|FVIQCHN!y)m@6Ig=p0dbq>nYyLfd}%; z9?ObO6}OYxEvdgJHShB!&CTBvUa#Byu(sR9@Y2eW`?~O%hAk zrl&zIFYZrx{vyHIzJjYV+kKWoTxI8C=XWU#SI^A&z$a$#(?-3z>5Pv0yZUeXHTI>` z->lR-y)8Dq@bBE>k3VmRKRUQuT5F@D(9VlR`cXB3X*G|TSSNK@AN~EJ?~Gb2>k-92 zYOS#zEvGlxvc_zmXd4+^v`~MF?Zm(rY6-fj`@3h(^!rj-z*sl4-r<>v+{trg4;LnS z-z`sZmdjanMT}X%{ObGQw@zz~|IcI+{I0J2Vy~RrY~S9`$NMgEW^7Ng_6wXd@k37e zB-@+HItib4#6NqWw5j021p6u7;c@dSb5*;JwYamQ!%l~cg6vSCwF+4 zJM{CfELf9vv5WJ{O!uu7PbWEDKRQ2vqhU+@a*nuF4zF@6ON2HA%oLsCKW|^dJm%+# zSFK)GZgUavygTdY#f71kSC{=bWIyHQdaYd(0{v~aTKtMx|Hn}M>CXJ%+!LxQ6U)Cz zh`LX767=%=?QxC8ZPDb0&R;njH?hfErY%-I%KhY8&AYEtBP^erU0qgjT4~pf?O6?M z$$ColYjV%I|5UBg+4rua|GmvsH<1IdnEI5dEaBJ&(sN|_(6b4`H^=Dyqg^W%$d?~BV^<)?IQjk&#6dV=^a#t2Rb0xj^}%>w!b5Z*NRnXV*6K^S9Fy zw+i^={EkhvO;0+zey>1o@2}32Q_Mdp=5k9Oi(VEOI`R37jSl~AWUNYG!ThM;%O0Qb z=cYOd$1e6YS@1mX?N7bhGxhK}+vgvD{#aY_uyE=3hmWRJbKO-bE>R4QI5P3M=_>(+ zU5@+#)9t4%Ila|iPAvHA?`=kG)8*FZv+UxSF{Ru?g-1Vb)%{<;f0jpod~^7$K+usd zan0f@WDcswmAR%#uQDdtL23^?)EK3`rI$vZJuNStf6k=s zGdJh@hgV-8-FjAc(X^cxnWa1Rn9@A7e|Y~aZWY^EHT~)NsqSY)HaryBecB*Rp{c1V z&^cJxiAk6zqkB;okJr{WddVNMBMvhI%{Mk}!Pu5tzbo*_*s~}xs zv*7Gyb3^W}5Z-D!tzjqY?%wpRI*gO!YA(i?2yDo@QgAjPY>jPO56|+8hi4tT#j8GH z%B0Q}9GZW{!!Ovj^el7ddCYzJMW$usU{^G|M91kpc zv+LZP%livjj?8al?b)a#WVLa=X`P3v2e-%M57y>Pwl+$WOZ&RlMYqcNM%xa3R4YHz4TP1*`hWlGVyDH*0qN)tO|xMRUu9 zty8%7uUe`-@3lh0$0wJJuEby4Gtp%JM$Id$LuFhKFWFP8lh6}7@ypFAL2r(`%sMIc zL!8#gHia)&XsESGdd7-(t!{Epu@g^z_l7rZ;`*2@iX z@oa*fcbu<1n4HR+BeIL*#x5Db*STL7F#o=K*n0lDcJIx-;k~OazPNPngIMNH56)SM z63qhE=cIe&%N8tSy5=6X)2T4m*d4Nb-1DVH`06O0ynWlG>LLpqj(xaPQB;u3!s@B% zHGwN(aZjV;`f*a}>;+8Sb)9WyOz!}-xEoVG)6Pr2LToxT)w_QI!q(>!wJH&iz z&Y^V~QEbh>%_o@t$&$6(;^VN{<=lp(%u2oI4?eD$^KfZF_ZbF}%vhsd7jZ`6M_Zj) z_@!(&bhzb|yISf_nsxY4lVQ12d5ieuZ`=MVXD757c6=4dRXoUQsF8l-`Wm^4>Fw;P zeCv*eHt8|36$MD^B{V&k%W8|her~$qmW4JY<<3)db+?wy775oqEa73qT54)m_d{j22=B@EHN?a#T&N=Yu z;pd$%R1zY;bk_zQdvI~$!Wm!heyynb!Bz9;Yno)RNY?`ecD1@P(OD3*zJ|+-oLwz_GjX(kX@?dMlrbJbL)U!RfDp7P)LbC2zNvYj2f@xc)(j)#mGxXL$c$ zJP>d()Z?DWqU57a47D%WoJB)5m+#moIob1$h(v!L6PwjNFJXfT9ee+KSk$hBn@~by4;%)|HMz&xm`#xiTjCPX{6Ijm5i>6&PRJspP6w&qeNuG z{MP0C4lQpw_x)R7yhzE(pzKlE&EUWGb8m+hxJ#F1gx*`Dxt}HB?g#I-lk+V@($+1# zlX)m})p`aSf5zQu0W%lzE)~_=A@cU!!BUY8!CPK<`Ooa<;6FIyyY=I5v$GSg-0+Rq zpdvSOW>J;xG`&cXe4gCi-|ur2R{LCe8lkLh+L-9$pkl($ZV>)2rl+omg|S$H|>hcn|XfyP_cP<wGNc?O1OpCwqb8V&KBIo|meRPXAwY%vME$O{8Ya9HAXP3)pswgfmp|AE;s8 z@8s(tQ$6|hg%G!_Fpf*T>zK;K4!mY?n`xQ(RqNu9S9{M@JU_Z^hE=tG+MiwG-r@4j zRc+rt^7FA@Sd*x^W&ff%(IWYKmT5UTFHuzyJEC*c{4!6nh=1VoZwjB6y>TcHuGait zugO~T<5qkCOU50Kqb;{j@NCfI5N!Rku&eX0YdE8Djn~qp?+mLS2^l6ZsZo5Vq+50; zbYv+@98$hm5esHMtVFR|HIgf7dUSa0K+;aPa^i?WSMBGesw;>m6MhK?}y~_9F}k}T~(6pwO94*&$)i_+Yb0Kv3Z@4 z;7DlNX{mTivCcf7VTG%FT5Q#d*{whR-dS*~!=~fgZcz=r*+0$0gDNf89%>NX*k=4*KW=k58PKB{nUD9+ETNJabCac-M(k2Gz*kgzgwcTG3?ga`#!a{%Yygry8m*Y z`UJryyC%EC%cifrz@yH(%QkiU*;TTvJX5Yn_$MxUVj448Lw%}9`IU=)cuZEbSChCe}IXEIag zCCmB!+fRO7bGmb{X{n0bvSOhuMNPkO7M-|z8+&ZHwjDXg#AanCuYSE-v$ft-t*y>~ zaYo{n>UA@kz3TS++wac1vgEtB$b;&Zu1`1Tiiy}p2X9rJWzZV4=H)8hiLG~8c|ta9 zGo7m=wc701Ba0Poi&kh=P47MJTe=`PT>WaB#DT30W*3(|=9VrOub6SGRwOjHgRR4; zT0d}S4d`eBHm`fR-pjjxhfb}z#uC^ay{UX8{&GjkH4r)WBrBMXPleuIx2Xppo?K!@v2_KZU>W=V$+z!qsyBhs3{GHdwGEpFY`7Hro0oGl&pLhIK zyxKOySAi=+&v~WT4Qs|t8-6;!Yx=aP?YoZQl|9B6?mfQ#k}r7sq{Pq_XD(h{#e0!K zoHdI_^?Kp~!4pi+7X6;^vc&O^P5NhX50kr_JB~cRe&TpusbcriTm5T8q!%?9bRFuw zwB%i^L1}AEdrsP?z&?SmJh0Lf8{Ojer#~HEpm$9mo#S_nqoBoqytQ1?FxIe6r3fFgHN1D)=j-U&8(u|Mt%AGLKK)`2D4q)m8Jr z+>Tbyg{zd~pXa#zyK>-lqLxm=boZF9I*{QK!!>hjX zgkGM!OJL8EjDM4!l-1p1`yR7e^VVj$+@jSN=U?5s=GSDOi5J>4dvAJVAFR^5^2TFd4>7GXFmCDK509{6|)MT{T`?N z4zoT#y8F9=yTGER#dqqXI{H^^I+fUbJlCgnf&4-)jk1Rfe`B;mq!0b!7c+RR7&CFP zX5bHV>Cg!op67q7q{v^MyI}v8g|YjDf;IGr=J1_QWp3$Ut z+vY8LVZHmIQsB1QXrb6d#?5BzE4Oxftb4cOmPE7Q{JqvMBE7G&rUiF3nzLM%jg~%H z_OJWr^5+k~{#CxCuBUus;wD-Bx3!G0h8?lzZ@ zhhNR|L}vSaZ?Rux)^bu_%;0r#->>kuOE$z~$NXcm}ZXZ7J1@;s6jxHWJ0 zqXjC_4{!aNCpIDZ=bo|&k#1`}BYLlY?>#W7xZZPq_Wjnq8QXRV%B}mN5x4%Q+My;N z&)KhhMAN>-KHG2k;>&-AxqIFS1x5%g&NMz6AtPo`d&41hlFQb2|IF&&OFgyh?R;_6 zRYYxn^S^47uEr{*q4L6Z$XM zcUPa1Ub*9|X-KM)K!ms8f!86@hbr3ViaS~tP3hw4SD)U0cXfGt0Pm@7Q+ReaYMr@$ zD)^-fm)~^Z^Y7%E*7MaX7SCe3Ibn-j?7x>C@w5JRxlQa%@h{hR-FMGJJ>jmm?ax#F zPrQ7Cb2%Q{>LgTsQok$t*N3%@x%Jr>4_6mSyPdBMYl=(eyjf{er1Mx}-TeYyF_ZWw ze0!&R>~QTeob4+nxP*@WI?Ie;$fkRer3U{%k|T&GyY#3ctJ04%uQpwItn&EoZlMwZ25)T`#%r zb2SG0^J@m6ohDydIfvz#T86Bsv(FEYqB_hPA=c|ZRB&nFi&w*L-bkGo+j zV(WFc21qItkxWS+^QVdWMSpxTGS#&+3orq)o!1vmXk_%lzZ< z50z|Mq0~5S-BaP6m6N+er4OBGWIc1QgRO=?u;o#=$i@>+)7S&QKAOGwS8S(BifY2A zkDfdBUus_EyHUw~rT4*zSAA!0h=^@CkRTr_DbOf)ex=zluN>cdP025-+=BG2c28h^ zvwzOwTfM(QMz^!-Y^a!5wmd)ZX=_9J%blvLd^Jq8g~N9FPjHp_W2!bSaK5G3dWJ^( zpN*}PtrWYbNh~@MUw)NsW!k@)l_0fBKT>iZMQ!QMTz*UB>YU|gZ{Bb+dzNC-%Bo{v z>G3wQ>hj%)%?^8{PZy=`ieCESOxNerOpym-LiT@@T(?4S#)bw~v&%1E`q)mGwZiO# z&!eD61qUk~w@TJ+G|XI_B^)w=^Fc9#JYz?AF3(~a@ z7R%-vu7X>NL!=Kyu!|Wy3v!Y8beW-!-GJ92@G9F$B~5M5t(y;d{y}3-kvQLD zFR3$2H#8XjG!%DWePya9)^D_m*Jzec__8eFKWdj}zFapi|MIIZ+?u-znzNiGvJWvG z6FFkJOGDoMJD>mUl~?!3iq*CsPVAolSZLbwIeX$Bx=rp;k2oNaFVM==9?W-Bz-nsd zeW@FKy^6|BU;aye+R(-o_&q)8n^;NDq^!dHyRHv`syQXn!ufD}n zFF1dt{>pyY?N*63O{_hdI{PmdvwoG^&FL^9YjMrOsEw~y`F@!F%602s$-aOquO+M= z8uy&v&|vuQ6Mw@zhF8IIk@B%jc2aI8E&@#tO2Thkn%p+AH)Qq$?b$EmzN#}wuIf$v z(#G0j*d@y#$Na%!>hh|?ub!@4)a9}`>0(!h|K;s7oSQq^R{4II=KSU70~@_#1`!7& z78eF?{T&N%?@k@LEaM=~@OFj&%$0W+&0k~1GTHyn6TOp5xXq{UW8Hcvd-aSe{;dyw z2Ai}l6E-`n0UBInl71oS$|!%=^F*G)_QfHyT+AHT8|^jx`^4mCu%y!lWeNT35_#83 z^md(}!?I|2Y5L4wK}SJiZypdA*CCX?S|* z1-4~mM6P3*>Xf>SB~2+Z&2`z%6w4@uwiy|EmaL9Tzbsn8u{|Veh0fVY0Xo@YGH2H% zfA*=5Un=(4^7+p5JMZ()5#RUW-+%Fvy3cdcC!5^qTffWl+@fHwWo*I;+qcLrn%8Rk zST8tR+jIJr|4ti?-z7OD3nZLKtG~HV@%xUaWy)`-CO^>@&&#oO*lcaTQoL@?{HyDQ z?!L%&J3g~;TETO{bK9FsEESm}dl{TH_H9Y;xhp5Dxqa6AmFN6dTLrHT=hN^1^;r3$ z+WxiobXH$DVAlLd=kuK2Jq>Hz8x&2XA9HLdp7(y2L-p$KFE+1wb8Fv}m0^sTe2_uMW4DCwqLe(&%8^8{=2J9*B(~(oN_C_xW~bO!=UAauF;A@_EX0r zmz++G-Lmt$nD4KuMgLCxkxO6sSt{Db&wID7_o_S3mT)l!DjpD6tBj@=P7i6HCTp_$vW#zz{mO@;NlbV2nV))!FfDRvPz>pQJLUJc zLh)U@Tsy_4>0SGho%Uj8bMe<>6HBcY8&AAmsJt(I_qoe29vOPFUbocSQ|G-&L4cKs zXJL_j0lUYosYeQfH&t$&Y8#fBc04kwu41N{?A-;kj7_Ki*?B$wo`*wrL0S}-tTkbUKhI}_xSzm-&Vc76}~xq z_v-SOA$M2)|MPopR9VpauSa%4U3urO%EUfmuR6B`H zEp~^&`hdmV$IWc~p5?f)`^#NeIJy1H>zB!^)~*h{dvjOY-qf|9Z=3AtDtY*PUBOAF zyqkYQ4sk0g9}w7O<;k6sKO_97@29J47gQe>Zit<;tMP(hf(LW?%tyy3<$b#Ap>XGg z_Qu~44Lbx9JebY7W@}{jFWI(lHs^sL&*)ln#uzOop3K4%)2{Ab9X{(7U z1S%hxkoIoRk$sbn{meTuS?c$@7V)4?2IpBjk8+jY{&T*0-)zqthMvl~hm9t9u}EAx zx7Tlu&dY|qerG=44Pd$xb6s}ZRt67UCZ3&13zyVRy5_C*>8?+YnOBv=CC>Z3428-E zCU|f2e3dJnxJ&-i+8T3_h5WhApZM#^sm)z=?S|}#ysvjJs1|XX&YSRM zmF!B^Cp=6%J5QbM~8-d&K)~ zN2^@_%@=D9lqZ+zOq;zvT6g8Oa$|>W3I`hMi+`_B^PHuZ=~Ua$Gu0#_sGFs_KzgEf z&X%pV2X}$;)q_J?xlcA<`nUeeM3XadhTr-NB=^nk{vCH|`M0bKx~G0LhPEC!DwuHK z|Kn$;?6#-x>pkXnOLbPW^iGB+)}FI-iogDosA3E1Wnh+i?q7X0Jh47ccje+GN!-Te zz23#UWv!CE?`h{M9@F0@|8~ps_Y5q(49trHk4`F6h-8sqQ1GA$QP0fsIZ`I{o$FZ! P1_lOCS3j3^P63@7{yv=|r|BuiW)N`mv#O3D+9QW?t2 z%k?tzvWt@w3sUv+i_&Mmvyoz8Xx-@P;uumf=gnUBikRoM&wkHa`C_m5B!vU_jw*SD zET45XC$ea2^roGDkB*$YmvVidr`c@ZcWIle^UZ8$`)>Mpt5f!LY}os$Q#69!>}_E2 zFky0eZ#={D^#7UVGA&BKEQ?Ro?0;^_?;$X!*r52__qzXb7fNNhTAdaK?C5Q{E50B= zLxk&SO#ss}5F>mMi_gLU4Uu&&oHLvj257`|3K@Wv3aKW5^c1Li9T4Gab$aMEr9o4K ztM$-S4aQ&%5w2G4Kx!HgVd2Pdl%a?*h4BP~z@zW29Q}q2$_&3Md~Vd-jl?8`7HW$qR2)?23v+I&6K$eQI$Ll9XJ0}Yl*CL;GE%PXe!m< z!_dtb@+pF4ft-Wv|2VIYpFTcMZ}xK(W_-6Qei#R=l3T{@~&2Yd6Xm-ZTG5XE?vw$IaDhF^kW{bsona@G|`4ZCJ~Y^>c6Q zvF&RP*=wE-yff!C;~oA7(hPkDhXWUSXisU-yws~O*-(yg!(_vCHe4;zA1?3JG)*@> z(^12CAoxr|h~|&3EsmTsl;)mT&iyWsp}hEGxi;gn5BI%a7Ye$*5y(C%!gU^Gp`M5C z^|uUmj1SDBV&f7;8?@x?SvJhxn!?q(9OQ6zmpyj84~k>n+>e1*jso!;wT5PsMK<;ui+#&w zc>ZReM(ZIRko1wFv`4j5cnd{2SIua>dEVr=cUk-dr$jcd10rz?C+uZ%-81b*ts$Sw zuKt>?W|R6wU2{d_95`nj;%14M^QZ7yrRKW+FEx939w;?z5529~dOMJ5*$1Ye6@N~@ z$$oJqZI9o9ZeiTwzE}b(pp`D*9yFG5t$Z zR|M<;W#8?Ol9%_c|CpWiTe`2$Y5xV5nho+6l~Ow@I3-@NA3N6IA<}VFsQtlrXMUE3 z0FDzU7`nI`Rl6Inu3nIPljq7chJy)O>+KHY$v!H(yV|N$<4%{5L2I;T>am48_?$H@ zlWq7)V%q9n2z)rm_#j64!Zp5_olHDD|GE>-wsc&*n0#Q~-V5b^{dpO zP;fzJ*nhPI^MA8%taX<9&$s;160X*n8jQg^1b607XZn$I$||>{)X~0xr))TMgZ24+0yCwWK%g7FAZf#Bb&vyOM@~6L28QwEzghh68 zZC}XZ^U%%9az@eQrRf(-55KzPnsTp}zv8LsgCCv=H&T+9ZWq7tSFhlWM8O}HFT3Bf zY|CxFwn5}W6tTcq(40=!nHY&X<32vrbX*nJ}j_!tYh8Has32G!7rW< zZ{IWJ?_>S)C92G-`5)thUh_ud^oHW+3+?}DYaHyqw%+)=^H$HdOh{QqY^gXgswGd8>_kzcfxx8l?911p0s{6Cz}_|r{VznD}D-UcNt1?@M$_Z&Qlwo%HmF zd>#L<_cgM&uIV}~OZ}R>-B>!16Y5;fnFf1i-MxBNU8wcnt=R|c>)12$*pdpp{fyOJ zj!2znuK&*a;vd5n>FrF6-g38H-zfi-YtRmx`S~Y>od}Ckt8r9PHT}w{b|8JVe znXzP-HS5l4%C5yv9gg*%b06Hb?OZ42=lUoye?N0=rtFsA?@gmZX8OpoH(g`4V0<7l`(N3I*9}4@ zt&;ua!pJ_MBa^a#vwhXH)``wPIHU4c=lL~VXNlM z*Gtv2%D#%nd#QN!^hej7VpTqR_0rnp>v3=VusQa zm(;7Kaajj2E!)s+u=!rurp0Yb3Z8E;cFCH^(z$#-vsJF&%=z5ndJOCLvDJKe7*e)z ze$L$5S-H!VOFlLBS={F7)UmW1TIYX)S#|x=F?Z>0c9-ZB?MWfQ{*s(_2_sp`g z=T2G8;oSM|nRn-zBhuS%mWn?5w0G*X&-npoQuf`DW;nNcT1Z#O0v4Y{Ri!A`67GA4 z^`uN0x^g$~P+QpFt7gQ%^mE&6f$Qnz=3mOooHx54Fxe$BSuY`T?)>vJo(k)<)d?Ta zW0+eOvp~rfsG^D@s%Isybg$@2`0pNuUT{Uj8e>5R^GDcXn7?e&lLG> zTb6ALTp!u-;?!2}^~XL1t=?69rd?rj(J6~}Y1We+Tu$&YTQEJC`h)?RVY5 zJD61yx^4y>ldJ46{61&;BtOY8rLfa0P9N46VxKI;aCa(`nPl%txz&>DzXcoQSBumf z_0eDq_Hmt1%PIeK%RM%;YiEn*?*349;?aA(>$giU2v1w`+q-S6C+l1D!cPbGzWMY~ zq(Sa!SV#=NYC>1`M`OvyeroAC51NENwG^m+{MoSJc78oCN9&t)jK;#fcOze^`zr33 zTe#`^4bl5=ck{J&?)b|YHltf5J%_b`J-gg2!qnzcnahDqo8Ii}YE|6t?B};=!p-Q6 zH&-^z%R4G6lctrs>f7ed4NePLd=djr+_96i4x@%o4R zDY@alCeCGE6Em^k@X^>=57disD-Xa0&7b!7%JJzf5W4V!;I5_z_modx9BIgv;?%FGG+bZSrHbDp3-BmTy^^WFUopdT% zgYmOiWXXIF?iJ@Zeh4;XzID!e!TyhG7fO%*42!6f+mNTb<*oUxT2;T}?%r=U*Qot# zR47D!1PSEXlCS(&s+9~IdO#fCoqG8T_ZVgD`Hsj+{TA;o{A=~#-0j9k zU)I#kP@i;QOV7%0NxibuXWl7`pOW-YgYh$4-L=x%*2bKtdUKR-F@JZqFJ#(L!ujI$ z_TNS8`a7<@-M;ck?@z^(vitcGPG7bEuX=pzibLts7qgtXt8!PR-eG&<*1O_Qq7EfK znB33I%zU7~{?(pc1~0dLys`Lzt#xC-g?Fn9wWWS*{gX4Vs$ca>bCU{gQT<(&W{TkEGS?_-Pdg_e1ym!jDKO9M4ur_-3 zkKLs<=f7{8#L2&POW2;S`Gp(VcQQ_itkB+nCtLZs)9fEgOGpSENRttR!7n6 z&YUx>IfD1pby^=Q+O2;=>DbF-6Ir)@e06Bk?5)3qGkrY)MItu7t0U#{Yd zV2055P5Ump&FTJ9zTUVirJ-rFVws2K990TO7Tade%F$Rby5f3 z%EdSzIaD(z?{fWdyDqNwM_Xr~_wKNM%y_LLwDQg7u1!nNZ)TX6(&YAECi;)b#=hsb zc}_&}3xW^MZg0H1yRq@$&gFMb+iaR7>{Glk5Ymw`A{(AXW@aWIwHRnb6oH%EE zjyNW}*2g#Qo!~>MC8ytq?pl51|Fvxg!t4sws{e>i=AE|e%uy*Gr^%V;jIO0*%5JE! z+_09t>c4~24mY>P43UnH*M$=&={TLtI&-pZ+VuG|*yheo4iEffFc>C3-inYg5L{A?36RNiL4~N6%yW-1!h)-#-?B4oizx}&Llb;fIo7gjQ z_nx;epJj0=Y0moJdslOxK5rPTSK6iUNV(m|UGGHsX+P)m$V2`S(-PQ(YkUh?ji^L2lf>T{^<*~q$GDB1CiC)czkDeo7F zrnxp|w3zyQns@iONygDDB?gj@HMwu8zwDW~)zI>Z+p?#pr?gC+GvR@oBJ*oL`|}mA zzehYXlxAAB?2OfvhO=`O%BNUOR$@G}#f{&{N9&Q$V_}u8o{NN;W_9+c# zd*+)bvh^DsTN!3_bJ3jp+Ryoni!c3Nb(!h)^GEm2`w97$Y&ZF|T13PC5L2wV(K(-O zcWXApKV_cj759MSXVI>t%L|v^kD1W=kt4Q!cW}wMlsRwIb*BE{_+5VQO#0DY){CF+ z{FjdUb^p24hAEfli+`3px7_1-T(P{HX4u}6rSZ=S=gDzL@>k91oo#dF#PrMnCNtZ* zW1;Wnip*Fy)4AWGcYWZI!>eUd!rzRt@68OWY;Spgs`3arYZ-Oo{vrun#G1Q{mwnIa_9c#7?u3;4b!|Xo2aDh$n5Vn-eG;qZ-=}r z5!%2|{?Piys+a#vBF>08wY=-x8}9o5{LD;?``et$1fMhgQ{VHgzv%m`YgS8MUYI$r zw&QW&ye$i-BrZJiLN0sZ(f_G>S5&o5K6cI!`5tXK^KWF`yZ45s_PT2}mI!Vz$TqE; zmpg0S^h2vW78uX|!1TKOpFQ{Pj3r%l%YL5b`zp?G$5HTeSGU$Vt3A_iIR4e0uAi`X zi&o0zFVa1|m8Du+BwJo4Mx0$|V*en=_I$d|vhIf5#PFB7)A$^|unDqt{GV}jd7R=o z$Cs z)cV(K^V?7f?JvnHK&ou7F3%55Ho-HN9R6kqrhSe$a1y17LE z{?SDjnRe?Ja8+3_J@w_fE-0QVsW*kg`u2ZgbA|b7)#g+0d`z6gDW~))e&O?%o?<_% zx%9bi7=^8^_v^gAMdkQBo5QoK^~=A#beYrimud4ozM?6AdTV~ny%e)^?sc`V1;tIl6GBR;W1;j%NQW!fZ}6F>8R?D>Dh*)WKCJ!gdMW*+<3jg1q+gdQ?! zJoaI%oM;%Xd~H*VB7Ar8X)2OWn17(N4aTjs1%3oHxof;#SUB`%KJDa{J%h zYd`LuFZ0$4eElouNytmXsHAxs72moVT6b{&o+7k9b0X^plWf<2cD%7|T={~FtG{l~ zm|55Kb)MIeb?1A1p6xjFCiJ>`R;BBKBQF2;TkeW6G;O~b_0m0VN2Ms2(TTp_Z)Ny) z-+RYhWKe!7$@X|B!@6T{nVvm9(P+Qu=~AYhmx~)UuY2tgiBmqs(to-vzT@!6&6Pi| zN2=V*{?4k)vYk7^F5T|#fm`gM&rNNuXPhm&^wxZxQz36Ek8;k2-%c`p$7&j*GuQr` zcvpOjz#W~0ooB>;o7fB7Uc0_Rr|-oJ7Bx5Z1+N>PF+1IKWI4o`VdtKat-8A=K4Jfs z67R!d6~gkH-QE}ZOy{%Z^XXw0R{qn*#$wdyV6oBQM)&;Qv+~J@E(LD?=3BD5;pa>v z{Yca9;MyX==k53DH4hZcw%N{m#T}rb^MK9lPi)p{x3VN2iFXZlH@JFA z7#G_*q%h2o@a>q*D9(kYKPZs5c`?wnvlja_XkY5m=uFzo5lOn=&Q}n{;tcIql1qGTDqPd^wHmUu8 z=700f)tsor^&8fmE8S~yS0jP(-kSd9t7cc|NF7c%Zm{e8w*^1hq`7KxUXj7Co$)rlXCdLEuRE#9*@r_|7_HcvO~-hL6+udN)ZtZVybw%#auU0fo+ zqq1y@lI_JCn}cE!M5ldsx$^kJ;=>N-BOm-Z?eKUr=j+ov$9^|FyWY^9&!D=xv8{dK z^}F1<%egp~E!qDiR;MJdD7t96G;5A^Z2PqM-5u9A%FjKvx_$%q-|1`u{gIPDU#MIp zt^fC1w8hUak$1jKJsa`&gi%Ye?)sx^63(C4pXx2An#5%;_)P81XHCtRJ|3p!bu4pg zgkEf0QR7v1Ea`0Nd^V~1%y~aT7yPTsKKFG!=cTC{VK;WKv{)Fq!}ow!MrH1{Pbwcf zEq3{pK7MO|vwQOSd50tZ&q#e_Dp}~g?~nZbV@qq_?)Vv==Wue9lD7Zyi?c+(ADmyk zbZd`Nu1DU1HR9{v1z-67-}J&Z^@$4-O+RWBJXQH}cR&A@ee5~^xVEebTD@63KV9a{ zj@j3kwsYR-712Na=GBV~5#QZWAMV|kaky+f-QbdO+SQeIE;%-B`@ESgel<^)cqeu2 zZ2aA(qqkSDwVHcO;oWk}BSjZ-|K+>hdX#?uaJ!E0-N|nb_5HoT|Ih!yr-v?S51Ssd zZ+3CXSs`ojeapk7E9QTgwmcGTxNo%gTw3F2M)SzTJfRH?>1J!Uy;|1W=lkGJ_SqZ1 z`P&QsolWcEU$W1{ssE+hp^hWRRAm2eyT3>9>g-c%y>^OUceGu5D{lMG{i2!Ptmd1y zPMe!pBd|*)Q|8ZYmrX}=CYzjbIT9q~HY=Xpg!k7h^#xONH+UT}S+{bsWQu?g-?UQ}F+FS69$=}D9 zUNlwCxO;ADgXW!vHLTV<8I9+k5GWB`yZFA(4&JxxzRSO7pB2YkqxfQ$eAG|3)pt6t z#w4%4S#<8>4$lK+8v-s)n!W9DhB#l{a|@~X{7tcYCb_gNw)i*M_{7>X$G*i+nGvD* z$I*Mz+PtT})p1u_HD&x}ylyZpELiz|l1gu|J&WGn-2D}*f`Yq0a0#4hlsnfR7Lk86 z(kZ8BUm)jPq!Y z&X(|Ji8f5l(Y&1cty^0}E$Ce0)@Rj8CAJrRA2%&IX+M8g#q}J?*M1R6Gmjp)b)GrV z`&2@BLGgz<(sw%Y1UDqB~#PUhRVUR$ptB*`nId)Kp&X;B(*jzFyWWDqwA0 z#mtNIPBCpWtXR$B^Dsq9V!5Hb=dB$-!kqRbox647U(Y{ohs&!kbcZ`d*DM!ZcZ6%( zj8o-@zvOS{xM9{cedn@vzQeIQUi*JvwEg7GnTs!Ndv%8Ud0AH5Nz1b_w!F zetg8$>Urhz*;G5(-@-?aq={-oAD#O6K<9g=P;)6g*U29*o|+nQ<96Eq9XEG)9{6;& zE>Lj&-v0$0w!VKmTxIfgrKcr5Z8Ms(UUTcBryU>0DTanq{Fs3x2W!y7Fw7qxzV!vDu3 zJw-g)MSh2nL91}252LZ=6oK}-mvddrE0?S0`LrH+U3H!@_dT{LQ_OOJJ`$O*;!`+e33$Cx2xZ|PNN3TowjT*iko-47(re}}W<-hkm8G|+EbeMi$ zUb5$f)hrFEGTvPm=Lb%2nf%H3!Wq#G3eu|Gv!BnIsh?x!C%!?!ILT-2-$@@ejP|dt z^j|$wf3}d?v7{HduMR&=jt%382t9QyNJFG}`K_+s!TW1CBwl%J$uANX;+|Znm%O)A zai(4H_aBjKwg)q9V`y6AZ4i1u<=NHRb>hylhtu}uoHBUU_4fUNP1Uc?)V&mXw?~9a zJ?ZRU&719y&z*DHr)OkPyn^>dz7boBQpziv^Gg~mrIvF?G~_Q@dEN4gWz)CC?{v$h zUP(C~@6>jixcOw^1{=9La}h(WOI-@@&+1%`3w-5pRJ8o*jEi=swwA~>#%+?CwUcR@ zf8T3CqMkx&D_D`E^v-__RmXUBVbZ}E+g*W7NpzKb@yo{`+^|GZZu&VPp90mi63eaCp2!cw+1 zxVkTrcvnH2#0 z0@1U|XO8hQO-oQ#~S}K3x**6H@9m7>o&@Lzf$Mh zs=Id0AqeZ${1q@;jsIZC2 zmhNxp{F?fQZ`VxId$Xj!9Q-J*anI;-d|>&x8S`5cN-mqr&;QBY>a;Q8&$K(!uh}@S ze473!_1)@Sio!QvGO6V!9XaFge(78-)L*B(H7b4$oxm~vUTuM znfeKP1$_V7mU#EDPcCe3m>DPcp8u!7IZms*il;FjSZhz5Ft=qEykoG#VtK^FKsTRw z?v#tu`NW=`zv894Fvh{SRx_L_jmb(pICyPSLYP3LYUDaGgZc~YYZmcn$5owq$!jB; zc~ZbU{F&mRsi9mMdi+1rrm3tkDDK>yxMtF2R-c*;Z{M^_7`=8qY^nGp?1{wNC3m_y z3N8kD@3PZm47Qlacv(Cop5erL@0wEMomU+Wd^x)&N6hMMUEt=zU+U^i*RskC3^!{q zALV5|W+7@Wbb>=+xeoWt)@f6+dyBTO4zTESyv*vOvwn?Gq(H+(J?)^2!Oo7h0tVZU z&3^wib#wfcoI45$xoO-HWrbp1;UcqtPSR9eZE<-=U7_-!l zL>>AXwns=Jr!d_)eqoB}mI*IaqXktHxZSt8Oh3b?=JjSpPO9OdO2x(N7u-Iv_JrHh zHY4r0syVY;8(w~7YfVa2xy$=(w&&B?4d3?ai5T#5JZE&(77CtuHnr!WwEN={#>s0I zUrtMx_x!UWp>W>CwwI5pbJO;1dHRD%iYx1Lz}}U2x~5OJcr3=i&uqc-z@~X8S8FVb z1oM8u16h})r8{^+a@zDd1)A5s)5@AA)UfHRgCFZ@Mjuxv&fc{BD{k&^Jm7b+R=|Ao z1jF4b+}iJ=ySl4y#f1J;d3Siz)uS4D8e1McJ<9*lkRhAlkH~>wnT+epTYM%arMOOu zjbd&%&R}z>arLw1JYwq^xTG5HxhWVW{5l=o77^=h&osljSMtlDFPevRBG(f9h6k$9E;EQP2K{%fH{x@`3H| z!)4P=G(@gHNPiqRllg~6Lwu5Y)$5|Ws@xmP*e?_dy|7BVndx)X!$Grk+ZQ`+mYf;c z>mI8IS;QWgB=dYx)-82W4)%Z@D)QNO+hpA9E4U^mKJOR)-u`#R-i=b;`D=ZTzI?Lx z9?Ju1hI_lR=KeKoD3|aE*I2WN>A-Q#nAO3hLAfXWUQ91zyt{m9aM>z9>w2T^M%8ll zS2xZqZ=WZA{IM9LHN%5AzU?NxJ$nrD{%(&h{I|BdqwkCLJ+A1gXx(XdVtNDmUjBPG zbHb0q_n9hq4{X;=S-f%|N9%II`9ItlYuFB?om=WHd+F?=Z($483+|{fSh{?kaZ>G5 zeYUXLQ>%@z4r%+%Qor|?iZJJ%)2%U(tesii9-6ddn`_XqFDdg0{r)>kjqGArMcBcfi| z;MoH;cGgmdzswhwG~BEynWp!4`=g_0kLfa9JJayU%XoQoYw^dYl$yS#Q}@z2=0C$S%e?OFJy(wsXw5qAr`#wf$xC zI`43i#LL3j2Dhx+y5DcL$_tD5d+un&_s*#rQBfJ~UrTivxn}GrzRnbJP^Eu+#OCLF zWE&27p5V*5dA0i4x7FXz%>6U#eTQVpEdHpEUN0uyG{3^Or9R=i=-=!mu_0G`9S$=) ztV=EYkhY6o_CC9yo{+)Ay{nJcZkjiHueY#w&dMxjlJrFLmrR4SM#}pQb!Wc3)8x9YoPAd4o_0Coj@jooZRU9D zwE7NLY}YLJYGFNB^#|N)T^c?oc^IK0{bPnEpxt@q|Lbli3Ff*caU1AwSvV|D>x2UY&QC>>~VG`n%f1 z1NQ^X`U_a|gbYq@xWSyD-H;nL$3aL{Wufv6!2`GJKiO63e^D0m-P_SwF?Frp!Ef=m zTGra{SA6<>zB*%_UUr7&f8_@89|1cK$u2Yym7iR%vipM~!~PS!eX7oH&bXb{Yv1>4 zbLX74fLZQzBQ~kSSk9F)wj#PihSEP&Svqs!TKOcDMMy`Q{f3x13%T94mfy{lag@wC;TT`Zl6)|7RPW%?h)p%}AM2`|FkR5B6~J z4g6C&mdXmPzQ_5%-ss`+i+U}u7q%8R`tDEZ(Z2ZoujPemG0nAG1wU%V^4|H^exYlc zx8jPc>34knr)L+;ZrHj(?TwB0S6#&!k}h}GZvFAZT>en?|Hlk{{~o9u3z{%@M)b_) zuk#~1rS0~MrLMknTX%j+@#c@U4El^es^7hvW0&pI#GM*bGWSrG*fgts))t$&^O-&z zUCsT-cG8zz*Z+s6Y^qS75Ib|j5xspAXNmlN!V;2#}1R)X|Mkqs-0R|c6^`1 z9km2upSyK4^*FtiW=?vk&Rc&vY^ubx3zMR=3Vu|p7ry*!rW|}R$H~%BXMg$1i=rtP zeM3*IKV^F9cWhsN(BB)%XCi7k|BCWk7sTxRa`(2!#k;3w^alUnHH)x25c`Rdy(@CU zp|6E$VtPq8TYqz=Icmo;*M45lG&Q>6vp&mki-x1x^oOCBFz9@8z& zn$xoE-RCvx8~4xO~R@^)F{X*%+b&<+W%Dg4UT@8z# zUhuxoeAS$}_IYDtTEYfiiI+@D&X@V~C#-SuJa$5ly(n8KcOlnwb+%xy>Af3XdR_}& z_<3jbq?zsOx`THI9ypM+`?;iwU*xPfzszZc9+Qq1JU7sr|KiwGW7`+G?~WH-?@&y- zxRLXSlHrTOyBjtw$*wUlPFQmAdDxQ$1%>kzTZ{rN*K1u*I^UqodD%AnbZdp=W^ew@ zohzm~g&irW*F3tTwR^p4^Hm+S+0qT7J;t7jKW~d{cI$4{Iq*G)x%G8mp3fcG=d-x& z_Fwb~U%gNB;XCP^)_c`@7Y{n8b@j$DE;{Ux!_#qGsQr)p%BPb=Dg^a256uprykU!q zqqEED>7SljIK6&t$q*h?TV!0Yx}o>t-!ErZ|6X)I{eeZQUbn-E{y(#99#3G(P&AS` z=sbKioT#ZK@@*lQc_InpIOeaZ2)Yj#dJy6Bqk8NI)H-B)Kb@XWZAZ0M1l z$e!+f_uQAf;Lf9uJO019u+TW!_s+$-`TTbC+2((|x+U7wVfFK3o!AMjFC?QV9!&nIo!wI1v7ujTKIIMaoY|dRWq||>^t~6uy0rxLD6F?sEHzBiF*G8WAZ~%eD0niyr8h>v8L^ zcDU}Q_W>8qtdRe_arMrWHUF3!4JQGTo4uL?0BW1+g;sCFW&H<@BD>+9&6U$d7~2&I%UDGMLbtUm#$c;bNp3m>57#)u4%`bN*#~< zy3Sa`av*+VP2$45$ikgt98T(?n<>yQvCcRGm!poy% zHF3j%Bfn-XU^Sau(I|H3zy$+0y`G1Mj&7^pc%X?B^ zT3Zh7{ULdI&GOq>#?e8w^Yu2)6iYl=RMYTo%h%$$aSBQ*OqXxmX>vY(?!kkP#xLHz zJ$&E-N1MBwp_yG?$5p2Z#T^W8>nA?h_s?-l?7Kq`h0`a@p4@xsk<*%}Z7j2|tvPV& z*U=Lv&lx?+-0YBSmcYt>a9V-F%h#JZuC>pQ`DD1QVb$|xPL<|+8n0CSHNN}RZ}rhR z#&H4$2E4y-^BCEma`kzb*VOZRO2PO2ef)P{i)?y5(>nU+&-#{Yhu2(wQhy>pc6VD0&B_&f;UY0eB00O7!TV-Mwv}eiUGmU&mV|t5mtorV$D)kP)>r?x z@Xr-8$e-ZzDdJSlZZiW)XU%$3Yobd7)^7+QWTTi~@HssdloKZ0GSe|NPI!%m(&HVuZ>?*X*!$)g{j)QCc*f64d2#Zwt@HL4aI*K!J@l+pyYTR? zscjRcP8B)lcEUt#Lb|b<#=1ksf9}oA3EkQ?ZPuaLF(t{{dtZK-)u0=AZTj<*sY%DI z)Wkjh-VlD{W0<_h`%a_K`CSDYMc==(?@Euq-Ma4|O}M3T7KB9 zl&oLy<%#8-%sn0M?{EI5Th7E*sf6p$$uU z9xR{i?UJtg*ZYp|`saM!Z`aI}(%%|-|JTFKH>!WI+_KswGch(pbkd8$H(x51kEy>q zdR+caXVac3y-y053;PyX9<6wpn;CXrEP-cD+xzC%A15{FKEJcFBy!L3nS8%v%ar>K zkN$~U=CXU^>0Ox}Q&qRd`2YKo`sQA_gl#Re+veEy##Ux|SNb|<@b>pS4DP?vd}$HW z;>Yf@E}uE4chc%m4IkTI9rJy0QQXU&7B}dIF+H=H#PqCGZ3fqGIqvHD>B)~J+NDpu z`@~`VWt&m1)!GTJdp53QJ-=rc_xe4XuSc9ozx?c^wu`zDr`#j6sc%lTzuzm`t#$hH z#ibhOUF?@f>_7O+QBzhTP4Zx6+LOD7)0b-S>TJ?FcF3l2I!n&XbhjDt^DSCmvB;m8 z9(l%kr>u`WyUV;K9kvJD)@a{2e(v+Nl^xTM>{OBa@+rF7=B?2jzLz_1T)y2uf6ouL zM^!TXZ}+fzXKDAI`;qA|C-#T`r^w1_t+yvze|FDk{bM?RW%Z9WYo55|t#4D(n$lp| zuXAmC#o34R=ghd}xjNfCnsM*>*e4++pPt`KTlGcVqH)Ke)4Y*Kb2VD`o!_^MTYt~T zbrZAZojY+>nd#r-=kexTbC;&&c=|5AXmMlli#a#$c^#az&C~z9fw=CevkPpZ%{Nc{ z!WX6GDi@_ZS>$8+=?5{c;g6^CrwblEt^KUN$8eou*Tu~>68>itVkS?F%`k7T+jqv& zuix+0u?JJn-*b2rE%dqS-PYKOpL4%oj@zTAtanu2?!OchqrKd|=Vn(zrp&Soxvc48 z$??_TIm^F}@BecomwDIeOMBR#nm1u-O`Mb5##2pauIQ!5T)0}1D!6*N=rhqJ7oW*} zTl3pceqAd|;Vp}zXPIT0|9N!7&R?*%XnAk@mn*LBcWuw|mA4D5jvZh8iKX1SD zo0k?o(>o$1Ii72zod2-!Pm}noXDPov{{MH+_{P@n6DDq0xWmp_d2z+dwYD7TEgz07 z{(gR0a{J}Zi>H3EnO8r5_F&)i#|K%i=61$kYCEVKzCDN74`#=4alWP_``>ONP*YC#5)Xo;zpG)bt*}ltT zUggi+J@p@UOw^k1ceAb7_1^40?YnIk!lzDsaPRi^+qIsbRnMh+R9V)w&uo=7KCn1zHYoP=9I)`pIX(&lliPSWld?YVtwnrzNggO@T#62*KaNpJKUQg`A?wcpTdH(ha9dxzK|^V!tnXF z)lz0wWhReahTn55O_khaY{JqVyX2r4)?zM?)CpS zd0tzxSVBU^?AXFRAK%rTu9p7ABzZz3nlZjYY?fTVqScMYkMG$m$hWM0q=GGm*aI!#wm*SQlSIhd1qUZN~^6QfN{E9>T zx^l=So_!C#wC$7YPu}xe=h4JOm)~=(%RCi>>WZH=98W`)SEm{OYPV{f zQ5^hhsrUbf!s#1-H!JV>-}m;Ij-l`7+J2#>*}rWbpRPa3XB@{};Aimf?n#yjA?uGO z>ht_B-QB<%aX#hvd&5IpV%HlT6ns*D;MT1BDccsU^=(>u_)q6%@ehAq?+&iI_wn>{ zfir7c>^BSi>v_p;|ND?7hv-$sm`e>GPpb!&^?rVOa(Vo*S5LhIX3T%xvTT3SwmSI( z_XCgY>FM~Ix$AS#-nP_=M6(&US8ySEAze(N`XrxA)vN{X?y_%x?z(ij{qFC+1xr>i zbg$CN+07I6=l)*5`%deHQa`_4Tk>-0Bm-Uh@W*A0+!wZ{PG>DU*!KCBx#8!I2(eoim^33_KYS%} zg>}*!(eJ_x@&YWQ&WxCt7&*Zgj-7}l0TW{-6tGBIBSo2t->E~6BCsWS;_$;~f zwBXy_9Tg=K9}9P9<)=T33FbaGC;!+q-qjOk=T;@pHeA;GKKc6h>o4_u($#**sYuH# zF=912U{N;h=6qu1e&!FX%eO`>Dbv|pB%7RZPPbs1+X->KtqXT9mCX*nGW+8s ztNs+*>&0^v>TdOje`h#3(f#nBVl8JyyQsvaTbSln2A^w~&8l>a{7_<+@@; zJ?|7FSIm2$a8@WS;%xMZBefMTen`f=+obueRQAYJi(6F!4=1H^JiG4joqd7t=0-kI z9iuukvrFFLi;OdOUCHy=61TbLrPJMxjh^Q_RWklCKVG&jMnhM{a>KTBZ9XpYtZQ_S zJ~!pv&K=qP>)XtNXSMZoZRKQ?ekW$E7dqWiU2kjrv6r94dzGFCWH_GdW#kL`Tgx?lb@jVQ zANJ%|WgIhP6(uegY;It+U^>F*Hl^Y035ja$e_RdG41bgw-hRGWB4b&}cXw}Xhh^@m zOyt zVTJuCUFO@V#u;DoWArwyQ%(3L_`rhMS|a%4dP&c3n_nm0y)pAp~AJtJ1{NZ~FIkXwyXFWs~Y*}*bXG~4jbpLHxhf~wYTUbwxU z;rZ1z$T7R|k} zv92KFNJs0*Ws26>r#AS^d3W&r#%Zr*>-K4RCcnF(nc(Vm;LSllZkT83o-py;myrZ;euN*^Fnie_g!v$C0IIi9pq4LH^6? zb5ze=?BqP$d*jx{)~DXfW$vuL{Pk;mHF~^v{js@A@PwvoK^s$gp?O-!8nd)^)vUvFram_Sbrf2Bl@p}; z<@o!oJWWrvTKM15NvK#u(h(E{b$XN-k6|QbSr50;MbuFHI zs6wUdboj3HUxtzP)4b-VswQwJ9Z+Mu9CWeew$~kUpPiKYAkXVy3ATNH0~`1a`S42d@fD!#8szUAZjvBLd%&s_hMzbn$2 z+LQkE+ z{rD*~GW3QiqA^VLwKU100VJipm`ou_zrL>`#J`Yi9y<t?0X!^u-j;kR*4q2Yih|W|g?kSzeQu2GPpP=E(fATym%QhW& zGTSmQjzP95R!ky?=kScC*{o;$xP3Tp9Qx>geUFLUby-8JRK*iFL=SUmuQER3$?Yr8 zKlx|U$+%9x7ZLii+b>=$eBu*(X3m_G%XK!iCN5`P7Smz2@Mg*1T|x%IGq&+K>1^28 zv741gWaGqX`~oMmTnzS$&8q*!lrFftbE2H`?7)BP=Snb{nWgP0Ud~aJ`G+TVH-lBn z64q(27EbdzkaHw`bHmOn+n-8IDs{Q;%AzCm(JifP%XZ0$_tX}*PO&fk_3v`ynxvMl z=}CKb3ksX5mvl7r`o2GQ^#xnmTc#HRkGu{%YoE>Pv#@}-nDc4ouI_M=R;AV*kq6!s zJl7U%mTf#r^AQ@dkrGUp$sn-CwkM-qo%1=g(&K*}n2u{Ol+DzO?n9mU|>)Ss(ky zPTt5VPuzevN}=bCtGUvISe~=ywdMn{D&J}weCo0 zGUJMH;hbT6Bv(X0HGwAx%#8c_uZpH-?7GsnBKnK>XT=Hey=PuYKj8MM@0R!)`jN9I=2my- z!P9QPHJ1zUwFU%68C~vowd6jdJHIdZyJBJ z+YAa`sUBH;uA|!5E$hRoQ(nt|PfU??=@#0+nmoPXWcrQg&m@)!ZTKk|#I!BpkP+X;nQo|1OFTjwMN9u z=->Cx(Z`~~XtTbu{KHM69M3|fr`;&=IJk-9ddBU<7ko1h*hm)2PZJYeu%_fx<>Ca! z&o{5cPF;R-bEa-z-~muMcZ#XlUMZoS@lMU&n+bMhO)FNfb-MjjkB86l{MEDtKPLq4 zlUjI4)$Q!{EibIp+*DqFkWg#e6;#?fa7 z=}n2#WeuUq_aeDoU)_w%`I{=;yDmQ9eyWszmANynee(+zlUs!%6W49-ds$jxtMYB@ zqPmYhx=Yr%&TIE)+QEAuI6VHDG{d*Z@~QI{pK@ai7MtOFf^YrpWyPnNqn%bC+A3ei zb>T-sS3#}ZfvjJ@pPyNF_4LPojmdq22K8=so5XUBtK^c}-1a&>Ic4dx&P2&9@A|y# zGM$wxO_KUPFPSp`YZ=3N<{$AoC1F0j^WV>w)pI%N^d>0t%Qu0iv+YwA*R=l7Olmdj zT5)-z#KW}=`xqQ%Fp=?n7$O|!I5KmRRMr)IwP(V`mV);HCw(jS$6G?tr_=xlu7 z!q{{7ojsi8n;TTE8Dk#$PB?Yn^|5dGLY6nq9j#j>54g{e+dnU9|AMroK@q$2LYe*M z?QRfGe{=T=JGe6@_%G5YLd@-Uzrv+z&Z;YM59fAEZimZT&u1UfMHOQ<@hm)a)OgvM(=$X~8rWYeYjM_+ zo#tf8DY#bp+PVXqcHNq3b>aN9CBG|JCzrn3IkQsuK;ug_!6ky5(sVav%E~PdeUr6E zAwjoi?Gh`FS&rKyb}cR}ymhK)%Wc&uNk{uOZQ8@KdiwQ;2QCI%)L(4t-D962qTTKt zE}XY&+-(j&#T&0Z|td4 z)!SB@kfgL#zS>~9%)?1fIi7@``?0wy=gtcg`&&g9GGw2oI2~ljySeRRhvBc9X?;s4 z&G=YbqSzA4LWMVq$-ynWGZaV+f?Xvc_IVzT)g9ld)m+xp)*eY|_0_rd+3 z9YLQ2PATn9{j2rHvVv!Nl>YKZQ`jc$m>9TGY~iC_lCe2%%YQzU<1v5VXlFQUQ}!JF zPxJ2WDC{*nVB|VwBge12Wq1GUHWaUUT@qL9@tWh3)>daw+l%4NwgXZ*7VGbd7&$%; zpP@H5C}m4m$+I6XY_~sLzwc-Bl)2`MYg7-Ze*Ms%F2^h`FYmVGdDBFz_iT25PuQ+l ztF`^suU%sD@0ZR`cAr+C;NEZC$0s(cd#>1wu0~6Sn`=#PZ^`C1I=$+~+rLNBIyt`C zuKB!kYInb`$DFj!yFw%OuVQrfi_#bR*Ta+&mgAsM{p?n$=ber(u}3~E-8(J*y3@pU zpJ!iNb70!n)e~nMI}0aePkjDDo8cXk!tP1(%o&?LHw8INSjy(gefGj!^~a}LGwcKV zN;(RzPdgQIC1u)GBL;TcH;1p^(0Z|X%Z_OVp>|g@_uD(GnRFlh@Ppa9aMwqrW6IZ# z%_y-=Q%UYAoj2oW`eWf467Sq{mR!G-9i?&U<^B~0^7HHeFp7viTMF{1{cAS!4JNKe zk@FW%sNCwKI+;_Z%qRP*W%v4FW(!Q$_C+tTCjre+py7E$UiH$S)Pahr&i zwDG=@^y_cL#GgJ^F2C2aRePF}#FoR&rLjhm{<_cHGPz$Lk9+?4aa+2&r1H6ERZ~PX zdZph9%H1?;Q<%hhrtIq^Q(ku+H*S@eZaEtq4{Va?$(s>!XU@UdVb2AZw^|%(H>m9R z5VuVJ*C!S2OL>Q4Y>xQu5I!KgS+a6c)}OlhiIz51|7A1sGRanag~hA&bM1OQNoDN`=QodPd(0!kyJqEzUFe!3HP?OTi-j-lO8%Xeb9=|Jcc*7^vzDaZcbNWY^BVc= ztrm^iN2dG_+GS?GDWP7rHD_NOXYc>(dh6Zn&CjoM=bT~8v0>Sod3q=9x^8~vNh^;O zaQ0SPD6-+}%M%k%KNbNuE)!$NKeIgyZ@@) zDZbsy!6NfiaGq|`%bJ{E^C=?;q*he$(gN4_mg=vf?~5xqnt#{MeH#ta%lCVj zUV8muBm28U&DQyDaqCuJPwX|kwZ6#U)%}1I>t<}(shx7|?Q;pK89P6muhn2ImRQT< z<5%&b{Lqq>+xv^J?_@u)eZ5`6dOe2|Mj4s4jk*iEa-^1}Te-4UR{VGuE!cnj8>|2I`rewWB!EaeQPzeHH{u6$T>d#6?)=E zgr#u*yrf%yr$+AcoAGgLzhUFbS?6b`&+E>9v1#Mqc`qwm4;*NEme zmzEsqhWdS3zH{e4Y>PQoW?O#g*Q!r9(*GN)oc62wZCh~tY}?b1&FS%nAAIPzC~G%Y zV4;?--jT=rZ;!;9=Q~zrP88JsqA}CQN$cS;AGXYWN3Q%{S!N)a^Y{x(>{o5gn0bLe zG&8;jO&8X`dvUpK%vSrEPdGlw)?_SQ`Obf4+#PF8K7j}8zLeQ5IBBT9uCMegTjsDj_tU$tWr|9AH`^v@={xD=IrB%xT@E~;b5K=e3Gd`r$0s>0 zD`YO$j&1vXxvMxe?6gfrFw--ia{&i}?gpLGYbq(P>D1i!DPZ5BiUhmh+We*2DN}h} z?`>1M<`!DnIq`;K)3)Eu$`<7hh1%Ta+?TCW^zc7C^{aQm^FG}j@4vl0adz9*;|pbf z>?qvbf6`NW=E>dtA2&+vdLW-H6da{teQ|1o<@B_&Egj1)Ce2-=zH_$bk4I5=PRnJ> zC#x;aezo(A+ppiAWsijW@8y5-tND1=)}rW>PStOA+vhiKfHH){_(TX z*Q#%(T1fv{^Kg>fJoC+-9~`4H`VC#}?s}YKD6SR%r4qO6uzO~0$>t!ty8;(~No5~( zl3NuSX)x(R;q|_?E7q<$V7z``v-$nM47PupJ6z`2s#b1mX>t@1fA+9(ci98G=jAsx zFc)h6t5&ng%-pjwdwE0UvX48av+(2?UY@s%sm=3ji|F;l$XMOFkDczljWg^kzJ-D& z^zKei;d&jee^^|;Uf^}uHD&F~pB2xwPSBe-b7jV-LjtEyR`?#6wd3%PgYUQlEheAo zG1!(H)9V`g^v2%z9Q%HMIlbf3<@76kJ3A8$8jfBS{c+~_J7cNOV&YFMSFYZ3Xr@VJ zBHN9WJ!%Q!d$%19I5R6QEy6FlMyqgRPl1H#)TcY%_q~nSTGOeRviqB?*)I*ZMd!-2 zz1(>-6KYOx>*Z8jx6VMw^qIl=)w4HF5Lz#?K|AfNtNzjb4-6SE-)JeDc~^XqhorF1 znVMOVY1uOpw-nrGnQ#VOrL9?!vUw4^ znO0A~o>*AAH1M`5`@>0VrZU&8Keu7thepqPT_q>(_?Az4y*SYK(%gHj1zleo%MG;s zSugB4>wD&!nD!lE#)S18Gjw9+r+2HYFMAZAvWBBjbl#JuKTO;^CNr*?EV9kJ#v?z~ z_d<`y!u(R#m4VjVQV(mzWbN(=em<*b@!Lym}{ z`PUvrx1IN}|JpIJ&@@kc!;_Sd%mY6I#lC6nIjJDOWAB^t@1I1Rd6%-^Gv0Ed?&GSZ zh5Fs$&YKG-cRFa7C)3k4XU%q@xPRWsZ{8+HZeUU`J zOV5-)H_s_@|M=^+y=OyVivS<{#HlkoFD+tHUKYB4hZL`O*2a>;uQk~_AL(dY`hWc8 z{cg|t(0iO|@BT@eGxc9oTK(M3U~X5<<<`_cbF3{M9*W%e$?{Pdqj%raf2n#wT=@$x zcfCmOc|ALP?W*f(7gv}b`sTR*=+3PD2jnU%i#O`IoLO=#>+UJG#{zBc$1jxa4`%rOd}3`}pF$`8xM@I(YqAwP%ZjsOr%bI{J=>CrLei z|Hv#xA@JQg{>{$6zo%PjXUqRTZ`LPN*`T!Sl+m8{BVG0L3@`G(**By0K^o)b8J=J7 zzx)=k+x5L*@`0cIj5Ymp!=5#9+`oS@={J}7vtO+-0cB}z53fyr9Cbu#^1@k*lLdP^ zTg5zixF)P|f5|m}rl8TKAI{g7?T&wN?$`V0hDOeM72>Tvk5c!at83)Sf0!Okn2>k5^TS(zqZFoL-UkFWv19BW9AP( zLpEL4nX%xsx7FMF?!q|>S@+x%-F5O_;`fj2H`7jp9MRSI8fkemvNJ5y(xKv{#%BNaA8~UZ6&#O$T)b?}M#-5EZeKrnd)@rL76Wyqsh>8~>P4IF z4Ud1;c6^QFPR*lhnWi0W_$>Z*MXIFqdm5Tl?M8H$Ls&GRtqnhoXg{#|n2{ z{T3iGKkd>X=MvpnPC*NTUT}37B|eQj#?pV$D>fs>`FZvhqrhh->AAMYbmFJ3%~+Kf3vO=A+u`<4or*ZCK zJ|k1pyr^c@p`5bSf0ulaUm{x5;~e^aA-9%q{tXS?HQyt9FPx|iQ=gpTuI6jHOU_Qp z;i%7~wa@k?SgRkHvh0;OXMyhDBTL@IhCg&;Je}%g-&h^bZf9|) zb8>oh;m=bqwltOMe%jpZ9QZh6mq`XkVxXf$HGgU1>0d!_ZbUVnl|B^^H)*C{Smc~V zo7%fS&R*j$x#7p2!y*Qem3;vQ*NfVw$ZX}yX|p-Q`b?ztiNN;1;z0#@bDzI^P+c}> zYx4FDEvmj9A9HWGFMg_!KmVX4%j>8|f9#t}EHZr7-95bYt=hHe(<6TDmQYz9X>)4R zl)P2zvkMkCtX=XuedAR1i4tuQp~wBdKl*xiZN;~#X)=~Kdm9~#*W?r$mzDNEyt+Ml zLv_COH`!N5{+8<m?#pu6_Z6@toB`3X3*^4pvnO0rb`A2MglS>{~8F~Nu`E9>n{#!-i*}G0Z zzHsO@M~$UcT$&%zoX&*LqXic3*7nDN?VWF?q@U^Y3rkE?7N# zYryWzM%#x9jK8Cgy`5uJSTkkW@yvBE;+MRfpcDFGM)tJ6-$GuxfMtiKq|9MBZ(7G1-51`?r3EJ0~1p*RA&3(Ubi;F6zd* z1G!Awnw1afI=eivwzq5k|LOVfi?`?h>g_NozEQnFSaxo8@0LIH|4+{js^b+?O!``X z%bNd##cJdG|CegMnxq=NN!5I3`JIA;cTFy5zu**@#5(D&&B?pbo4(t9*DIgA`{vyz zY2TNL3mp>jVQt73SmRkUSATwKKf6#+@7KAS`!g%+E1v}( za!%_rF@9cu@B5zj|7*LJeq^5$*OQ>F7^Yq4qFCK?RPI*U?H>z{yl&4?y;7@Nt=F_C z?~l~&*!9d4)_-Pwel+BjRHG-;wByMgX4`)zt4Dv>y|ntnlS_BQ;-^nu!F7DT{npFB z-uK%X+`HnTQx&~;O26&0Z?ieXUAUta$~K;O6?m;v+Jm+A^>)1#hQ(9bmz2&n|qG+__{r|da!fIw*?dp@}i;-}jX;yP=;fdEihj_Z? zrL4bkcUM=6ab&M`=foMW4LuK}eSUZP+ny>5nS-abPkp#`yFH+8iJ;>0tKAxctAFf~ z++S0r&hw@_*W!ey)%D?|NowH)ptJ@bA42>dbg}EYlHr>`kWcNzt7zz z@wv{t_bk^(nIB5rcKMe#iZ0uvRJe&_#@>{V{aL&J_9tEbrnP6wBA(sfuSbdnQeE6>a&8wO!`ij@u=0Di`Ja5|F^A}1b-pVT0<-|V@ zy6eV%`VG5*q-o&sZl1)+Rc&p@DsCIP9&p?I{&bkWnP}zKZKY8w53jXeeesgi|AVdK zOgE9*vZ+IAChKEpYpf+COP~Pq%qIw)(}ow&YVm@UdSXRC9`Ey;%3N z_SL=DbB*23?CcHBnOQSeO={ch<4-o;+C4>a{%S+l1J5@6Id)ArT0vk_)!wi@)i+kz zl^B0rZuQ~XE3VZie@;_f`oSvOTD0@yWy=Gbemvdtyy&%ge8If1Rn4pXeofe0RxHuG z)cUr)SjwY>-e-3D)&buW^EYaJEZRNarnOmrd1u4T<012Sa-1!5H3EOUd|$p`+rGz( zau&`>dVFni#Qv8o&(6s-{QE0o%=Z3?TS-UniT#gi-A}A!vcT4|8)};-h zTzpH!{Dbz~nPFu!*+6&2yu^zOj=!^y6ujKA?zjkJ_qIj1-P{#=oZV+{sJWV0vg^aW z*D)+ii8Y&^s$8q9ktx4>XZ6Ogy^gN2r!``B&xvZ8AYf9=a%OhP=O0#=yZFBJ?>(ur zTyycptQ%F|W2KmG@0u!p;m)nEJ7N#m*zodf*v%4d%Bf$y`i{iMu1(eTdsUOR$J_J7 z<;NK7@%jkc`ODjwUlHf-bTq!Ldu`L6ck_O@F)+HtAN%s&sJp&vA4C7GxeHDuNNrr- z{pR$!&fBbKjtkFRyUj!^He&IcMc2Y^eVtPHI3$?i_bI6j+4Z(+{nzXM%j#aLn6CcR zSLZ^(;l8GVc)yrY6xrgog7oE6A4eZb42KW*x+>X+MFFy4l8+z zLpk?$uK4nNTkI{R-(g{5bsqV<`75==9tu5jm3H4MlNPdVw(o_@U#8s6H(BuMLyXG| zo|^|JRYkMv%*}KGt;h;~t-Y~&?$*+`H_9*Ek-NPsSk|KAhlmxE;*y`a``_Plm3||t zSDYd~slUuEv05VS$xZEqL^JmH#z%jxXD>4d*m_m@;`vtsAd@}^9LTx5H1uc9Pr>E9 zR{FA`y(?TIc#p`0aOz(x7K`4a#p{l- zFluj#J}@h)$s#cIQzv&u?)xJX7yDYV==e@g+@$cODd+du2J1||B(Ik>{voeEocy-) zwCdA4J7Nz6Rm{xDo*ow+t)BJj?%O?uc1;HjvZS{}Oig{UX_r__+=S%)4Xrnt^M8GP z=PBzvCAe}%scD|L!Hw9)6#;E~mx&d)1pdwL?Tzy5vlF)4;dE(Y-v2t$v|krWglCG$ zYF-Et{&Mgwx6i|sr?+wVOkKFzQ1w-!(W>cdjh8%qB4M~;UW7-DNsXE^=L9>JdiLA@ zZ8X_lH2)UQ*nTkG%j>(c>DJp%?}qHEXV;xp_>je75?81DiGMNk1*^ZXChQhZdLz7B zYWx0b!D~Bjd2rS23t6(fve!?!%0~0VsV%m@ujdc_P>5B=rZBEns!<`L)Wp~Lh|e$AC-@iQ2h!WJwJ zS#X(Uf3PsinQzfyqC3C5pWgUb-S@NIZ*H%a?(Qu;A(`fhLaQf~nB0~ZtjPSbSaq_X zdspd|*v^KV4j~6}d}W_J{b9I6#eKQ# z^=htsGuK%beEiC-r4P&aSF>+6Sa+Y@M__~0ZdRTB{!(sJp56L6rBK!Uc>luY;*vX! ztr`i>ZcX9n^s;?@G*`r6s#VBu2hHAepR*@=wpYg}X(X)se!F;g18b&E_UpN48V;SQ z=ux`Z=2h<}6}{l`N@P=#U$+%?=pLL*_KHM12bz&LV~SOG35@HzQ6k+FiXm=SEQGi44)Fxi(WTE;Ez47W+lSlPY59#4 zc;-A{SYP(+e2z=!K_3Qd#+tnM!MFXi&VMd{-Ldb0^BX={{~U8Q&KtE1Y&R=E1*N|D ze#b~if4zotaxsJM+IO~SC_o;7}4ra*_UfrMLE4!jC;cD>LyZh&Ly|?+8{^m|} z`t{)3=UO(h?)dm>;S7T)@1Hgm9tFQI1`C{$P`jyZ*3Lt_Ki10wRqwFZ&yH+^3&kg+NGF2Ta2sPp@> z;uUj?`}~V!KFjY&7J0X?lEsTHC9+k0R>gejrrG<}HWlbu?c`ta_krK<`Ej|O%8YlI z9bz?Y-cQ=Qrz4f2{MP#0^$X{&bXI=1KVe3LY_e3LS@ii_*Sqb$vv2V8-!nb7$}Q&K zyTmJ2BAqKWR@W!0veMeCh8cXn!tr2va?fUC^v-Ab*qOKxl^ z5C7)N@*(_7P{r}YcGIg&)%xAzzwCj7NR&W-cav8vTe_*t6+GLPu4*|Njuy!Q&G zPDzG;!rI^Nr{_A`OcuFVsQ>ER>(xCxTYKvQ+i&vT;lFcV#M?*6pkQ(6&zqKW9;I1l z*72wL={oJ-Cwbz$_u)^W?S(5A3ChYUzJ0#_j*Pq;@9XC1=~JR!`dt0KbRx&|zc%8* z4>dW|1kyGhENb)8wBp-x_uyTgJkwR*KiRU??|Lo0X!0WW&--4#X883=ZqG_S*7~x_ zpHCxas3q|B97@}KFVx$!c~`cSsSbbr|LvF8cDy;h|6X5h>eq}69tDXl{|go=?VFP$ z|Lm;Jwl2?gkp>>eS4=VHEE29bnIN5~Sh;dhgRNWZ#YxHU(_F6qe8Mn@B%3XYWzu^qEgoK^nwHU%*$}OEW^Wov#>%4~#X?uP6 z(p~+rUHn_p_kUF_hrY3C3m+4CFq_~10B<<2t7rY~_Ku+-q-I$P!`so5xw? z_Vdm;eW%)NI1=m{!WsWquRiT6(O!nA(b2<0ize_{4Rw?x!YAVs$k))6p8Pv7Ym@VSJwX$+v zzB~K=+WMaB++0m;SN6Wu&+wcmI{$BuK)3Z3?>d$Q)`qVH&6WN8s+i*;@_ruS45sX{V{RsL)Sz{wlshFhV%XA$=@5Ra_)zA zH@Evg^hgz6m3{ldx_76}{g7F{v~rDPoHP5=gfks>VTo;@jAbI`v@WWf8MbpzRjz@6UCUN?&zAjso?`XyK>9S!_ zgq~XO>u%TZDGSyuWajOa&B)`7s_V*qJNE^T>w~XL6pRmN+ZJv5`(pCz?f?Fta@nDv zA3J^d!=EdjUi|3$^0$x>gKCogg?LT#x=M3-Rv%k8#u+RRyz2J)87gHzRX$U1Ke=C~ zDCc~zwF(~Dcx>WvyN-^UN`vnBT9Q)Z^ZM8}=YAN^C!%L?a zeu{ek#fjyh+m&a()usN3-L~KOGsq*=b>XywoA)lA{FlMoSLaU0(G*5z=Lh$V>-Ntx z+^OAXu9vDVExI%_(!*rY(b&&-6%*E}zB2pIw?3igSP3Kdf%|MvI#&Xrdg>baFB{gb_{ zWB(|3v%(BNJ?q~0385_)jO7ioL!W(ddZQ4?!n0=0Z)N2TnST1mj;`2!s6BG??^?0L z98H}S5`}-Y`PbFXjhk@#XG8CO>A7(ou_}`qwr<{a?sVbO%X8xIMquNWQEN@TGfoTQGKgypSeLW{`hVzmQqREW@pH}TInQ^(h%kicO4`YEY zZ}|1ilD3iW4MGopDrI>0yrj!S^WHBfvm+hhdoOoyoa`cOaAZeFv;3=-eCv;|U9xHR zr>c9hd?}r^l|rT#)jK8QP6q$cxuu*T%Ur-IQjq(&LL+$c$?Jx>aWB6-deqUi;TZFd z*0jdUCDB`?{j9Z(r&jDeeQ0fqk{~P3pP>Dx>e|_VI{Hr!z7^@Be3f;Nbnw)e^_%bP zJNxoz<`L(>fcDu(_xzE6b#2*PiK3ZO^2a-Q7Hg(NM@&dQBeUFFVe6FW^`yC7Ab8qcGmtlGTCF{-^0?KPk|HSNh{iUg7 z(g`LuH$g4kvOTZ6|K8sjDf&Y0aFW5QuGfs`bn7R5@BGSHvZ+7s4}ahupZAhJ&t)&L zEbEMWGKKMR%+cxt-kbA873;g#ZSUNd{*|q?VrANqufdmN&GcGQej4N?mVC7EKH+Y; zS3&&o^CPbVeS-h;^9h;89tpYiK`pPPn74b!Id{F)AFtfK;lHD?)N1dFxJCEYAB=53 zTc2{u;Py*a$%a*G|3tOJCNDZ}9Ld6Sr=|WN*E;?3xqj~^>K_#kIiv zH0nHYPN{YFBbR&Pz0c+@n$=c&=H=13?I!nT*36!rxL@Vxp5+^z-s|lvw}1LVX8Upx z_RkXvnWLq=o&?-f-fGjZ|E%2lyJz}l%zL&!W70$JT0N(ns<7qLWkt5k-1A`mgSLx0 zddjV{lj_ZW^d&$3T=i|%j$WQwVymmq@BX=e^2%RVjvsqo{nPTFhzcu-Lt%c48J`VCMW9(kqWn6UaL$~Stz?&9gj9(TyJI``SzjmyGx%FQp zpRmmV{n?Y9o+W-=(q*%6(ve4^s#7nXx&L9`6IVW?5~Hh6`Xy_NnD489G<>6W#CnaJ z*n^ObBL9RLBG=Ah&JW=Xp5-?@#^t^Skor)6YjlnqA}r z*Y7frSG2SG>DXiUN#gE|FLggEPX4pceEV7D{V}bI^DmC5MV_(S*1l7d>+X|HwGN@n zZO^Z9-sAZ+y+&oV2zaoRD^_*Crt+ zdCAvK=l4?$XKYEda_sQEp#Oo_~tMJYkte@3R8we9n)pGz~6 zL=0~pd)_C0v1=2f$q8A{z{=U@1WKhYcL~q1nxiuRrT&rQN=vn-U3-7|i}9#2lclMC^BC_3-ZzB5ch!kvAYCl|gxnz8F{{egv5 z?ztkHGUnehzhRxSYkQ(ifZzqCef;(tekx2m^}UACw_H7N&m|sly?{M+*T3$q>2`bA z#4W&BEG{aOE@XT4tbxVdNnV1podjne=wy(7u^=w*u*eIZZ~S);cwOA_Z>`cqjw|)6 zm_jYCeY9NFd^LmP+G5QsE#C^Pn*fms~mX#Mftw4ovTNs;{5&qdV}y>+UO4 zcD#SBspArLi23MmGn@1+nKfE-P0~L7HQPI1d)BAl6LLA;fAEVb;8aR@_gKSqP4mSY zI?GagM2!lRyYsR>zgWG)a9PLFmqJ}!drI#1xQa96vBz)0kS*a43T`XP zWNE(_+vs5W%3At@RGy>3o!X@j{l6-op4`in&)D^+Rbqx6$6=$k#U`~9ZA#;LZ&X9tlf5S!Z@5+9*+wu~_~1 z48JvtINzpm@?)&3qAIsP6@9546eOW3hU-)zPmbNI}hgLVr1ZGQ{ zKV00_zv1B(^$bqa=!p^iEe*F4bhXxGFVD02pm0CD;bvucg5y6XpR+UD5+x6>S)pTV zs`tB|@%;+1&HeA4+&3)woNwQ8sFYWpmGjDJ(M1s})&ADAZLoBFzDBa2V@9>*(J8IF zq<;&V|GUE?VECap-0AW){f(71m1hEuiA9Bc6%nnd_*AbWH#<#3$SF8oxNPg2BVDha zR%}jFJ#~xk+dE4^C*KWH+S${h`#B?p3*0YG->~F;sl-&vQ(ZSqmWu~#SpA>M$1fT$ zz`EQZR{HJZs3TqHBRN%~0-bbLRm~UOT0J*z!Ubccgm-EOek)y_@w>h<|KjnNt~Win z?3=?s7dCz8)t{@L8Z2=+V%^+h=YoHPKGpwlr*o-BzV`J`QoEzd60a|ejfneeC8t-( z@||~w{LXtey6Z)qrT+JA{;~btN_Ne5SIO_2SMK-ten@6XpY8nrDKjS52PrY7u^pf3 zwI=0?(TSI=nTsZ<|8CvG9dkHt(^;QFS7nw^gM!`fj+V4oe~A2CR`z5I%ddw2%1+cDTry`z#+UF6A!*SK86{<`+$mxkEdQKQ`|I+UL7R8J zC_9g_0HblUfccmBpfhp_N5dCAOq``<{A^w-r*yvk)7D>s%YwK5`}g%=)tYtH^8dp= zCcnwP+kP$JwD|qvn=S@IM>_i+M}+?F3%&bet>XQIhwQi~KD?Oq+M@sJBD-B)XCB!; zb&%8gB)nk4i<%W*zRJ(oW{a`Zx~1XZCYMBndp*a`)|?p8AG+4To4at$7u?COe}2 zSis`T-Y+NKIv2Gk{=3|1=qJ~5apfD$91%%@Zm*?7i~d>Y zKWqJ5u;eYrwDA3npSBq-pI#KV%{2Gu{H%M&=Iq(2;b&a>FY`{N_yPY6m9_h4`gcex zC3Lz@SS%M-bnmh*gPgB`ylbw`>bk3&+PGY*visK0-&P=9W>Vkw{Z-gQ?W6pbdu7ap zldf!?a+5Qa^Qunf;Ui8gIv+VUdwmoZD-S8~cy@zDB0o|4Rou#rn+5Z)u5k%HAoG~@ zY2E+2x79ngekjX!=`Ijk{$kUAo3$rTEj?vmD9Gp?V`Z`K3CrS5*_nr|BJO?9V|vJ7 zW|nuL@mg~R|8~p0bGY5^UVfWP0_52iWi#Dqj{o)DUC zQ2g)IoDzQjMW;;47l~9dtbG`EIySD?*^t+?Jg4;UZ>5C#gd9EtM8QptY-fYcA8GWC{j*-5mYqqzgRR!#EVFTG6t!ljzfew#1aelkNU^H;gMkL{nu z*c5xYv+8y=7TRrRjwLBZn+!S9(MfRBc1EC zl#AIg_2aAJo$+@}S~QdroOCX-y>Qmq9X_?YII?X1L=RWFRh>Ikue<{3WJxq>oh4}Y zLvB6$p*`1coy*A9UCsUah^KRhD~pfCWP{7b`GuE%Oes&E%+QmPb+IB^YwOF|^QwYb zHRe7{P9U!B%v?Ipy?t9R^>fkJTLEiKEn=~144Y4N=hpxzdU*5L(fA${l(!wf@-goq&dI) zlyxJ2N7qqD_n+rocC>iAvG}~uWz2J%saJAp!Qx}E^c0qRo4+uW9S*P5yq3qb6|Bq%VW;nC>T=1NcB$n$T>sP(1vgh2dkL!G< zKCXT=H|>na*(1EQ`A1f($2%6UI61>)k(WH*hwsN1{Wrxl1ByPlPPI zsW!REu%m(VqtcL1BwKmFEGA5+Hjr)Kv~oT;qi?`WEQnx$xu zW!T4KsW&G-{<$geV%GBmC$i2>4Q^JspL**;PPXtt3zq{d>KsOQOIrNoD@8WHe(}Sw zOK@gbh}6aTkB-N>eqXd`FVoZ9r@P`WiMgfr9qVm1j}Gl9R5_$H*Hk8bliwU8mgRzs z!gm~$gL_Wx_?pge%Fr_7zq;iX44POlmtCIizE5XDBbRk=Slz6+Ew7IbzoSmWwmjCQW;P z`uN?y@3*q#cwbᷢnKQFnXaBm}65~IwZ#jQ4*TJY|?B6fBt!X?e@iyq6-VRPd zgM+y(5i$$pME7w2?zuSA>gWBlNdmV6_H}T77Eet7%ooDCD&S_}m2=Ct^G>yjx%A!2 z=ltA-tA7P_Uf&!W_@3)t=E)yI2d{QCaOSDM3c2TC{IIi(d&Z&@@hMA%`q}#)_pVCK z;4;1WS7`5|esg*AZ@W3eMPquI;v3gZzv;2OCw5nygTNaX79WGoyFEhddI}G2?>%`l zxWN74+!dTx5`6z=ZE3&#wyx9LX=iIpYyoec?w+$ayedLPN zl+Y{s$N%2?WPL@Vvhn5Na+O+*8EqUh5~SXKxfT45fBTCnLDLUGJFn``{lQdM_hos- z(pRZ-c;;OATD06yDOu&6bb-B`*1uJ%afLH>?CH@w@}qrL(*)hn+) zy_C5w`^D3E{(oZ2p8Fm5R*yfVzWmzag~8`d0~fBpu*zu9+82C2=Xc5+%{;p6Y}I0A z|2UQcpXc11O&~uAuUuaFCMwE5)3I~LFaJz8%};wOC1%BS@%Bn@SXZZOW}4?zy5ZEc z>zaPr2j}eBIpco1UD}g#M>@_-Jn{9}txtEJFYHbIY@KqYGeJoyp{cXw#p&g<^3pZx zqwg+`oD$u>iY?@kk!m#;sL68gn(dAIyW~aRx(CRvaQwjZ{$z+@+bmZzmAxX#o7|Ff zo>lmqo}0E*=Xb^Lh(nU>Q$nvikNx#iTGBvsMi8_OS#DG79^s9-1g;zwqaJt=0XrZL4>w%Pux@ zkK}jFUcsu)tX{b0@eb2jOI5G*daQ~&Owf$Gl za`DGSvu2vrTzc8c;gm1PC@jQi>i;G_@=Rf}cTZbZGg! zI*x6@np^syVM|xv;JVhf6Gm#T26BQ1ih%*OKKaZi79YLBvRwR$(Y6AI7gpcuIh#I$ zbhf<8u4-N!CdU}?>Pu5h)27apHAE--CKm`?B{hj>sYx*TTA-Qmqm|UBp0un@40p1 zgcBhBeYXzOAKa~=niMSbqgeh)%2RdA1wlu=Ju-y8cI$9#oRd_#XqRvm!-3vY3HM&Q z-s~}YUfa2OZ-{@)WRUwnx%s3MZ>{ia%dV$p48l`S?zznUn)M<_wz0N;=#^53v}(?i zO&l{O_-rZIygqH>;n12H5lbbW+`Y#xvW|a-+SHwKPL~vz)KsKT@o$hhP{ - + Date: Sat, 4 Apr 2026 10:00:44 -0400 Subject: [PATCH 36/94] remove unneeded replace --- lib/widgets/chat_page/html/html.dart | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index cbd79ef..f9b74d2 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -49,14 +49,10 @@ class Html extends ConsumerWidget { return switch (element.localName) { "code" => element.parent?.localName == "pre" - ? element.outerHtml.contains("
") - ? Html( - """

${element.outerHtml.replaceAll("
", "\n")}
""", - ) - : CodeBlock( - element.text, - lang: element.className.replaceAll("language-", ""), - ) + ? CodeBlock( + element.text, + lang: element.className.replaceAll("language-", ""), + ) : null, "blockquote" => Quoted(Html(element.innerHtml)), From a9c4acaa74a50855d7db47aaa52faae5cd2875c4 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 10:29:56 -0400 Subject: [PATCH 37/94] smaller icon foreground for android --- .../drawable-hdpi/ic_launcher_foreground.png | Bin 8962 -> 6332 bytes .../drawable-mdpi/ic_launcher_foreground.png | Bin 5820 -> 4186 bytes .../drawable-xhdpi/ic_launcher_foreground.png | Bin 12014 -> 8605 bytes .../ic_launcher_foreground.png | Bin 18695 -> 13176 bytes .../ic_launcher_foreground.png | Bin 25605 -> 24335 bytes assets/smallerForeground.png | Bin 0 -> 14399 bytes assets/smallerForeground.svg | 126 ++++++++++++++++++ pubspec.yaml | 2 +- 8 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 assets/smallerForeground.png create mode 100644 assets/smallerForeground.svg diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png index 215264a434fa0e4a7cc1c50e9432bc868bcf88a3..86ebaa51c6946389a446c009450c9c447dd3342b 100644 GIT binary patch delta 6316 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 literal 8962 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 O7(8A5T-G@yGywn?=PX_T diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png index 9af265d3a5c0622a876eb108d9ab19b9247456c3..3c64f707b76717b82f752f701bd7ed7837e247b5 100644 GIT binary patch delta 4153 zcmdm^drM)0M?L=^PZ!6KiaBrRa#o1AiXH!NJlR^niD!a9Ujduacg5$ggaXAr9BH`D z^?I+;DX!IvVi*gjDo7Nvx<27}wW(p3b`v8@<5M+3RW8OPK7)xZEfXe8NM1L0)}DD^ ze0Ei|C{K0eJ+_7~FtTXhQ z)-rvVbKv8;FI6lDwll~v1zh6zS}g4)en$SleunxxubpmwRJmoru+-?vS!Ipb4b!tb zOV>&~b8c_cvNiZ4mT{~7^Z7oO4b!4#DFtpyILMH8>)y<>2K#plMeJo;v1*mwv!|@Z zZ&VTvzP`Z1R;0R|A%gRP$Kory8Z|;U#4$XJl&j$=Kd>iJ_`^N>x89OG2cGFR?3WiX zpKrZt;~#at1NAWs>ZxnDN(W3zn9FG6rn`jS_D7bMR)gsK&iI|F(jQLcC@3^o7qRa( zVyt0&5X@j-`A2#Oqbf(4a6>A?za(`b{?GZA4xKF@!^@bTeSUd?=f(4iW!*;^ep)x& zOqsBu{_>sZ+V7X9ux2M_S{G-`$x@6oGI02`WonpXQ2I^&M5g)%iS7<*-SerO4fkKg zBui$jYT{kKZq;7xR?~+M4Zgo+Uh+>TW54^!|6oJxmKW#oa%~ zY@&MbMzep(LHA$s4dx7Q6zjF~JH9@eXPa z_GazKt|UM6)#tmXZOoOhyz83xdl~P7jV#%o{7t;S>=-^V+IR+XDEcszMebU&UH9=B z$9sj>q{=VbF6ey2v*y3Vh1ZOeIX`?Y-P1qsQE%&)UFR=-{kAmh$f5A4#`AJ+_4EIV zH5jBlW;n4-V(A;DQ@Rq>O>63_PG0KtjAFEj=&q1C5Y|}C&R~;Z_<8%qy{i-NE-)z0 z$ayl^>m5_^qkq2dnw6ayYhD(dNf7+tEVTR1(f5}Uo`|dww!N{ibVnNh);-=rsW&2u zXS*&l*JbJx%v3!&ZQ8~Qoq~mPUTtGKusEq+kA?r+n>2<5~!JhJJFX=)A7&{)KG zUFG883%diR*}mDx5X8VEWKqAnJMfqGm0JO{Isyg_lK-G;Yv3#MGJ*z}EQrF25yU#VGQiFYmrKB;*B zG3DtKfj5f#94DB3ZEA5eWpa{@8|rd@wBL{xyVxsSIOp!kxnC@| zJzLB9il_LAti!Yu8$?VyYc>B(yezkFu})L&VvVM&Z2L;D>7KAHm9(#`JGQPqpu4Pb z#-2PSlUY&!w4}wiMXcGrI3?HXYT?jS#dY^7`+{_3E&t4%K4`Du41-j{9Nl3Vk$ z%^4&NkALiXD>VPH*b{*@a~9nz-pL$ncBl0pzuV*Me{Wux{!;MXeqA=38sk4vSG)dx zlyvO-sLt@H@^h%wtUG_S8yMM|1zTN@8r0A9<;WCeiI2~o@li$fe$9#E1<@0VZK92Q zp2;%Y*)RH|)P1Y`F%Eey-|d_Y*WP`sRdwIu?05WU-MOwyueQ9X4KIJax^KhGQ{pGS z)b&29e&fjN@9Ta1*jnoXm8T{%GI-|tbu(VH{rF||NJKw+KslK_)=EsHbfY7f+GnRLnM|BtJkH) z$-o&A{%7i##f$Z<*E*bfxp{_Nxu%e?7;77g^;`{)W!GlZ`)P7*Ug4MIaoJ&v^wKap`Pl= z9|p{e&C+6i7aBXc`?-HO&>m2cVtVG-s;H%9vKFT}(-gKhFO``ixMQJ&Q~l>+z0{v3 zB0E{KbqjR1_kBOHSf-nst+`NjGh3^{H1UNdj~SY(dfgMI2Wt5K-xoG>{`pjge1ikG zPiLFn-Mo(D=$c#C9@~|==^bog>-oiZA+gon~=wCMX~UmrkEzq-q5eTIq^wpmc?DYSNZpf{S#zr02U~H6?8O%~7mc!Hd_v6& z>i-@pZ!&)=c(c*?b#+im+={xO77iJ00gGp4PlVQNtBsxO_v~%bB;M7}Q%-TbJIiNd zmAR)OL5k&jf9o}Mf$+(~iDn%Bk9+o(N_@fEQ_Ydvj3ESasRal-z}fj?l)!} zzqV_iU)}VCv=^%{FMsrN=f@M;JF;re`}~?@^6|>yYipyDo-Vq{!6V>vn}4~?@wpf zsl33>d4=Hh13Xh(TsI3`_0`yTo$a^dUDuN)*%~sY$6DV{zj^1Ffqd7E-Au3Z#C8Yt z{dCH9ZfZWZu6Mrl-?R4)U*lja?wUEDbraie_p$@B^%8CDYR4A1Hq_UPo$a>Ooiyp} zLgAZkPCT}{p+#S_2!6#{N8&F_dj#5QL{I`ds50IApEzMtEP}fp2gv>=^waLH)O70yv3WW z6ZJn&)^(kD5y!LB{?%#!zpp;3?N|Tk>{NS)_3J+_PI7ZkaCYLc+BQ8V=j5@e7D7G6 zalI+Fdg+?C@7;^DjSPS35YN7NL62C9L2??SuH_}pdX;RG^B2k#WQsGsrLx)|_{g)= z>CdA}!7J5^ILs!_zwzm%_;Z2nHao)>*0vXYxHG5uhgxCN1>1EK89xb1>L;ZhG4m0u z?~eZGvO4})0QZ}S-6wq5H#+cs6P~r9uGxCU(meu4xzjc;jP%I7cS(2=Yx%pgZdGB1 zyWF=u;*-n_GM<;V$d2IX-_+1Y6Tg)$EOX3XxMIihm7Xf1rMoTm$Oik*YMolqCFB3&=c^A7_THVak^TD{6dSXJ^W6GOw&e{3-tw$4}EG~T=42S z(~^06v%*i@j_$7iKCNe)+r!F=64efl*(WO14i_5l-TNqD4R5WkwM|^SCChG)oy|F? z6STSfJ~f-i4Vr!QZp9_*91ZPH`8(7)4kU)cEE z6Q8#o>lYEKU>IL#97w3t?cf3^ObW9`o6uL@#>IK$Ta_hW$e-lMgO?FdOURV*iGk~ zpIlic%B-6-J;LLe-IF`63ca5mKM(%?;#ALPrd?UcUQCB5-Bp_wR74v*P^K1oH2(_)wmx!aGGzi z{p>S$yc@&QSuWXb%Dm3VCpM#RvBlzuK6kxHiHT2UEDhYQ`Mv3<+ilM0vmExM3+Lu2 zec`?y^x>V{sZ)173Xihvi`Y5UGUD%TEg5Y)uDzc92OXUa_8#Ub;ok6gfF4nd_s(NvibiehHb(f~FFfaA-U;Hs^=49FHZ+4wm zKW~$#F6O>;C6oT#`JK$pd>{F(g;%%q^s~h6tDjuIU?u0Vjg2+iOPjQnWlyZ$({s1g zbJ27EX>&Go-8|`kN^fJ~oLyf1?z*p>gGG6s+}^OV-+t0Y!#JJD%_knz1aIB5%0~0x znJraCtUDQsbeq;2`$ih{{r&RFRqcz&@sr2;>gGOJ`?2kw&K$$%OFue=I83>qDIB@L zJ461MTE7s>V~={}{ckE~iOfBv`-A%m>!YQ9n+4|Y4f$~BWbSf>6Q}i7&GR|tJIRA9 zZnCRqQ=v#;*j2r*n25<{Vr8*CJ)d}TPH(?2+cfjr>1s8Ld&zd4OD(2oNE{dSlH8yt zo^;!F&3;S4%eUmHjK@*Gb#H9EaW-g%dEM!muI_MWZ#TuvxC zA6p##q*7=pcbJ#vo!1%v<23s&*2&4;k2^70Bi7);j57PywPGA^!Y252n!c7=S(mre zlH=G%)4hi@iq=hzcZ`G+!Xqn)~Id=wwxMP=sLczh+LG?=_R&>d- zaKGBx_~5HnxWk``O1@qDyA~JAM7!28tuYbIpyiK?!jr@!i0tfh8@9cMSTp*-2ZSQ|3jr7I2ybSjXIRCB^ z_!YK-!{tyG^N(w4k$x>q5%mlUWE;-by@_6^EWkNCb5p#dzd!rF|AII6vI~gKxwW;S z@9n~=#!t+64z$NaZRXy4z0PTYpjw)X`eNo^pF|Iw2s|KZ?r5MS^Nahuqx1)Z1)1;8 z<}rN8xM%b;?qTW8{TwET;tYQU%{P>;c%A!LZ2F}J^J5P7B^dv^AP&-^-9#RW?Q z)kGdLGPDIsec}51Ni^ZF|EF;K8kS%3>eV`8iue&r2xr z?`IBaW`($2u0=uIqw3uEeKu1VQY78;}y?^K9UO# z@|NX?x7SzQc3|LVw#fMIv~R%|ZFy8gcMTfP6o;kjWs+1#$K5w?>;!x#4Yt^T$9N@t6O#LVq7UzZ&HtF^_;@WTEW z2AxQ@ny`~0KaR#dVm6$3`ff18FO8nFz6^CwjrKb} z{C=}(Kc}{D`@#*|(w^AXZ#}*vk~`d^@?`c4l^5|0b;Z_YhWS4$=S}Nmy5KdPt?K)k z|0f>XtDNrq`e#p9&B?EIS|7t!oV#Bk-cVY3$Wx~G^z@3qJX8HDo9#KY-B)^gRO{Xh zR6p_{osnbWo@;pw`V$O{bBzH98h38wIV@>{Y$;*ly}TBYbv|G z3-Mp>*ZXI@o8#fUoAb0U1zzR8p{O%yZhd~z;G76` zNS;y_Ru0edzM0O~cviKPE@{8U93Eu6;p^kr8~c9==*`>J@?g`!6&KfN&9c8DadlRR zS=OS!dau{3Z?r!;#$mka3%7g2UG9b>YaQIQ1i}PY+>_d|P=Cv&wm{w_8+mpE?{Cjm zwfLF{75C@ZH16Ch_BbfXQUA!ot;aVcp3>j(_vy74e~zua&}8$Z`QDthw?%&k#t1C_ zx8)*Z?e&<1zx53==Hj<5$~VR@cY1Fk8Krwzx86kcTFK0Mz7+y$W~Yw5i=TIIY7hTz z-2+p(kF9E(xB7AWMrm0?!`NeYp4CM>{`Yx?lYN(P^PMYV>|fRzH}>Yw+J4H5In0i8 z?{ArmjsFYmrJO%s%bCfyLg3o%#fx|KwK9jznDn&E^s(8)Wxu5tc`^4deJFQJj9GEn zA!qID!F)`!?`_JPIh(b9O`Y7Y=GP0RetWPdJW-%*^KbFv+vXhVjDPBrk+dv#-dE{& zbKYz`&h>r&2aR{ZFT4vCjs)DewLRpI#ZK)d>Wu7ROkZtmyyTLz*+dUz?W`{>=$$2{ z_{~KBy57r{qf%POI^SJ0=ALsW#U-LrN^}{DBffj`4uLy!pr*%L;m9>Q#IN5elB@1+tFd$bWfo;))zKsH9ACjVx~56 zC35!EcfEc1EA=Kd(6Q8W&!Gjn6)huE+{#<0Y{-xZqWrijRTaq-K>mGaY zF1-I;wjhT&w^KaBJBM{`l=tSDQ=Ps0OG>3f4}B|)tU3N-fr3ZP9}dMQXBKR}SX#eZ z*}wev2}!l-Git=*|JQ#IHv6|r_&2|-b5zy8OCf3|EL$IEElR!2_Ttt$-bcx+xi9_^ z6jGM!`2YB8-UGYR)%tU!-KQlh(Y@y$L zF5ctl8vlJqHic$Socs2r<<3bHJ6memW#{*ct#kOjP1oSbL%S7`Yl|$357n<*&iYuB zGk?>5#k8v)SKZ1uy1!0b!@MQu1k3gvPHQy3e`^ABN|ujBnImmMyqgz1MH)MA<`8H!_&hntPVKfBrtEN$Td# z3*zzh{e{MMZF6^}CDlvYzZRb3W!0R{UjL=|?&FXGi30)*@x}2XiCk?`8(H)8xDKsJ zlA5HEnR@B(-d@iqr(%-@OSR=+M9kWx7yZZV=??+xmdev=0*52wol4kLRSa+2E!^^5=xydQf$D3s zFF%v0pA>4fN$S+GXa4_sjO!JYPe#q0|L>yHmb_~mrH`^seJM3#uIO3yG9}2wT9!`8PeyZz!IL{&!8&b5qR3V~sz00{1ObnkHbNH|tI0&FOy1OKNPI7OzrT6P2v8 z)|SWi^zN6C#a=QJMKJ3H>1wdd#0yWig4SW_9!WL4LcRMMD|ug z$F2Vk5m((#thgsKz1}}>^PY#B-e21(q{j9>m#xq1Xk^5J+cQ`KBP>;_4MPu|irnHE zwr8GXk%Zdbj)T(PbY^}9ylD(>%U`gV{ z-AhvW^ndCi#W9$~iDL#&=}eS(L_hQkxIbDE7bWFuK0ZvFK5e~Vw)(=A{B ziHjCqWqkO)fOWND;HOQih0;P#2J4J-Nx0vjVj=T!-EejN0SVr!c zknlFEtFOo5XHocL-zBUJo-^1s33y!%4CVA*s1R`K$&UTHKTIpnb39aqqn|dOO>@0H|4n>-*0?Qw&+EQ(Ti&jv*x&R^lqFV%>G4h<%xs0XS@ix zur%@brNTShiPOF=R8a_;aBS!8`;U@WFLb^w7P0qVefs6LK;CyPVOkP9Eobi7kUa59 zUHpDOnOi3M3C+4k?>wvXSu6E&G1J>zpK~j(ZQSI1BT;RMq~f%P%w=7fri;pdiftBd zv$@2cy*;ORhsPa3nNa2vXC2)>)x~(;>Ya01GswKQE}-66yjx=SLX~%tRnl)3%$uG( zNwtQn%uDp%w9a}3JgOzJ{}FxdnrYROYy+=XhT5I0+^(Mb@w}m~qWYP--0@98 z{txcx7f-3{C|G;v%8A<*Rtl?>bQm{uIXb4fJvhepZfc4C>WK&Bmh#*4}ged3f&kx{1vSva9ayj%J_ZW!3F3R=@a8VR%PD<8t|l=jv6jymtwD7qtt|ssHr5)d^H*I~o+}rLMpDxb$cG1bZd4u4Q zGeRj-wU$aPeW378(XPc$|8sEMk~D3N{-v^qzI}eVN%@eq^T}T~GG4pPT9*0n@B5cc zVP~ZhFI9iukb3)l5bx`&ork%bf7S2SYj*PN+jg-%qiSAl;|;kpsv5#K6n)kxi{@_m zEV?tMuWjN7&eBI&ZGO_WBDaOVS=Xc0m}(vZ_h8OtSffqoVig zH~rdbEM8^x_@~Q-^nNwoSJUz?Em-I5E*w^#ctUjB!4n+?AsXUOtLo>fYR9GCWGz_M z;c#`LNKMTezAf!Vo?Nl*+c!*_sCe%|{^WBX!`|MBSbVeers$>9>I zQ0{)0l0WwcRL z>2Idf_V2pNa+mNbKG&%9op#}6i{58V_tVnTW=wd>wNdikG1c#-KZ|Pi8%k@Qd@$pj zjgVnyn6%}a`rDu8Wv9Gb;uKN8_;bA6-jh>w)GuAy{PDnqt2eeU+@HMi3)9?(mk;k) zCc<74)*XH1=HIsOe|6_tK6OjksnpnMaJrYzE<~cG^2pq7+w5mCmYi=>lC120)bP)f zd=O}Ps8cp(n#ikJ;#x(Sv!V`sD4D#gt~(+%fK6LdqUy;4@8e(UR|`D*IB(thFxihc zHt$LNc%A($hxFkXota*FuD!x@oc7M#{j4otRDb)Cjt#f&oY%4arqxpE!OPCK`sXh` zIYZw3A4^*%J>?QMo|NTicG%;9!mF~d*QL7EFHWDHqP#4W zRi926xYy2+R_H_BpjZxyvmn7%D**5+AQZ}V#jm2C61}k!N zxI_<4+co{3@UhF^)wel6ub!@~e0leWj~V-`GAh(>?F!mKr?P?f1&_^9g_9 zT$n6Y>ax9X_JNA)o@!^CD*WmfH)rbjYO{(SDw-koW#{k4xVu@4fA)S)ywz^KvFdGM zyY9xE85>lOJ(yVc_2S;-IWuhob^gT}$#}{1eX38jWag@qb&-BI_so&IKJE@Yp=;Fl zKGoZ|G-zt*)oB*a`BL-)_De;-9*S{A} zJ6SGl`S{JQ69>DPq+fWbv$V{_Br@%$>)Q29g^35PPI&``S)Srde#T;w@b^KZfN^=?AM!aoun^wIG))V zPiXzI{PISQNp`1u)@*%#J)!WDQvDm>``t0I?rS$hy%4<2Tz;g)c|zKa6s7M;=_hWL zPB|zaenxori4RQa&lb!%f0C{1)!)R|8Y|X>oS4J^_<`-gZ<{4cUJ3r3Ic;*ABPa7l zmCC|{vzM(oczpS@1->QGXR>YNr#jip6JMy&`(&RQ+n(9IGppw?E;(h`Ru(WN|HWq4eN%d9Rc>OBQH0y-_DM@7h_2xxu=-GYl1ryqjCve!8ZRj=ZI;FpN zWA~yu_Ggk^pY$(%efL?~MO;nq!1R;x5%adImKv+cR7xMt-uvja?!!mD|H1=zw{<<% z-Mmv|<@C*5eZPLsjy3prqT*%oa+zv&{bRhQvr@Gz>JJFjy{_Ha5i9b%dd~NNhq69P z!z{PIskv79W{1lEThpez5jpt)%2xz)Vi(izcxE%^i%ez?+oGa9m~%u6#AbK2-n(?gRWG^J^zi&Y+@imK zmRy<^F(u?s%)}$Lc~+Zwxb|f3Ek64Hps?xuLuVepuiESJeTJMw%;UoP#m>tkmG&-a zziBDJ(X#VV?zt8wiTHA*Rc#?hXSW|y2-aUA#;SP2@yd-|j|04nOSJX~YosVLa7Oz) zE?~UtdxNw6D8KuDt?!9-k8IxT+9JI3#L=&*uNRg--IpPd_$u4>;E5Rxjtc~r8GEPh zop{|`Pv@ND9F?MsCtuxtk0?1W5Ugj5`v1FNn`Wp}apSolKJ7nuwmZ2i2(e9*fA1JS zE$T<%zNwEqXB}nYYxI(s@#;o{)oq1Nb88OiXW~~rPvM?5MTEsDF~R1(z`kvD41Xhg zW9!28?pocy_$O4wZuKFT1;WpcC_dBwKYRa5Ceb&aRe!9l-}WI~@r>wA)r(7>=>HMr oRQ&DzSSpdRExUzP;-CB~nLBLW;XCpf7#J8lUHx3vIVCg!0MdFig8%>k diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png index 536e844c8400b0792451c12f228e0ba98a36f91a..c2b441b266837d41cdf1ad13fd008eb29304147c 100644 GIT binary patch literal 8605 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 XFEiNRPi|mfU|{fc^>bP0l+XkKu%Sw0 literal 12014 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 Gp$PzYwmxV8 diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png index eba22d253bc84a76c399acd1e6b83add798f02a5..e472dab57b115d6b6800acb401c7b10854ef4721 100644 GIT binary patch literal 13176 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 BaxDM= literal 18695 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 diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png index 7d9fd324591b795f9c73ee5cd1892b5e9821cb07..64a515494a38349f79169ff7762592845afb33a2 100644 GIT binary patch literal 24335 zcmeAS@N?(olHy`uVBq!ia0y~yVB7%09Bd2>3@7{yv=|r|BuiW)N`mv#O3D+9QW?t2 z%k?tzvWt@w3sUv+i_&Mmvyoz8h}-Dt;uumf=gnUB8kz5v&;FkY)2U+T2orcSSHyGU z%D}U)V!OIib{;K!^FyyZ^PK9=-_MiO_x-5XEPG?om%H=iw5w~>L6Rlk?bd9j!I zHsAh(o6T6v-n=<;j^{(2{rtZTLY;RHCY8;ry#4vtqmLFWjsh$PbAP&1B3nlSo;P>23dw#j8~XfFb15p=Wa@1 z5HxV~=&ND4%y?!|fiIJRpo3up6GJ6~k!D=y`S2P2c?`J>Q4A(b7VHmn8YVOF1z*^e z^Y16iLDP-~&O=W>2CYbAn!%pHDQ_&18{5ZIr^{kcdEf^BF!&Z$7>`@KtitheVKxhjmy#c)G>8n#p&+N{Qik(9TfD z^T0jln*fU=i;qBE_b;70tPdVDgl$&6m;BimK)^9S$r$68UQw3e*=H6kl1g zll8_{>5sL)8y>4SwjERa*i{sA`n9akM$Xg&J(3s1bXlIAXZ;lX?wY^M|CF*IGlm

Kv>TSptFL=chaGznzHYQtJ=ZV(~Y~&QQ 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 literal 25605 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 P1_lOCS3j3^P6I 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 literal 0 HcmV?d00001 diff --git a/assets/smallerForeground.svg b/assets/smallerForeground.svg new file mode 100644 index 0000000..a821be9 --- /dev/null +++ b/assets/smallerForeground.svg @@ -0,0 +1,126 @@ + + + + diff --git a/pubspec.yaml b/pubspec.yaml index f7fe258..72bf939 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -73,7 +73,7 @@ flutter_launcher_icons: android: true image_path: assets/icon.png adaptive_icon_background: assets/background.png - adaptive_icon_foreground: assets/foreground.png + adaptive_icon_foreground: assets/smallerForeground.png remove_alpha_ios: true windows: generate: true \ No newline at end of file From 5796d250c70f2846236bbb0922db5d242b7b069b Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 11:07:04 -0400 Subject: [PATCH 38/94] add comment to html [skip ci] --- lib/widgets/chat_page/html/html.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index f9b74d2..f3b756f 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -90,6 +90,8 @@ class Html extends ConsumerWidget { ), ), ), + + // Allowed elements list ("del" || "h1" || "h2" || From d70c439278d587a923a7b8785b12790ed1fd003e Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 11:34:40 -0400 Subject: [PATCH 39/94] clickable user mentions --- .vscode/settings.json | 1 + lib/controllers/author_controller.dart | 14 ++---- lib/controllers/user_controller.dart | 40 +++++++++++++++ lib/helpers/extensions/get_localpart.dart | 3 ++ lib/widgets/chat_page/html/mention_chip.dart | 51 ++++++++++++++------ 5 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 lib/controllers/user_controller.dart create mode 100644 lib/helpers/extensions/get_localpart.dart diff --git a/.vscode/settings.json b/.vscode/settings.json index 25ea52b..8708bf5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "Appbar", "Displayname", "Homeserver", + "localpart", "prefs", "vodozemac" ] diff --git a/lib/controllers/author_controller.dart b/lib/controllers/author_controller.dart index 8b709d3..70b7343 100644 --- a/lib/controllers/author_controller.dart +++ b/lib/controllers/author_controller.dart @@ -1,10 +1,10 @@ import "dart:async"; -import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_state_controller.dart"; -import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/controllers/user_controller.dart"; +import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/models/membership.dart"; import "package:nexus/models/membership_status.dart"; @@ -15,11 +15,7 @@ class AuthorController extends AsyncNotifier { @override Future build() async { final member = await ref.watch( - MembersController.provider.selectAsync( - (value) => value.firstWhereOrNull( - (membership) => membership.userId == message.authorId, - ), - ), + UserController.provider(message.authorId).future, ); final pmp = message.metadata?["pmp"] == null @@ -39,9 +35,7 @@ class AuthorController extends AsyncNotifier { status: member?.status ?? MembershipStatus.leave, avatarUrl: pmp?.avatarUrl ?? member?.avatarUrl, displayName: - pmp?.displayName ?? - member?.displayName ?? - message.authorId.substring(1).split(":").first, + pmp?.displayName ?? member?.displayName ?? message.authorId.localpart, userId: message.authorId, ); } diff --git a/lib/controllers/user_controller.dart b/lib/controllers/user_controller.dart new file mode 100644 index 0000000..e7ca973 --- /dev/null +++ b/lib/controllers/user_controller.dart @@ -0,0 +1,40 @@ +import "dart:async"; +import "package:collection/collection.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/controllers/profile_controller.dart"; +import "package:nexus/helpers/extensions/get_localpart.dart"; +import "package:nexus/models/membership.dart"; +import "package:nexus/models/membership_status.dart"; + +class UserController extends AsyncNotifier { + final String userId; + UserController(this.userId); + + @override + Future build() async { + final member = await ref.watch( + MembersController.provider.selectAsync( + (value) => + value.firstWhereOrNull((membership) => membership.userId == userId), + ), + ); + + if (member != null) return member; + + final profile = await ref.watch(ProfileController.provider(userId).future); + return Membership( + status: MembershipStatus.leave, + avatarUrl: profile.avatarUrl == null + ? null + : Uri.tryParse(profile.avatarUrl!), + displayName: profile.displayName ?? userId.localpart, + userId: userId, + ); + } + + static final provider = + AsyncNotifierProvider.family( + UserController.new, + ); +} diff --git a/lib/helpers/extensions/get_localpart.dart b/lib/helpers/extensions/get_localpart.dart new file mode 100644 index 0000000..445351f --- /dev/null +++ b/lib/helpers/extensions/get_localpart.dart @@ -0,0 +1,3 @@ +extension GetLocalpart on String { + String get localpart => substring(1).split(":").first; +} diff --git a/lib/widgets/chat_page/html/mention_chip.dart b/lib/widgets/chat_page/html/mention_chip.dart index c2b832d..6e9b741 100644 --- a/lib/widgets/chat_page/html/mention_chip.dart +++ b/lib/widgets/chat_page/html/mention_chip.dart @@ -1,25 +1,44 @@ import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/user_controller.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; +import "package:nexus/helpers/extensions/show_user_popover.dart"; -class MentionChip extends StatelessWidget { +class MentionChip extends ConsumerWidget { final String label; const MentionChip(this.label, {super.key}); @override - Widget build(BuildContext context) => ActionChip( - label: Text( - label.mention ?? label, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onPrimary, + Widget build(BuildContext context, WidgetRef ref) { + final mention = label.mention; + final membership = + mention?.startsWith("@") == true || label.startsWith("@") == true + ? ref + .watch(UserController.provider(mention ?? label)) + .whenOrNull(data: (data) => data) + : null; + + return InkWell( + onTapUp: (details) { + if (membership != null) { + context.showUserPopover( + membership, + globalPosition: details.globalPosition, + ); + } + }, + child: Chip( + label: Text( + (membership == null ? null : "@${membership.displayName}") ?? + mention ?? + label, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onPrimary, + ), + ), + backgroundColor: Theme.of(context).colorScheme.primary, ), - ), - backgroundColor: Theme.of(context).colorScheme.primary, - onPressed: () => showDialog( - context: context, - builder: (_) => Dialog( - child: Text("TODO: Open room or join room dialog, or user popover"), - ), - ), - ); + ); + } } From 8154d41dc56d04680962ffa089e189f3f0385492 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 11:39:15 -0400 Subject: [PATCH 40/94] don't preview matrix.to urls --- lib/controllers/url_preview_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controllers/url_preview_controller.dart b/lib/controllers/url_preview_controller.dart index 119a845..e54efea 100644 --- a/lib/controllers/url_preview_controller.dart +++ b/lib/controllers/url_preview_controller.dart @@ -17,7 +17,7 @@ class UrlPreviewController extends AsyncNotifier { r'''https?://[^\s"'<>]+''', ).allMatches(message.text).firstOrNull?.group(0); - if (homeserver != null && link != null) { + if (homeserver != null && link != null && !link.contains("matrix.to")) { { final response = await get( Uri.parse(homeserver) From c3ca1e349166993632467aafff11eb028fe75a74 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 12:28:49 -0400 Subject: [PATCH 41/94] Add dialog for kick/ban --- lib/helpers/extensions/capitalized.dart | 4 + .../requests/set_membership_request.dart | 1 + lib/widgets/chat_page/user_popover.dart | 99 +++++++++++++------ 3 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 lib/helpers/extensions/capitalized.dart diff --git a/lib/helpers/extensions/capitalized.dart b/lib/helpers/extensions/capitalized.dart new file mode 100644 index 0000000..cbff0b9 --- /dev/null +++ b/lib/helpers/extensions/capitalized.dart @@ -0,0 +1,4 @@ +extension Capitalized on String { + String get capitalized => + "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; +} diff --git a/lib/models/requests/set_membership_request.dart b/lib/models/requests/set_membership_request.dart index 7384f5d..ae101e8 100644 --- a/lib/models/requests/set_membership_request.dart +++ b/lib/models/requests/set_membership_request.dart @@ -8,6 +8,7 @@ abstract class SetMembershipRequest with _$SetMembershipRequest { required String userId, required String roomId, + String? reason, @JsonKey(name: "action") required MembershipAction action, @Default(false) @JsonKey(name: "msc4293_redact_events") bool redact, }) = _SetMembershipRequest; diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index e8f4dea..2895eb4 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -1,16 +1,19 @@ import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/helpers/extensions/capitalized.dart"; import "package:nexus/models/membership.dart"; import "package:nexus/models/membership_status.dart"; import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/main.dart"; import "package:nexus/widgets/chat_page/expandable_image.dart"; +import "package:nexus/widgets/form_text_input.dart"; class UserPopover extends ConsumerWidget { final Membership member; @@ -25,6 +28,56 @@ class UserPopover extends ConsumerWidget { SelectedRoomController.provider.select((room) => room?.metadata?.id), ); + void showMembershipDialog(MembershipAction action) => showDialog( + context: context, + builder: (context) => HookBuilder( + builder: (context) { + final actionReasonController = useTextEditingController(); + return AlertDialog( + title: Text("${action.name.capitalized} ${member.userId}"), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Are you sure you want to ${action.name} ${member.userId}?", + ), + SizedBox(height: 12), + FormTextInput( + required: false, + capitalize: true, + controller: actionReasonController, + title: "Reason for ${action.name} (optional)", + ), + ], + ), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text("Cancel"), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + client + .setMembership( + SetMembershipRequest( + userId: member.userId, + roomId: roomId!, + action: action, + reason: actionReasonController.text, + ), + ) + .onError(showError); + }, + child: Text(action.name.capitalized), + ), + ], + ); + }, + ), + ); + return Column( spacing: 16, crossAxisAlignment: CrossAxisAlignment.stretch, @@ -79,38 +132,26 @@ class UserPopover extends ConsumerWidget { runSpacing: 8, children: [ FilledButton.icon(onPressed: null, label: Text("Message")), - FilledButton.icon( - onPressed: () => client - .setMembership( - SetMembershipRequest( - userId: member.userId, - roomId: roomId, - action: MembershipAction.kick, - ), - ) - .onError(showError), - label: Text("Kick"), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.colorScheme.error, - ), - foregroundColor: WidgetStatePropertyAll( - theme.colorScheme.onError, + if (member.status == MembershipStatus.join || + member.status == MembershipStatus.invite) + FilledButton.icon( + onPressed: () => showMembershipDialog(MembershipAction.kick), + label: Text("Kick"), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.error, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onError, + ), ), ), - ), ElevatedButton.icon( - onPressed: () => client - .setMembership( - SetMembershipRequest( - userId: member.userId, - roomId: roomId, - action: member.status == MembershipStatus.ban - ? MembershipAction.unban - : MembershipAction.ban, - ), - ) - .onError(showError), + onPressed: () => showMembershipDialog( + member.status == MembershipStatus.ban + ? MembershipAction.unban + : MembershipAction.ban, + ), label: Text( member.status == MembershipStatus.ban ? "Unban" : "Ban", ), From fa8b8ddd14585f7ad8230ee74c18ee9d6dc61ff1 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 12:31:17 -0400 Subject: [PATCH 42/94] remove capitalized extension in favor of intl's toBeginningOfSentenceCase --- lib/helpers/extensions/capitalized.dart | 4 ---- lib/widgets/chat_page/user_popover.dart | 8 +++++--- 2 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 lib/helpers/extensions/capitalized.dart diff --git a/lib/helpers/extensions/capitalized.dart b/lib/helpers/extensions/capitalized.dart deleted file mode 100644 index cbff0b9..0000000 --- a/lib/helpers/extensions/capitalized.dart +++ /dev/null @@ -1,4 +0,0 @@ -extension Capitalized on String { - String get capitalized => - "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; -} diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 2895eb4..7ae7a1c 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -1,12 +1,12 @@ import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:intl/intl.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/helpers/extensions/capitalized.dart"; import "package:nexus/models/membership.dart"; import "package:nexus/models/membership_status.dart"; import "package:nexus/models/requests/set_membership_request.dart"; @@ -34,7 +34,9 @@ class UserPopover extends ConsumerWidget { builder: (context) { final actionReasonController = useTextEditingController(); return AlertDialog( - title: Text("${action.name.capitalized} ${member.userId}"), + title: Text( + "${toBeginningOfSentenceCase(action.name)} ${member.userId}", + ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -70,7 +72,7 @@ class UserPopover extends ConsumerWidget { ) .onError(showError); }, - child: Text(action.name.capitalized), + child: Text(toBeginningOfSentenceCase(action.name)), ), ], ); From 20f0ce9fa568cb84f3169e40868dfd7880faf62a Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 13:33:24 -0400 Subject: [PATCH 43/94] Add filtering by ban type to member list --- lib/helpers/extensions/scheme_to_theme.dart | 4 --- lib/widgets/chat_page/member_list.dart | 33 +++++++++++++++++---- lib/widgets/chat_page/user_popover.dart | 20 +++++++++++-- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/lib/helpers/extensions/scheme_to_theme.dart b/lib/helpers/extensions/scheme_to_theme.dart index d106186..df68a05 100644 --- a/lib/helpers/extensions/scheme_to_theme.dart +++ b/lib/helpers/extensions/scheme_to_theme.dart @@ -12,10 +12,6 @@ extension SchemeToTheme on ColorScheme { backgroundColor: WidgetStatePropertyAll(primaryContainer), ), ), - chipTheme: ChipThemeData( - labelStyle: TextStyle(color: onPrimary), - color: WidgetStatePropertyAll(primary), - ), textTheme: ThemeData( fontFamilyFallback: ["sans", "emoji"], brightness: brightness, diff --git a/lib/widgets/chat_page/member_list.dart b/lib/widgets/chat_page/member_list.dart index 72ce744..8be1ddd 100644 --- a/lib/widgets/chat_page/member_list.dart +++ b/lib/widgets/chat_page/member_list.dart @@ -1,4 +1,5 @@ import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/members_by_type_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; @@ -6,24 +7,25 @@ import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/models/membership_status.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; -class MemberList extends ConsumerWidget { +class MemberList extends HookConsumerWidget { const MemberList({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { + final status = useState(MembershipStatus.join); final membersProvider = ref.watch( - MembersByTypeController.provider(MembershipStatus.join), + MembersByTypeController.provider(status.value), ); + return Drawer( shape: Border(), child: Column( + spacing: 8, children: [ AppBar( scrolledUnderElevation: 0, leading: Icon(Icons.people), - title: Text( - "Members ${membersProvider.when(data: (members) => "${members.length}", error: (_, _) => "", loading: () => "")}", - ), + title: Text("Members"), actionsPadding: EdgeInsets.only(right: 4), actions: [ if (Scaffold.of(context).hasEndDrawer) @@ -34,6 +36,27 @@ class MemberList extends ConsumerWidget { ), ], ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 8, + children: [ + FilterChip( + label: Text("Joined"), + onSelected: (value) => status.value = MembershipStatus.join, + selected: status.value == MembershipStatus.join, + ), + FilterChip( + label: Text("Invited"), + onSelected: (value) => status.value = MembershipStatus.invite, + selected: status.value == MembershipStatus.invite, + ), + FilterChip( + label: Text("Banned"), + onSelected: (value) => status.value = MembershipStatus.ban, + selected: status.value == MembershipStatus.ban, + ), + ], + ), membersProvider.betterWhen( data: (members) => Expanded( child: ListView( diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 7ae7a1c..2baae22 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -116,9 +116,25 @@ class UserPopover extends ConsumerWidget { for (final pronoun in profile.pronouns.where( (pronoun) => pronoun.language == "en", )) - Chip(label: Text(pronoun.summary)), + Chip( + label: Text(pronoun.summary), + labelStyle: TextStyle( + color: theme.colorScheme.onPrimary, + ), + color: WidgetStatePropertyAll( + theme.colorScheme.primary, + ), + ), if (profile.timezone != null) - Chip(label: Text(profile.timezone!)), + Chip( + label: Text(profile.timezone!), + labelStyle: TextStyle( + color: theme.colorScheme.onPrimary, + ), + color: WidgetStatePropertyAll( + theme.colorScheme.primary, + ), + ), ], ), ), From 7c1918857a7318802ee756ffa5baaa047fd3ffc2 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 13:37:07 -0400 Subject: [PATCH 44/94] Fix pointer on MentionChip --- lib/widgets/chat_page/html/mention_chip.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/widgets/chat_page/html/mention_chip.dart b/lib/widgets/chat_page/html/mention_chip.dart index 6e9b741..0f80617 100644 --- a/lib/widgets/chat_page/html/mention_chip.dart +++ b/lib/widgets/chat_page/html/mention_chip.dart @@ -27,17 +27,19 @@ class MentionChip extends ConsumerWidget { ); } }, - child: Chip( - label: Text( - (membership == null ? null : "@${membership.displayName}") ?? - mention ?? - label, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onPrimary, + child: IgnorePointer( + child: Chip( + label: Text( + (membership == null ? null : "@${membership.displayName}") ?? + mention ?? + label, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onPrimary, + ), ), + backgroundColor: Theme.of(context).colorScheme.primary, ), - backgroundColor: Theme.of(context).colorScheme.primary, ), ); } From 63535fb4621a4e4798e9cebd8e3bd43346a9679f Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 16:30:36 -0400 Subject: [PATCH 45/94] Fix embed link open on some configurations --- lib/widgets/chat_page/wrappers/text_message_wrapper.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart index d3329ed..d6f7aa8 100644 --- a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart @@ -105,6 +105,9 @@ class TextMessageWrapper extends ConsumerWidget { data: (preview) => preview == null ? SizedBox.shrink() : LinkPreview( + onTap: (url) => ref + .watch(LaunchHelper.provider) + .launchUrl(Uri.parse(url)), imageBuilder: (url) => Image( image: CachedNetworkImage( url, From f38715c8efba9a139c8e64f9b1770e8e880768f6 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 17:44:55 -0400 Subject: [PATCH 46/94] Add powerlevel checks --- lib/controllers/power_level_controller.dart | 48 ++++++++++++++++ lib/models/configs/power_level_config.dart | 16 ++++++ lib/models/requests/membership_action.dart | 4 ++ .../requests/set_membership_request.dart | 4 +- lib/widgets/chat_page/composer/chat_box.dart | 17 ++++-- lib/widgets/chat_page/user_popover.dart | 56 +++++++++++++------ 6 files changed, 121 insertions(+), 24 deletions(-) create mode 100644 lib/controllers/power_level_controller.dart create mode 100644 lib/models/configs/power_level_config.dart create mode 100644 lib/models/requests/membership_action.dart diff --git a/lib/controllers/power_level_controller.dart b/lib/controllers/power_level_controller.dart new file mode 100644 index 0000000..db57d4f --- /dev/null +++ b/lib/controllers/power_level_controller.dart @@ -0,0 +1,48 @@ +import "package:collection/collection.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/selected_room_controller.dart"; +import "package:nexus/models/configs/power_level_config.dart"; +import "package:nexus/models/requests/membership_action.dart"; + +class PowerLevelController extends Notifier { + final PowerLevelConfig config; + PowerLevelController(this.config); + + @override + bool build() { + final room = ref.watch(SelectedRoomController.provider); + final event = room?.events.firstWhereOrNull( + (event) => event.rowId == room.state["m.room.power_levels"]?[""], + ); + final user = ref.watch(ClientStateController.provider)?.userId; + if (event == null || user == null) return false; + + final users = (event.content["users"] as Map? ?? {}); + final events = (event.content["events"] as Map? ?? {}); + + final userLevel = users.containsKey(user) + ? (users[user] as int) + : (event.content["users_default"] as int? ?? 0); + + final requiredLevel = switch (config.action) { + MembershipAction.ban || + MembershipAction.unban => (event.content["ban"] as int? ?? 50), + MembershipAction.kick => (event.content["kick"] as int? ?? 50), + MembershipAction.invite => (event.content["invite"] as int? ?? 0), + null => + events.containsKey(config.eventType) + ? (events[config.eventType] as int) + : (config.isStateEvent + ? (event.content["state_default"] as int? ?? 50) + : (event.content["events_default"] as int? ?? 0)), + }; + + return userLevel >= requiredLevel; + } + + static final provider = NotifierProvider.autoDispose + .family( + PowerLevelController.new, + ); +} diff --git a/lib/models/configs/power_level_config.dart b/lib/models/configs/power_level_config.dart new file mode 100644 index 0000000..c051fed --- /dev/null +++ b/lib/models/configs/power_level_config.dart @@ -0,0 +1,16 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +import "package:nexus/models/requests/membership_action.dart"; +part "power_level_config.freezed.dart"; +part "power_level_config.g.dart"; + +@freezed +abstract class PowerLevelConfig with _$PowerLevelConfig { + const factory PowerLevelConfig({ + @Default(false) bool isStateEvent, + required String eventType, + MembershipAction? action, + }) = _PowerLevelConfig; + + factory PowerLevelConfig.fromJson(Map json) => + _$PowerLevelConfigFromJson(json); +} diff --git a/lib/models/requests/membership_action.dart b/lib/models/requests/membership_action.dart new file mode 100644 index 0000000..d852164 --- /dev/null +++ b/lib/models/requests/membership_action.dart @@ -0,0 +1,4 @@ +import "package:freezed_annotation/freezed_annotation.dart"; + +@JsonEnum() +enum MembershipAction { ban, kick, unban, invite } diff --git a/lib/models/requests/set_membership_request.dart b/lib/models/requests/set_membership_request.dart index ae101e8..dd0e1f2 100644 --- a/lib/models/requests/set_membership_request.dart +++ b/lib/models/requests/set_membership_request.dart @@ -1,4 +1,5 @@ import "package:freezed_annotation/freezed_annotation.dart"; +import "package:nexus/models/requests/membership_action.dart"; part "set_membership_request.freezed.dart"; part "set_membership_request.g.dart"; @@ -16,6 +17,3 @@ abstract class SetMembershipRequest with _$SetMembershipRequest { factory SetMembershipRequest.fromJson(Map json) => _$SetMembershipRequestFromJson(json); } - -@JsonEnum() -enum MembershipAction { ban, kick, unban, invite } diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index d1c9c61..3c2057e 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -5,6 +5,8 @@ import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:fluttertagger/fluttertagger.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:nexus/controllers/power_level_controller.dart"; +import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/relation_type.dart"; import "package:nexus/widgets/chat_page/composer/mention_overlay.dart"; import "package:nexus/widgets/chat_page/composer/relation_preview.dart"; @@ -42,6 +44,7 @@ class ChatBox extends HookConsumerWidget { } void send() { + if (controller.value.text.isEmpty) return; onSend( controller.value.formattedText, shouldMention: shouldMention.value, @@ -69,6 +72,12 @@ class ChatBox extends HookConsumerWidget { fontWeight: FontWeight.bold, ); + final canSendMessages = ref.watch( + PowerLevelController.provider( + PowerLevelConfig(eventType: "m.room.message"), + ), + ); + return Positioned( bottom: 0, left: 0, @@ -95,6 +104,7 @@ class ChatBox extends HookConsumerWidget { children: [ PopupMenuButton( tooltip: "Add media", + enabled: canSendMessages, itemBuilder: (context) => [ PopupMenuItem( child: ListTile( @@ -136,12 +146,11 @@ class ChatBox extends HookConsumerWidget { }, triggerCharacterAndStyles: {"@": style, "#": style}, builder: (context, key) => TextFormField( - // enabled: room.canSendDefaultMessages, + enabled: canSendMessages, maxLines: 12, minLines: 1, decoration: InputDecoration( - hintText: - true // TODO: room.canSendDefaultMessages + hintText: canSendMessages ? "Your message here..." : "You don't have permission to send messages in this room...", border: InputBorder.none, @@ -156,7 +165,7 @@ class ChatBox extends HookConsumerWidget { ), ), IconButton( - onPressed: send, + onPressed: !canSendMessages ? null : send, // onPressed: room.canSendDefaultMessages ? send : null, icon: Icon(Icons.send), tooltip: "Send message", diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 2baae22..2f76d9f 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -4,11 +4,14 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:intl/intl.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/power_level_controller.dart"; import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/membership.dart"; import "package:nexus/models/membership_status.dart"; +import "package:nexus/models/requests/membership_action.dart"; import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/main.dart"; @@ -150,7 +153,17 @@ class UserPopover extends ConsumerWidget { runSpacing: 8, children: [ FilledButton.icon(onPressed: null, label: Text("Message")), - if (member.status == MembershipStatus.join || + + if (ref.watch( + PowerLevelController.provider( + PowerLevelConfig( + eventType: "m.room.member", + action: MembershipAction.kick, + isStateEvent: true, + ), + ), + ) && + member.status == MembershipStatus.join || member.status == MembershipStatus.invite) FilledButton.icon( onPressed: () => showMembershipDialog(MembershipAction.kick), @@ -164,24 +177,33 @@ class UserPopover extends ConsumerWidget { ), ), ), - ElevatedButton.icon( - onPressed: () => showMembershipDialog( - member.status == MembershipStatus.ban - ? MembershipAction.unban - : MembershipAction.ban, - ), - label: Text( - member.status == MembershipStatus.ban ? "Unban" : "Ban", - ), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.colorScheme.errorContainer, - ), - foregroundColor: WidgetStatePropertyAll( - theme.colorScheme.onErrorContainer, + if (ref.watch( + PowerLevelController.provider( + PowerLevelConfig( + eventType: "m.room.member", + action: MembershipAction.ban, + isStateEvent: true, + ), + ), + )) + ElevatedButton.icon( + onPressed: () => showMembershipDialog( + member.status == MembershipStatus.ban + ? MembershipAction.unban + : MembershipAction.ban, + ), + label: Text( + member.status == MembershipStatus.ban ? "Unban" : "Ban", + ), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.colorScheme.errorContainer, + ), + foregroundColor: WidgetStatePropertyAll( + theme.colorScheme.onErrorContainer, + ), ), ), - ), ], ), ], From 185ee37f0419f966b22ab3f0cf157e0a6ed04d24 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 17:52:42 -0400 Subject: [PATCH 47/94] fix checks for memberships --- lib/controllers/power_level_controller.dart | 46 ++++++++++++++------- lib/models/configs/power_level_config.dart | 1 + lib/widgets/chat_page/user_popover.dart | 2 + 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/lib/controllers/power_level_controller.dart b/lib/controllers/power_level_controller.dart index db57d4f..26221e1 100644 --- a/lib/controllers/power_level_controller.dart +++ b/lib/controllers/power_level_controller.dart @@ -21,22 +21,40 @@ class PowerLevelController extends Notifier { final users = (event.content["users"] as Map? ?? {}); final events = (event.content["events"] as Map? ?? {}); - final userLevel = users.containsKey(user) - ? (users[user] as int) + int powerLevelOf(String userId) => users.containsKey(userId) + ? (users[userId] as int) : (event.content["users_default"] as int? ?? 0); - final requiredLevel = switch (config.action) { - MembershipAction.ban || - MembershipAction.unban => (event.content["ban"] as int? ?? 50), - MembershipAction.kick => (event.content["kick"] as int? ?? 50), - MembershipAction.invite => (event.content["invite"] as int? ?? 0), - null => - events.containsKey(config.eventType) - ? (events[config.eventType] as int) - : (config.isStateEvent - ? (event.content["state_default"] as int? ?? 50) - : (event.content["events_default"] as int? ?? 0)), - }; + final userLevel = powerLevelOf(user); + final targetLevel = config.targetUser != null + ? powerLevelOf(config.targetUser!) + : null; + + if (config.action != null) { + return switch (config.action!) { + MembershipAction.invite => + userLevel >= (event.content["invite"] as int? ?? 0), + + MembershipAction.kick => + targetLevel != null && + userLevel >= (event.content["kick"] as int? ?? 50) && + userLevel > targetLevel, + + MembershipAction.ban => + targetLevel != null && + userLevel >= (event.content["ban"] as int? ?? 50) && + userLevel > targetLevel, + + MembershipAction.unban => + userLevel >= (event.content["ban"] as int? ?? 50), + }; + } + + final requiredLevel = events.containsKey(config.eventType) + ? (events[config.eventType] as int) + : (config.isStateEvent + ? (event.content["state_default"] as int? ?? 50) + : (event.content["events_default"] as int? ?? 0)); return userLevel >= requiredLevel; } diff --git a/lib/models/configs/power_level_config.dart b/lib/models/configs/power_level_config.dart index c051fed..31cc08c 100644 --- a/lib/models/configs/power_level_config.dart +++ b/lib/models/configs/power_level_config.dart @@ -9,6 +9,7 @@ abstract class PowerLevelConfig with _$PowerLevelConfig { @Default(false) bool isStateEvent, required String eventType, MembershipAction? action, + String? targetUser, }) = _PowerLevelConfig; factory PowerLevelConfig.fromJson(Map json) => diff --git a/lib/widgets/chat_page/user_popover.dart b/lib/widgets/chat_page/user_popover.dart index 2f76d9f..a9a4799 100644 --- a/lib/widgets/chat_page/user_popover.dart +++ b/lib/widgets/chat_page/user_popover.dart @@ -160,6 +160,7 @@ class UserPopover extends ConsumerWidget { eventType: "m.room.member", action: MembershipAction.kick, isStateEvent: true, + targetUser: member.userId, ), ), ) && @@ -183,6 +184,7 @@ class UserPopover extends ConsumerWidget { eventType: "m.room.member", action: MembershipAction.ban, isStateEvent: true, + targetUser: member.userId, ), ), )) From a8383951ba5fe93d6d1678235b6252dd4efbb134 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 18:26:19 -0400 Subject: [PATCH 48/94] Refactor dialog stuff --- lib/helpers/extensions/focus_room.dart | 27 ++++++++++ .../extensions/join_room_with_snackbars.dart | 34 ++---------- lib/helpers/extensions/link_to_mention.dart | 4 +- lib/widgets/chat_page/html/mention_chip.dart | 9 ++-- lib/widgets/chat_page/join_dialog.dart | 46 ++++++++++++++++ lib/widgets/chat_page/sidebar.dart | 53 +------------------ 6 files changed, 83 insertions(+), 90 deletions(-) create mode 100644 lib/helpers/extensions/focus_room.dart create mode 100644 lib/widgets/chat_page/join_dialog.dart diff --git a/lib/helpers/extensions/focus_room.dart b/lib/helpers/extensions/focus_room.dart new file mode 100644 index 0000000..e8b3140 --- /dev/null +++ b/lib/helpers/extensions/focus_room.dart @@ -0,0 +1,27 @@ +import "package:collection/collection.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/key_controller.dart"; +import "package:nexus/controllers/spaces_controller.dart"; + +extension FocusRoom on WidgetRef { + Future focusRoom(String id) async { + final spaces = watch(SpacesController.provider); + final space = spaces.firstWhereOrNull((space) => space.id == id); + + await watch(KeyController.provider(KeyController.spaceKey).notifier).set( + space?.id ?? + spaces + .firstWhere( + (space) => + space.children.any((child) => child.metadata?.id == id), + ) + .id, + ); + + if (space == null) { + await watch( + KeyController.provider(KeyController.roomKey).notifier, + ).set(id); + } + } +} diff --git a/lib/helpers/extensions/join_room_with_snackbars.dart b/lib/helpers/extensions/join_room_with_snackbars.dart index eaa0659..4cf1f68 100644 --- a/lib/helpers/extensions/join_room_with_snackbars.dart +++ b/lib/helpers/extensions/join_room_with_snackbars.dart @@ -1,10 +1,8 @@ -import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; -import "package:nexus/controllers/key_controller.dart"; -import "package:nexus/controllers/spaces_controller.dart"; +import "package:nexus/helpers/extensions/focus_room.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; import "package:nexus/models/requests/join_room_request.dart"; @@ -14,7 +12,7 @@ extension JoinRoomWithSnackbars on ClientController { String roomAlias, WidgetRef ref, ) async { - final roomIdOrAlias = roomAlias.mention ?? roomAlias; + final roomIdOrAlias = roomAlias.mention; // TODO: Parse vias properly final scaffoldMessenger = ScaffoldMessenger.of(context); @@ -41,33 +39,7 @@ extension JoinRoomWithSnackbars on ClientController { content: Text("Room $roomIdOrAlias successfully joined."), action: SnackBarAction( label: "Open", - onPressed: () async { - final spaces = ref.watch(SpacesController.provider); - final space = spaces.firstWhereOrNull((space) => space.id == id); - - await ref - .watch( - KeyController.provider(KeyController.spaceKey).notifier, - ) - .set( - space?.id ?? - spaces - .firstWhere( - (space) => space.children.any( - (child) => child.metadata?.id == id, - ), - ) - .id, - ); - - if (space == null) { - await ref - .watch( - KeyController.provider(KeyController.roomKey).notifier, - ) - .set(id); - } - }, + onPressed: () => ref.focusRoom(id), ), ), ); diff --git a/lib/helpers/extensions/link_to_mention.dart b/lib/helpers/extensions/link_to_mention.dart index b0e62aa..289de3f 100644 --- a/lib/helpers/extensions/link_to_mention.dart +++ b/lib/helpers/extensions/link_to_mention.dart @@ -9,7 +9,7 @@ extension LinkToMention on String { /// /// Returns the decoded identifier (e.g. "#room:matrix.org") /// or null if this is not a Matrix link. - String? get mention { + String get mention { final trimmed = trim(); final matrixTo = RegExp( @@ -39,6 +39,6 @@ extension LinkToMention on String { } catch (_) {} } - return null; + return this; } } diff --git a/lib/widgets/chat_page/html/mention_chip.dart b/lib/widgets/chat_page/html/mention_chip.dart index 0f80617..d4b9927 100644 --- a/lib/widgets/chat_page/html/mention_chip.dart +++ b/lib/widgets/chat_page/html/mention_chip.dart @@ -10,11 +10,9 @@ class MentionChip extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final mention = label.mention; - final membership = - mention?.startsWith("@") == true || label.startsWith("@") == true + final membership = label.mention.startsWith("@") == true ? ref - .watch(UserController.provider(mention ?? label)) + .watch(UserController.provider(label.mention)) .whenOrNull(data: (data) => data) : null; @@ -31,8 +29,7 @@ class MentionChip extends ConsumerWidget { child: Chip( label: Text( (membership == null ? null : "@${membership.displayName}") ?? - mention ?? - label, + label.mention, style: TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onPrimary, diff --git a/lib/widgets/chat_page/join_dialog.dart b/lib/widgets/chat_page/join_dialog.dart new file mode 100644 index 0000000..0128037 --- /dev/null +++ b/lib/widgets/chat_page/join_dialog.dart @@ -0,0 +1,46 @@ +import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/helpers/extensions/join_room_with_snackbars.dart"; +import "package:nexus/widgets/form_text_input.dart"; + +class JoinDialog extends HookConsumerWidget { + const JoinDialog({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final roomAlias = useTextEditingController(); + return AlertDialog( + title: Text("Join a Room"), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Enter the room alias, ID, or a Matrix.to link."), + SizedBox(height: 12), + FormTextInput( + required: false, + capitalize: true, + controller: roomAlias, + title: "#room:server", + ), + ], + ), + actions: [ + TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")), + TextButton( + onPressed: () async { + Navigator.of(context).pop(); + + final client = ref.watch(ClientController.provider.notifier); + if (context.mounted) { + client.joinRoomWithSnackBars(context, roomAlias.text, ref); + } + }, + child: Text("Join"), + ), + ], + ); + } +} diff --git a/lib/widgets/chat_page/sidebar.dart b/lib/widgets/chat_page/sidebar.dart index 18413a0..8534dc3 100644 --- a/lib/widgets/chat_page/sidebar.dart +++ b/lib/widgets/chat_page/sidebar.dart @@ -1,14 +1,11 @@ import "package:flutter/material.dart"; -import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/key_controller.dart"; import "package:nexus/controllers/selected_space_controller.dart"; import "package:nexus/controllers/spaces_controller.dart"; -import "package:nexus/helpers/extensions/join_room_with_snackbars.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; +import "package:nexus/widgets/chat_page/join_dialog.dart"; import "package:nexus/widgets/chat_page/room_menu.dart"; -import "package:nexus/widgets/form_text_input.dart"; class Sidebar extends HookConsumerWidget { final bool isDesktop; @@ -91,53 +88,7 @@ class Sidebar extends HookConsumerWidget { PopupMenuItem( onTap: () => showDialog( context: context, - builder: (alertContext) => HookBuilder( - builder: (_) { - final roomAlias = useTextEditingController(); - return AlertDialog( - title: Text("Join a Room"), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Enter the room alias, ID, or a Matrix.to link.", - ), - SizedBox(height: 12), - FormTextInput( - required: false, - capitalize: true, - controller: roomAlias, - title: "#room:server", - ), - ], - ), - actions: [ - TextButton( - onPressed: Navigator.of(context).pop, - child: Text("Cancel"), - ), - TextButton( - onPressed: () async { - Navigator.of(alertContext).pop(); - - final client = ref.watch( - ClientController.provider.notifier, - ); - if (context.mounted) { - client.joinRoomWithSnackBars( - context, - roomAlias.text, - ref, - ); - } - }, - child: Text("Join"), - ), - ], - ); - }, - ), + builder: (_) => JoinDialog(), ), child: ListTile( title: Text("Join an existing room (or space)"), From fd4b16c700438066ddee77af9be5f78819eea988 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 18:36:20 -0400 Subject: [PATCH 49/94] Pass href to mention chip, fixing some mentions --- lib/widgets/chat_page/html/html.dart | 4 +++- lib/widgets/chat_page/html/mention_chip.dart | 11 ++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index f3b756f..8e9bb58 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -60,7 +60,9 @@ class Html extends ConsumerWidget { "a" => element.attributes["href"]?.mention == null ? null - : InlineCustomWidget(child: MentionChip(element.text)), + : InlineCustomWidget( + child: MentionChip(element.attributes["href"]!), + ), "img" => src == null diff --git a/lib/widgets/chat_page/html/mention_chip.dart b/lib/widgets/chat_page/html/mention_chip.dart index d4b9927..b850211 100644 --- a/lib/widgets/chat_page/html/mention_chip.dart +++ b/lib/widgets/chat_page/html/mention_chip.dart @@ -5,19 +5,20 @@ import "package:nexus/helpers/extensions/link_to_mention.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart"; class MentionChip extends ConsumerWidget { - final String label; - const MentionChip(this.label, {super.key}); + final String content; + const MentionChip(this.content, {super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final membership = label.mention.startsWith("@") == true + final membership = content.mention.startsWith("@") == true ? ref - .watch(UserController.provider(label.mention)) + .watch(UserController.provider(content.mention)) .whenOrNull(data: (data) => data) : null; return InkWell( onTapUp: (details) { + content.mention; if (membership != null) { context.showUserPopover( membership, @@ -29,7 +30,7 @@ class MentionChip extends ConsumerWidget { child: Chip( label: Text( (membership == null ? null : "@${membership.displayName}") ?? - label.mention, + content.mention, style: TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onPrimary, From 9464b2bf78a9b6ded823078781417c236db197f8 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 18:49:21 -0400 Subject: [PATCH 50/94] Don't defocus chat box on send --- lib/widgets/chat_page/composer/chat_box.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index 3c2057e..dae8bc8 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -149,6 +149,7 @@ class ChatBox extends HookConsumerWidget { enabled: canSendMessages, maxLines: 12, minLines: 1, + autofocus: true, decoration: InputDecoration( hintText: canSendMessages ? "Your message here..." @@ -159,6 +160,8 @@ class ChatBox extends HookConsumerWidget { key: key, // TODO: Setting for send on enter on / off onFieldSubmitted: (_) => send(), + // Don't defocus on submit + onEditingComplete: () {}, textInputAction: TextInputAction.done, focusNode: node, ), From 24f5f7d0b6470e591ee801ab7b07e6374870aa08 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 18:49:35 -0400 Subject: [PATCH 51/94] Remove old comments [skip ci] --- lib/widgets/chat_page/composer/chat_box.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index dae8bc8..894dbfa 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -126,7 +126,6 @@ class ChatBox extends HookConsumerWidget { ), ], icon: Icon(Icons.add), - // enabled: room.canSendDefaultMessages, TODO: Permissions check ), Expanded( child: FlutterTagger( @@ -169,7 +168,6 @@ class ChatBox extends HookConsumerWidget { ), IconButton( onPressed: !canSendMessages ? null : send, - // onPressed: room.canSendDefaultMessages ? send : null, icon: Icon(Icons.send), tooltip: "Send message", ), From 4aa962193ddd67bf2738a5c67908ca4da55e7b28 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sat, 4 Apr 2026 21:33:31 -0400 Subject: [PATCH 52/94] fix parsing links as mentions --- lib/helpers/extensions/join_room_with_snackbars.dart | 2 +- lib/helpers/extensions/link_to_mention.dart | 4 ++-- lib/widgets/chat_page/html/mention_chip.dart | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/helpers/extensions/join_room_with_snackbars.dart b/lib/helpers/extensions/join_room_with_snackbars.dart index 4cf1f68..0267ac3 100644 --- a/lib/helpers/extensions/join_room_with_snackbars.dart +++ b/lib/helpers/extensions/join_room_with_snackbars.dart @@ -12,7 +12,7 @@ extension JoinRoomWithSnackbars on ClientController { String roomAlias, WidgetRef ref, ) async { - final roomIdOrAlias = roomAlias.mention; + final roomIdOrAlias = roomAlias.mention ?? roomAlias; // TODO: Parse vias properly final scaffoldMessenger = ScaffoldMessenger.of(context); diff --git a/lib/helpers/extensions/link_to_mention.dart b/lib/helpers/extensions/link_to_mention.dart index 289de3f..b0e62aa 100644 --- a/lib/helpers/extensions/link_to_mention.dart +++ b/lib/helpers/extensions/link_to_mention.dart @@ -9,7 +9,7 @@ extension LinkToMention on String { /// /// Returns the decoded identifier (e.g. "#room:matrix.org") /// or null if this is not a Matrix link. - String get mention { + String? get mention { final trimmed = trim(); final matrixTo = RegExp( @@ -39,6 +39,6 @@ extension LinkToMention on String { } catch (_) {} } - return this; + return null; } } diff --git a/lib/widgets/chat_page/html/mention_chip.dart b/lib/widgets/chat_page/html/mention_chip.dart index b850211..575ad03 100644 --- a/lib/widgets/chat_page/html/mention_chip.dart +++ b/lib/widgets/chat_page/html/mention_chip.dart @@ -10,9 +10,9 @@ class MentionChip extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final membership = content.mention.startsWith("@") == true + final membership = content.mention!.startsWith("@") == true ? ref - .watch(UserController.provider(content.mention)) + .watch(UserController.provider(content.mention!)) .whenOrNull(data: (data) => data) : null; @@ -30,7 +30,7 @@ class MentionChip extends ConsumerWidget { child: Chip( label: Text( (membership == null ? null : "@${membership.displayName}") ?? - content.mention, + content.mention!, style: TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onPrimary, From 92f6b2fbba340072d170718e7485910ffdfb89e6 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 11:21:15 -0400 Subject: [PATCH 53/94] Autofocus chatbox on edit/reply --- lib/widgets/chat_page/composer/chat_box.dart | 17 +---- lib/widgets/chat_page/room_appbar.dart | 68 +++++++++++--------- lib/widgets/chat_page/room_chat.dart | 31 +++++++-- 3 files changed, 66 insertions(+), 50 deletions(-) diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index 894dbfa..41bc123 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -1,6 +1,5 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; -import "package:flutter/services.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:fluttertagger/fluttertagger.dart"; @@ -15,6 +14,7 @@ class ChatBox extends HookConsumerWidget { final Message? relatedMessage; final RelationType relationType; final VoidCallback onDismiss; + final FocusNode? node; final Future Function( String text, { required bool shouldMention, @@ -26,6 +26,7 @@ class ChatBox extends HookConsumerWidget { required this.relationType, required this.onDismiss, required this.onSend, + this.node, super.key, }); @@ -55,18 +56,6 @@ class ChatBox extends HookConsumerWidget { controller.value.text = ""; } - final node = useFocusNode( - onKeyEvent: (_, event) { - if (event is KeyDownEvent && - event.logicalKey == LogicalKeyboardKey.escape) { - onDismiss(); - return KeyEventResult.handled; - } - - return KeyEventResult.ignored; - }, - ); - final style = TextStyle( color: theme.colorScheme.primary, fontWeight: FontWeight.bold, @@ -135,7 +124,7 @@ class ChatBox extends HookConsumerWidget { triggerCharacter: triggerCharacter.value, addTag: ({required id, required name}) { controller.value.addTag(id: id, name: name); - node.requestFocus(); + node?.requestFocus(); }, ), controller: controller.value, diff --git a/lib/widgets/chat_page/room_appbar.dart b/lib/widgets/chat_page/room_appbar.dart index 3df7f9d..62e282d 100644 --- a/lib/widgets/chat_page/room_appbar.dart +++ b/lib/widgets/chat_page/room_appbar.dart @@ -9,12 +9,12 @@ import "package:nexus/widgets/chat_page/room_menu.dart"; class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { final bool isDesktop; - final void Function(BuildContext context) onOpenMemberList; + final void Function(BuildContext context)? onOpenMemberList; final void Function(BuildContext context) onOpenDrawer; const RoomAppbar({ required this.isDesktop, - required this.onOpenMemberList, required this.onOpenDrawer, + this.onOpenMemberList, super.key, }); @@ -23,39 +23,43 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final room = ref.watch(SelectedRoomController.provider)!; + final room = ref.watch(SelectedRoomController.provider); return Appbar( leading: isDesktop - ? ExpandableImage( - room.metadata?.avatar?.toString(), - child: AvatarOrHash( - room.metadata?.avatar, - room.metadata?.name ?? "Unnamed Rooms", - height: 24, - fallback: Icon(Icons.numbers), - ), - ) + ? room == null + ? null + : ExpandableImage( + room.metadata?.avatar?.toString(), + child: AvatarOrHash( + room.metadata?.avatar, + room.metadata?.name ?? "Unnamed Rooms", + height: 24, + fallback: Icon(Icons.numbers), + ), + ) : DrawerButton(onPressed: () => onOpenDrawer(context)), scrolledUnderElevation: 0, - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - room.metadata?.name ?? "Unnamed Room", - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - if (room.metadata?.topic?.isNotEmpty == true) - Text( - room.metadata!.topic!, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.labelMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + title: room == null + ? null + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + room.metadata?.name ?? "Unnamed Room", + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + if (room.metadata?.topic?.isNotEmpty == true) + Text( + room.metadata!.topic!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.labelMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], ), - ], - ), actions: [ IconButton( onPressed: null, @@ -63,11 +67,11 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { tooltip: "Open pinned messages", ), IconButton( - onPressed: () => onOpenMemberList(context), + onPressed: () => onOpenMemberList?.call(context), tooltip: "Open member list", icon: Icon(Icons.people), ), - RoomMenu(room), + if (room != null) RoomMenu(room), ].toIList(), ); } diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index d85c826..9c950e9 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -1,4 +1,5 @@ import "package:flutter/material.dart"; +import "package:flutter/services.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_ui/flutter_chat_ui.dart"; import "package:flutter_hooks/flutter_hooks.dart"; @@ -47,10 +48,17 @@ class RoomChat extends HookConsumerWidget { final danger = theme.colorScheme.error; if (roomId == null || userId == null) { - return Center( - child: Text( - "Nothing to see here...", - style: theme.textTheme.headlineMedium, + return Scaffold( + appBar: RoomAppbar( + isDesktop: isDesktop, + onOpenDrawer: (_) => Scaffold.of(context).openDrawer(), + onOpenMemberList: null, + ), + body: Center( + child: Text( + "Nothing to see here...", + style: theme.textTheme.headlineMedium, + ), ), ); } @@ -58,6 +66,18 @@ class RoomChat extends HookConsumerWidget { final controllerProvider = RoomChatController.provider(roomId); final notifier = ref.watch(controllerProvider.notifier); + final composerNode = useFocusNode( + onKeyEvent: (_, event) { + if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.escape) { + relatedMessage.value = null; + return KeyEventResult.handled; + } + + return KeyEventResult.ignored; + }, + ); + List getMessageOptions(Message message) { final isSentByMe = message.authorId == userId; return [ @@ -65,6 +85,7 @@ class RoomChat extends HookConsumerWidget { onTap: () { relatedMessage.value = message; relationType.value = RelationType.reply; + composerNode.requestFocus(); }, child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")), ), @@ -73,6 +94,7 @@ class RoomChat extends HookConsumerWidget { onTap: () { relatedMessage.value = message; relationType.value = RelationType.edit; + composerNode.requestFocus(); }, child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")), ), @@ -259,6 +281,7 @@ class RoomChat extends HookConsumerWidget { ), composerBuilder: (_) => ChatBox( + node: composerNode, onSend: ( text, { From 639d27a5fc4914f31294d58a718ce4b2c379e63e Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 11:26:03 -0400 Subject: [PATCH 54/94] refactor url previews --- lib/controllers/url_preview_controller.dart | 11 ++++------- .../chat_page/wrappers/text_message_wrapper.dart | 12 +++++++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/controllers/url_preview_controller.dart b/lib/controllers/url_preview_controller.dart index e54efea..c2161d5 100644 --- a/lib/controllers/url_preview_controller.dart +++ b/lib/controllers/url_preview_controller.dart @@ -7,17 +7,14 @@ import "package:nexus/controllers/header_controller.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; class UrlPreviewController extends AsyncNotifier { - final TextMessage message; - UrlPreviewController(this.message); + final String link; + UrlPreviewController(this.link); @override Future build() async { final homeserver = ref.watch(ClientStateController.provider)?.homeserverUrl; - final link = RegExp( - r'''https?://[^\s"'<>]+''', - ).allMatches(message.text).firstOrNull?.group(0); - if (homeserver != null && link != null && !link.contains("matrix.to")) { + if (homeserver != null && !link.contains("matrix.to")) { { final response = await get( Uri.parse(homeserver) @@ -57,7 +54,7 @@ class UrlPreviewController extends AsyncNotifier { } static final provider = AsyncNotifierProvider.autoDispose - .family( + .family( UrlPreviewController.new, ); } diff --git a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart index d6f7aa8..8d7a625 100644 --- a/lib/widgets/chat_page/wrappers/text_message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/text_message_wrapper.dart @@ -40,6 +40,12 @@ class TextMessageWrapper extends ConsumerWidget { final colorScheme = theme.colorScheme; final textMessage = message is TextMessage ? message as TextMessage : null; + final link = textMessage == null + ? null + : RegExp( + r'''https?://[^\s"'<>]+''', + ).allMatches(textMessage.text).firstOrNull?.group(0); + return MessageWrapper( message, ClipRRect( @@ -97,9 +103,9 @@ class TextMessageWrapper extends ConsumerWidget { ), if (textMessage?.editedAt != null) Text("(edited)", style: theme.textTheme.labelSmall), - if (textMessage != null) + if (link != null) ref - .watch(UrlPreviewController.provider(textMessage)) + .watch(UrlPreviewController.provider(link)) .betterWhen( loading: SizedBox.shrink, data: (preview) => preview == null @@ -117,7 +123,7 @@ class TextMessageWrapper extends ConsumerWidget { fit: BoxFit.cover, errorBuilder: (_, _, _) => SizedBox.shrink(), ), - text: textMessage.text, + text: link, backgroundColor: isSentByMe ? colorScheme.inversePrimary : colorScheme.surfaceContainerLow, From aac843d79372dc210ae36fc4a674628440f353a6 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 13:32:59 -0400 Subject: [PATCH 55/94] Continuously load more messages until there are 20 or no more --- lib/controllers/room_chat_controller.dart | 46 ++++++++++++----------- lib/controllers/rooms_controller.dart | 32 +++++++++------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 3fe9d74..aef5226 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -123,9 +123,9 @@ class RoomChatController extends AsyncNotifier { ref.onDispose(controller.dispose); - // While there are under 20 messages, try up to two times to load more messages. - for (var i = 0; i < 2 && messages.length < 20; i++) { - await loadOlder(controller); + // While there are under 20 messages, try up to load more messages until theres no more or we have 20 messages. + for (var more = true; more == true && controller.messages.length < 20;) { + more = await loadOlder(controller); } return controller; @@ -151,7 +151,7 @@ class RoomChatController extends AsyncNotifier { RedactEventRequest(eventId: message.id, roomId: roomId, reason: reason), ); - Future loadOlder([InMemoryChatController? chatController]) async { + Future loadOlder([InMemoryChatController? chatController]) async { final response = await ref .watch(ClientController.provider.notifier) .paginate( @@ -183,28 +183,30 @@ class RoomChatController extends AsyncNotifier { ), }), const ISet.empty(), + addToNewEvents: false, ); final room = ref.read(RoomsController.provider)[roomId]; - if (room == null) return; + if (room != null) { + final messages = await ref.watch( + MessagesController.provider( + MessagesConfig(room: room, events: response.events.reversed), + ).future, + ); - final messages = await ref.watch( - MessagesController.provider( - MessagesConfig(room: room, events: response.events.reversed), - ).future, - ); - - final controller = chatController ?? await future; - await controller.insertAllMessages( - messages - .where( - (newMessage) => !controller.messages.any( - (message) => message.id == newMessage.id, - ), - ) - .toList(), - index: 0, - ); + final controller = chatController ?? await future; + await controller.insertAllMessages( + messages + .where( + (newMessage) => !controller.messages.any( + (message) => message.id == newMessage.id, + ), + ) + .toList(), + index: 0, + ); + } + return response.hasMore; } Future send( diff --git a/lib/controllers/rooms_controller.dart b/lib/controllers/rooms_controller.dart index 27eb18e..7013de0 100644 --- a/lib/controllers/rooms_controller.dart +++ b/lib/controllers/rooms_controller.dart @@ -11,7 +11,11 @@ class RoomsController extends Notifier> { @override IMap build() => const IMap.empty(); - void update(IMap rooms, ISet leftRooms) { + void update( + IMap rooms, + ISet leftRooms, { + bool addToNewEvents = true, + }) { final homeserver = ref.watch( ClientStateController.provider.select( @@ -29,18 +33,20 @@ class RoomsController extends Notifier> { (item) => item.eventId, ); - ref - .watch(NewEventsController.provider(roomId).notifier) - .add( - incoming.timeline - .map( - (timelineTuple) => events?.firstWhereOrNull( - (event) => timelineTuple.eventRowId == event.rowId, - ), - ) - .nonNulls - .toIList(), - ); + if (addToNewEvents) { + ref + .watch(NewEventsController.provider(roomId).notifier) + .add( + incoming.timeline + .map( + (timelineTuple) => events?.firstWhereOrNull( + (event) => timelineTuple.eventRowId == event.rowId, + ), + ) + .nonNulls + .toIList(), + ); + } return acc.add( roomId, From 7fc314036e6891a310b437a0e0ac55350e1e348a Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 16:30:40 -0400 Subject: [PATCH 56/94] Refactor content handling in message controller --- lib/controllers/message_controller.dart | 36 +++++++------------------ 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index 6ec62c9..f278d91 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -1,4 +1,5 @@ import "package:collection/collection.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_state_controller.dart"; @@ -16,7 +17,6 @@ class MessageController extends AsyncNotifier { return null; } - if (!ref.mounted) return null; final event = config.event.lastEditRowId == null ? config.event : config.room.events.firstWhereOrNull( @@ -24,11 +24,11 @@ class MessageController extends AsyncNotifier { ) ?? config.event; - if (!ref.mounted) return null; - - final content = (event.decrypted ?? event.content); + final decrypted = (event.decrypted ?? event.content); final type = (config.event.decryptedType ?? config.event.type); - final newContent = content["m.new_content"] as Map?; + final content = decrypted["m.new_content"] == null + ? decrypted + : IMap(decrypted["m.new_content"]); final homeserver = ref .read(ClientStateController.provider) @@ -39,24 +39,19 @@ class MessageController extends AsyncNotifier { final metadata = { "body": config.event.redactedBy == null - ? (newContent?["body"] ?? content["body"] ?? "") + ? (content["body"] ?? "") : "Deleted Message", "flashing": false, "timelineId": event.timelineRowId, "big": event.localContent?.bigEmoji == true, "eventType": type, - "pmp": event.content["com.beeper.per_message_profile"], + "pmp": content["com.beeper.per_message_profile"], "error": event.sendError, - "format": content["format"], - "editSource": - event.localContent?.editSource ?? - newContent?["body"] ?? - content["body"], + "format": content["format"] ?? content["format"], + "editSource": event.localContent?.editSource ?? content["body"], "txnId": config.event.transactionId, }; - if (!ref.mounted) return null; - final editedAt = event.relationType == "m.replace" ? event.timestamp : null; @@ -67,12 +62,6 @@ class MessageController extends AsyncNotifier { return null; } - // TODO: Use server-generated preview if enabled - - // final match = Uri.tryParse( - // RegExp(regexLink, caseSensitive: false).firstMatch(body)?.group(0) ?? "", - // ); - final replyId = config.event.content["m.relates_to"]?["m.in_reply_to"]?["event_id"]; @@ -81,12 +70,7 @@ class MessageController extends AsyncNotifier { metadata: metadata, id: config.event.eventId, authorId: event.authorId, - text: - newContent?["formatted_body"] ?? - newContent?["body"] ?? - content["formatted_body"] ?? - content["body"] ?? - "", + text: content["formatted_body"] ?? content["body"] ?? "", replyToMessageId: replyId, deliveredAt: config.event.timestamp, editedAt: editedAt, From 9fdf08a5d839c4597d3f25ac3f8b739d39e7fd16 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 21:25:25 -0400 Subject: [PATCH 57/94] don't open user popover on reply preview --- .../lazy_loading/message_displayname.dart | 18 +++++++++++++----- lib/widgets/chat_page/reply_widget.dart | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/widgets/chat_page/lazy_loading/message_displayname.dart b/lib/widgets/chat_page/lazy_loading/message_displayname.dart index ca9eed7..88d2fa6 100644 --- a/lib/widgets/chat_page/lazy_loading/message_displayname.dart +++ b/lib/widgets/chat_page/lazy_loading/message_displayname.dart @@ -8,17 +8,25 @@ import "package:nexus/helpers/extensions/show_user_popover.dart"; class MessageDisplayname extends ConsumerWidget { final Message message; final TextStyle? style; - const MessageDisplayname(this.message, {this.style, super.key}); + final bool clickable; + const MessageDisplayname( + this.message, { + this.clickable = true, + this.style, + super.key, + }); @override Widget build(BuildContext context, WidgetRef ref) => ref .watch(AuthorController.provider(message)) .betterWhen( data: (membership) => InkWell( - onTapUp: (details) => context.showUserPopover( - membership, - globalPosition: details.globalPosition, - ), + onTapUp: clickable + ? (details) => context.showUserPopover( + membership, + globalPosition: details.globalPosition, + ) + : null, child: Text( "${membership.displayName}${message.metadata?["pmp"] == null ? "" : " (via ${message.authorId})"}", style: style, diff --git a/lib/widgets/chat_page/reply_widget.dart b/lib/widgets/chat_page/reply_widget.dart index 343215f..b999be4 100644 --- a/lib/widgets/chat_page/reply_widget.dart +++ b/lib/widgets/chat_page/reply_widget.dart @@ -70,6 +70,7 @@ class ReplyWidget extends ConsumerWidget { Flexible( child: MessageDisplayname( replyMessage, + clickable: false, style: Theme.of(context) .textTheme .labelMedium From 06d6bf0cbc4dd3e855f27a4bf806bef7c5e8bcba Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 21:30:47 -0400 Subject: [PATCH 58/94] Make reply displayname flexible, fixes overflow on small screens and large displaynames --- lib/widgets/chat_page/composer/relation_preview.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/widgets/chat_page/composer/relation_preview.dart b/lib/widgets/chat_page/composer/relation_preview.dart index 7c682b4..bc22e0d 100644 --- a/lib/widgets/chat_page/composer/relation_preview.dart +++ b/lib/widgets/chat_page/composer/relation_preview.dart @@ -39,13 +39,15 @@ class RelationPreview extends ConsumerWidget { style: TextStyle(fontWeight: FontWeight.bold), ), MessageAvatar(relatedMessage!), - MessageDisplayname( - relatedMessage!, - style: theme.textTheme.labelMedium?.copyWith( - fontWeight: FontWeight.bold, + Flexible( + child: MessageDisplayname( + relatedMessage!, + style: theme.textTheme.labelMedium?.copyWith( + fontWeight: FontWeight.bold, + ), ), ), - Expanded( + Flexible( child: Text( relatedMessage?.metadata?["body"] ?? relatedMessage?.metadata?["eventType"], From c857b89899365e151e7516e66a71f38e0bec0a05 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 21:41:37 -0400 Subject: [PATCH 59/94] update readme --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1e78900..fda0e4e 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Inline emoji picker (Putting this here since it'll be implemented the same way as mentions) - [ ] Custom emojis/stickers - [ ] GIFs using Gomuks' GIF proxies - - [x] Recieving + - [x] Receiving - [x] Plain text - [x] Per message profiles - [x] HTML @@ -87,8 +87,6 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Plain text (not sure if I want to add this or not, I probably won't unless there's interest) - [x] Matrix URIs - [x] Matrix.to links - - [ ] Do some fancy fetching to get nice names - - [ ] Make clickable - [x] Custom emojis/stickers - [x] History loading - [x] Backwards @@ -102,7 +100,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Threads - [x] Profile popouts - [ ] Working actions -- [ ] Copy link to [room, space] +- [ ] Copy link to [room, space, message] - [ ] Reporting - [x] Events - [ ] Rooms @@ -110,6 +108,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Group calls using [MSC4195](https://github.com/matrix-org/matrix-spec-proposals/pull/4195) - [ ] Invites - [ ] Settings + - [ ] Matrix: URIs vs Matrix.to links - [ ] Light/Dark mode - [ ] SSD or CSD - [ ] Show media by default From 8dff27c56ffd1f94afe4901a76c9b676251808a0 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 21:45:39 -0400 Subject: [PATCH 60/94] use room alias where available --- lib/widgets/chat_page/composer/mention_overlay.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/chat_page/composer/mention_overlay.dart b/lib/widgets/chat_page/composer/mention_overlay.dart index a78bdd1..8013d36 100644 --- a/lib/widgets/chat_page/composer/mention_overlay.dart +++ b/lib/widgets/chat_page/composer/mention_overlay.dart @@ -94,7 +94,7 @@ class MentionOverlay extends ConsumerWidget { ? null : Text(room.metadata!.topic!, maxLines: 1), onTap: () => addTag( - id: "[#${room.metadata?.name ?? "Unnamed Room"}](https://matrix.to/#/${room.metadata?.id})", + id: "[#${room.metadata?.name ?? "Unnamed Room"}](https://matrix.to/#/${room.metadata?.canonicalAlias ?? room.metadata?.id})", name: (room.metadata?.canonicalAlias ?? room.metadata?.id) From 15d02458abe80a68c9081d7ac43a2565e0aa4513 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 21:48:25 -0400 Subject: [PATCH 61/94] Add comment to mention overlay to add vias to link --- lib/widgets/chat_page/composer/mention_overlay.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/widgets/chat_page/composer/mention_overlay.dart b/lib/widgets/chat_page/composer/mention_overlay.dart index 8013d36..0b9f4d5 100644 --- a/lib/widgets/chat_page/composer/mention_overlay.dart +++ b/lib/widgets/chat_page/composer/mention_overlay.dart @@ -94,6 +94,9 @@ class MentionOverlay extends ConsumerWidget { ? null : Text(room.metadata!.topic!, maxLines: 1), onTap: () => addTag( + // Should add vias to generated link, see following: + // https://github.com/gomuks/gomuks/blob/d5deeb5d409181e469eada8b534882576ac78e63/web/src/ui/modal/ShareModal.tsx#L31-L57 + // https://github.com/gomuks/gomuks/blob/main/web/src/api/statestore/room.ts#L329 id: "[#${room.metadata?.name ?? "Unnamed Room"}](https://matrix.to/#/${room.metadata?.canonicalAlias ?? room.metadata?.id})", name: (room.metadata?.canonicalAlias ?? From b80bd557dde31cb8189550a74eac9dd23b1f6686 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 22:00:34 -0400 Subject: [PATCH 62/94] add redact PL check --- lib/controllers/power_level_controller.dart | 4 ++++ lib/widgets/chat_page/composer/chat_box.dart | 2 +- lib/widgets/chat_page/room_chat.dart | 13 +++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/controllers/power_level_controller.dart b/lib/controllers/power_level_controller.dart index 26221e1..41b5f19 100644 --- a/lib/controllers/power_level_controller.dart +++ b/lib/controllers/power_level_controller.dart @@ -50,6 +50,10 @@ class PowerLevelController extends Notifier { }; } + if (config.eventType == "m.room.redaction") { + return userLevel >= (event.content["redact"] as int? ?? 50); + } + final requiredLevel = events.containsKey(config.eventType) ? (events[config.eventType] as int) : (config.isStateEvent diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index 41bc123..1cf03d9 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -63,7 +63,7 @@ class ChatBox extends HookConsumerWidget { final canSendMessages = ref.watch( PowerLevelController.provider( - PowerLevelConfig(eventType: "m.room.message"), + PowerLevelConfig(eventType: "m.room.redaction"), ), ); diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index 9c950e9..b6891f0 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -8,10 +8,12 @@ import "package:flyer_chat_system_message/flyer_chat_system_message.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/power_level_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/show_context_menu.dart"; +import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/relation_type.dart"; import "package:nexus/models/requests/report_request.dart"; import "package:nexus/widgets/chat_page/composer/chat_box.dart"; @@ -98,7 +100,11 @@ class RoomChat extends HookConsumerWidget { }, child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")), ), - if (isSentByMe) // TODO: Or if user has permission to redact others' messages + if (ref.watch( + PowerLevelController.provider( + PowerLevelConfig(eventType: "m.room.redaction"), + ), + )) PopupMenuItem( onTap: () => showDialog( context: context, @@ -145,7 +151,10 @@ class RoomChat extends HookConsumerWidget { }, ), ), - child: ListTile(leading: Icon(Icons.delete), title: Text("Delete")), + child: ListTile( + leading: Icon(Icons.delete, color: danger), + title: Text("Delete", style: TextStyle(color: danger)), + ), ), PopupMenuItem( onTap: () => showDialog( From 729f71e529e282b395eba91a1672e5af63f0d69e Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 5 Apr 2026 22:10:07 -0400 Subject: [PATCH 63/94] remove a todo since its already in the progress list --- lib/widgets/chat_page/composer/chat_box.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index 1cf03d9..e11aef8 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -146,7 +146,6 @@ class ChatBox extends HookConsumerWidget { ), controller: controller.value, key: key, - // TODO: Setting for send on enter on / off onFieldSubmitted: (_) => send(), // Don't defocus on submit onEditingComplete: () {}, From 6fe5677a13a392f0c8aadc3f9851dbc660b24a1f Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 09:20:39 -0400 Subject: [PATCH 64/94] oops thats not the event type! --- lib/widgets/chat_page/composer/chat_box.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index e11aef8..1b41037 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -63,7 +63,7 @@ class ChatBox extends HookConsumerWidget { final canSendMessages = ref.watch( PowerLevelController.provider( - PowerLevelConfig(eventType: "m.room.redaction"), + PowerLevelConfig(eventType: "m.room.message"), ), ); From f4b2669f3d03960833976f53306801a51291f78b Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 09:20:54 -0400 Subject: [PATCH 65/94] Fix link sending --- lib/controllers/via_controller.dart | 54 +++++++++++++++++++ .../chat_page/composer/mention_overlay.dart | 54 +++++++++++-------- 2 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 lib/controllers/via_controller.dart diff --git a/lib/controllers/via_controller.dart b/lib/controllers/via_controller.dart new file mode 100644 index 0000000..b423947 --- /dev/null +++ b/lib/controllers/via_controller.dart @@ -0,0 +1,54 @@ +import "package:collection/collection.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/models/room.dart"; + +class ViaController extends Notifier { + final Room room; + ViaController(this.room); + + @override + String build() { + final servers = {}; + + void addUserId(String? userId) { + final server = userId?.split(":").lastOrNull; + if (server != null) { + servers.add(server); + } + } + + addUserId(ref.watch(ClientStateController.provider)?.userId); + + final powerLevels = room.events.firstWhereOrNull( + (event) => event.rowId == room.state["m.room.power_levels"]?[""], + ); + + for (final userId in IMap(powerLevels?.content["users"]).keys) { + addUserId(userId); + if (servers.length >= 5) break; + } + + final members = room.state["m.room.member"]?.values.toIList(); + for (var i = 0; servers.length < 5; i++) { + final member = room.events.firstWhereOrNull( + (event) => event.rowId == members?.getOrNull(i), + ); + + if (member?.content["membership"] == "join") { + addUserId(member?.stateKey); + } + + if (members?.getOrNull(i) == null) break; + } + + return servers.isEmpty + ? "" + : "?${servers.map((server) => "via=$server").join("&")}"; + } + + static final provider = NotifierProvider.family( + ViaController.new, + ); +} diff --git a/lib/widgets/chat_page/composer/mention_overlay.dart b/lib/widgets/chat_page/composer/mention_overlay.dart index 0b9f4d5..b650421 100644 --- a/lib/widgets/chat_page/composer/mention_overlay.dart +++ b/lib/widgets/chat_page/composer/mention_overlay.dart @@ -2,6 +2,7 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/members_by_type_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/controllers/via_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/models/membership_status.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; @@ -62,7 +63,7 @@ class MentionOverlay extends ConsumerWidget { title: Text(member.displayName), subtitle: Text(member.userId), onTap: () => addTag( - id: "[@${member.displayName}](https://matrix.to/#/${member.userId})", + id: "[@${member.displayName}](matrix:u/${member.userId.substring(1)})", name: member.userId .substring(1) .split(":") @@ -78,36 +79,43 @@ class MentionOverlay extends ConsumerWidget { (query.isEmpty ? rooms.values : rooms.values.where( - (room) => (room.metadata?.name ?? "Unnamed Room") - .toLowerCase() - .contains(query.toLowerCase()), + (room) => + (room.metadata?.name ?? room.metadata!.id) + .toLowerCase() + .contains(query.toLowerCase()), )) - .map( - (room) => ListTile( + .map((room) { + final name = + room.metadata?.name ?? + room.metadata!.canonicalAlias ?? + room.metadata!.id; + return ListTile( leading: AvatarOrHash( room.metadata?.avatar, - room.metadata?.name ?? "Unnamed Room", + name, fallback: Icon(Icons.numbers), ), - title: Text(room.metadata?.name ?? "Unnamed Room"), + title: Text(name), subtitle: room.metadata?.topic == null ? null : Text(room.metadata!.topic!, maxLines: 1), - onTap: () => addTag( - // Should add vias to generated link, see following: - // https://github.com/gomuks/gomuks/blob/d5deeb5d409181e469eada8b534882576ac78e63/web/src/ui/modal/ShareModal.tsx#L31-L57 - // https://github.com/gomuks/gomuks/blob/main/web/src/api/statestore/room.ts#L329 - id: "[#${room.metadata?.name ?? "Unnamed Room"}](https://matrix.to/#/${room.metadata?.canonicalAlias ?? room.metadata?.id})", - name: - (room.metadata?.canonicalAlias ?? - room.metadata?.id) - ?.substring(1) - .split(":") - .first ?? - "", - ), - ), - ) + onTap: () { + final vias = ref.watch( + ViaController.provider(room), + ); + addTag( + id: "[#$name](matrix:roomid/${room.metadata?.id.substring(1)}$vias)", + name: + (room.metadata?.canonicalAlias ?? + room.metadata?.id) + ?.substring(1) + .split(":") + .first ?? + "", + ); + }, + ); + }) .toList(), ), From 2c23951ea85245018da00e86c6fda91d186bd9e9 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 09:57:03 -0400 Subject: [PATCH 66/94] Fix via handling --- lib/helpers/extensions/focus_room.dart | 27 ----- .../extensions/join_room_with_snackbars.dart | 63 ----------- lib/helpers/extensions/link_to_mention.dart | 3 +- lib/widgets/chat_page/join_dialog.dart | 105 ++++++++++++++++-- lib/widgets/chat_page/sidebar.dart | 2 +- 5 files changed, 101 insertions(+), 99 deletions(-) delete mode 100644 lib/helpers/extensions/focus_room.dart delete mode 100644 lib/helpers/extensions/join_room_with_snackbars.dart diff --git a/lib/helpers/extensions/focus_room.dart b/lib/helpers/extensions/focus_room.dart deleted file mode 100644 index e8b3140..0000000 --- a/lib/helpers/extensions/focus_room.dart +++ /dev/null @@ -1,27 +0,0 @@ -import "package:collection/collection.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/key_controller.dart"; -import "package:nexus/controllers/spaces_controller.dart"; - -extension FocusRoom on WidgetRef { - Future focusRoom(String id) async { - final spaces = watch(SpacesController.provider); - final space = spaces.firstWhereOrNull((space) => space.id == id); - - await watch(KeyController.provider(KeyController.spaceKey).notifier).set( - space?.id ?? - spaces - .firstWhere( - (space) => - space.children.any((child) => child.metadata?.id == id), - ) - .id, - ); - - if (space == null) { - await watch( - KeyController.provider(KeyController.roomKey).notifier, - ).set(id); - } - } -} diff --git a/lib/helpers/extensions/join_room_with_snackbars.dart b/lib/helpers/extensions/join_room_with_snackbars.dart deleted file mode 100644 index 0267ac3..0000000 --- a/lib/helpers/extensions/join_room_with_snackbars.dart +++ /dev/null @@ -1,63 +0,0 @@ -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:flutter/material.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/client_controller.dart"; -import "package:nexus/helpers/extensions/focus_room.dart"; -import "package:nexus/helpers/extensions/link_to_mention.dart"; -import "package:nexus/models/requests/join_room_request.dart"; - -extension JoinRoomWithSnackbars on ClientController { - Future joinRoomWithSnackBars( - BuildContext context, - String roomAlias, - WidgetRef ref, - ) async { - final roomIdOrAlias = roomAlias.mention ?? roomAlias; - // TODO: Parse vias properly - - final scaffoldMessenger = ScaffoldMessenger.of(context); - - final snackbar = scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Joining room $roomIdOrAlias."), - duration: Duration(days: 999), - ), - ); - - try { - final id = await joinRoom( - JoinRoomRequest( - roomIdOrAlias: roomIdOrAlias, - via: IList(Uri.tryParse(roomAlias)?.queryParametersAll["via"] ?? []), - ), - ); - - snackbar.close(); - - scaffoldMessenger.showSnackBar( - SnackBar( - content: Text("Room $roomIdOrAlias successfully joined."), - action: SnackBarAction( - label: "Open", - onPressed: () => ref.focusRoom(id), - ), - ), - ); - } catch (error) { - snackbar.close(); - if (context.mounted) { - scaffoldMessenger.showSnackBar( - SnackBar( - backgroundColor: Theme.of(context).colorScheme.errorContainer, - content: Text( - error.toString(), - style: TextStyle( - color: Theme.of(context).colorScheme.onErrorContainer, - ), - ), - ), - ); - } - } - } -} diff --git a/lib/helpers/extensions/link_to_mention.dart b/lib/helpers/extensions/link_to_mention.dart index b0e62aa..f4868d3 100644 --- a/lib/helpers/extensions/link_to_mention.dart +++ b/lib/helpers/extensions/link_to_mention.dart @@ -30,7 +30,8 @@ extension LinkToMention on String { final identifier = uri.pathSegments.last; if (identifier.isNotEmpty) { return "${switch (uri.pathSegments.firstOrNull) { - "r" || "roomid" => "#", + "r" => "#", + "roomid" => "!", "u" => "@", _ => "", }}${Uri.decodeComponent(identifier)}"; diff --git a/lib/widgets/chat_page/join_dialog.dart b/lib/widgets/chat_page/join_dialog.dart index 0128037..e718200 100644 --- a/lib/widgets/chat_page/join_dialog.dart +++ b/lib/widgets/chat_page/join_dialog.dart @@ -1,15 +1,21 @@ +import "package:collection/collection.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; -import "package:nexus/helpers/extensions/join_room_with_snackbars.dart"; +import "package:nexus/controllers/key_controller.dart"; +import "package:nexus/controllers/spaces_controller.dart"; +import "package:nexus/helpers/extensions/link_to_mention.dart"; +import "package:nexus/models/requests/join_room_request.dart"; import "package:nexus/widgets/form_text_input.dart"; -class JoinDialog extends HookConsumerWidget { - const JoinDialog({super.key}); +class JoinDialog extends HookWidget { + final WidgetRef ref; + const JoinDialog(this.ref, {super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { + Widget build(BuildContext context) { final roomAlias = useTextEditingController(); return AlertDialog( title: Text("Join a Room"), @@ -17,7 +23,7 @@ class JoinDialog extends HookConsumerWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Enter the room alias, ID, or a Matrix.to link."), + Text("Enter the room alias, Matrix URI, or Matrix.to link."), SizedBox(height: 12), FormTextInput( required: false, @@ -33,9 +39,94 @@ class JoinDialog extends HookConsumerWidget { onPressed: () async { Navigator.of(context).pop(); - final client = ref.watch(ClientController.provider.notifier); if (context.mounted) { - client.joinRoomWithSnackBars(context, roomAlias.text, ref); + final roomIdOrAlias = roomAlias.text.mention ?? roomAlias.text; + + final scaffoldMessenger = ScaffoldMessenger.of(context); + + final snackbar = scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Joining room $roomIdOrAlias."), + duration: Duration(days: 999), + ), + ); + + try { + final id = await ref + .watch(ClientController.provider.notifier) + .joinRoom( + JoinRoomRequest( + roomIdOrAlias: roomIdOrAlias, + via: IList( + Uri.tryParse( + roomAlias.text.replaceAll("/#", ""), + )?.queryParametersAll["via"] ?? + [], + ), + ), + ); + + snackbar.close(); + + scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Room $roomIdOrAlias successfully joined."), + action: SnackBarAction( + label: "Open", + onPressed: () async { + final spaces = ref.watch(SpacesController.provider); + final space = spaces.firstWhereOrNull( + (space) => space.id == id, + ); + + await ref + .watch( + KeyController.provider( + KeyController.spaceKey, + ).notifier, + ) + .set( + space?.id ?? + spaces + .firstWhere( + (space) => space.children.any( + (child) => child.metadata?.id == id, + ), + ) + .id, + ); + + if (space == null) { + await ref + .watch( + KeyController.provider( + KeyController.roomKey, + ).notifier, + ) + .set(id); + } + }, + ), + ), + ); + } catch (error) { + snackbar.close(); + if (context.mounted) { + scaffoldMessenger.showSnackBar( + SnackBar( + backgroundColor: Theme.of( + context, + ).colorScheme.errorContainer, + content: Text( + error.toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.onErrorContainer, + ), + ), + ), + ); + } + } } }, child: Text("Join"), diff --git a/lib/widgets/chat_page/sidebar.dart b/lib/widgets/chat_page/sidebar.dart index 8534dc3..f79c38f 100644 --- a/lib/widgets/chat_page/sidebar.dart +++ b/lib/widgets/chat_page/sidebar.dart @@ -88,7 +88,7 @@ class Sidebar extends HookConsumerWidget { PopupMenuItem( onTap: () => showDialog( context: context, - builder: (_) => JoinDialog(), + builder: (_) => JoinDialog(ref), ), child: ListTile( title: Text("Join an existing room (or space)"), From ee648ab105624dfa9a671ad040158f16b918fd8e Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 10:17:04 -0400 Subject: [PATCH 67/94] add ability to copy room/space link --- README.md | 5 ++++- lib/widgets/chat_page/room_menu.dart | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fda0e4e..3f49f5b 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,10 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Threads - [x] Profile popouts - [ ] Working actions -- [ ] Copy link to [room, space, message] +- [ ] Copy link to: + - [x] Room + - [x] Space + - [ ] Message - [ ] Reporting - [x] Events - [ ] Rooms diff --git a/lib/widgets/chat_page/room_menu.dart b/lib/widgets/chat_page/room_menu.dart index 2687bc8..382e20f 100644 --- a/lib/widgets/chat_page/room_menu.dart +++ b/lib/widgets/chat_page/room_menu.dart @@ -1,7 +1,9 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; +import "package:flutter/services.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/via_controller.dart"; import "package:nexus/models/room.dart"; class RoomMenu extends ConsumerWidget { @@ -16,13 +18,18 @@ class RoomMenu extends ConsumerWidget { return PopupMenuButton( itemBuilder: (_) => [ - // PopupMenuItem( - // onTap: () async { - // final link = await room.matrixToInviteLink(); - // await Clipboard.setData(ClipboardData(text: link.toString())); - // }, - // child: ListTile(leading: Icon(Icons.link), title: Text("Copy Link")), - // ), + PopupMenuItem( + onTap: () async { + final vias = ref.watch(ViaController.provider(room)); + + await Clipboard.setData( + ClipboardData( + text: "matrix:roomid/${room.metadata?.id.substring(1)}$vias)", + ), + ); + }, + child: ListTile(leading: Icon(Icons.link), title: Text("Copy Link")), + ), PopupMenuItem( onTap: () async { await client.markRead(room); From 798eb3c3fde96e54cf27c0fd8f915af677e3f160 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 10:22:32 -0400 Subject: [PATCH 68/94] add ability to copy link to message --- README.md | 2 +- lib/widgets/chat_page/room_chat.dart | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f49f5b..659c637 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Copy link to: - [x] Room - [x] Space - - [ ] Message + - [x] Message - [ ] Reporting - [x] Events - [ ] Rooms diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index b6891f0..a36bec8 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -11,6 +11,7 @@ import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/power_level_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart"; +import "package:nexus/controllers/via_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/show_context_menu.dart"; import "package:nexus/models/configs/power_level_config.dart"; @@ -100,6 +101,22 @@ class RoomChat extends HookConsumerWidget { }, child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")), ), + PopupMenuItem( + onTap: () async { + final room = ref.watch(SelectedRoomController.provider); + if (room == null) return; + + final vias = ref.watch(ViaController.provider(room)); + + await Clipboard.setData( + ClipboardData( + text: + "matrix:roomid/${room.metadata?.id.substring(1)}/e/${message.id}$vias)", + ), + ); + }, + child: ListTile(leading: Icon(Icons.link), title: Text("Copy Link")), + ), if (ref.watch( PowerLevelController.provider( PowerLevelConfig(eventType: "m.room.redaction"), From 3a7e708e3949147d590cb8728c905a4719ff93f0 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 10:39:43 -0400 Subject: [PATCH 69/94] update readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 659c637..1aedba8 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,14 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Polls: Waiting on https://github.com/SwanFlutter/dynamic_polls/issues/1 - [x] Mentions - [x] Users + - [x] Clickable - [x] Rooms - - [ ] Plain text (not sure if I want to add this or not, I probably won't unless there's interest) + - [x] Clickable - [x] Matrix URIs - [x] Matrix.to links + - [x] Events + - [ ] Render more nicely + - [ ] Clickable - [x] Custom emojis/stickers - [x] History loading - [x] Backwards From 2850b015a10d8c07b793278a6a2f70c436c7ca5b Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 10:41:34 -0400 Subject: [PATCH 70/94] Remove readme note on reactions, I'll do a custom impl --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1aedba8..0b29de8 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Forwards - [x] Editing - [x] Deleting -- [ ] Reactions: Waiting on https://github.com/flyerhq/flutter_chat_ui/pull/838 or me doing a custom impl +- [ ] Reactions - [ ] Pins - [ ] Displaying - [ ] Creating From f860d9651f1408c09aa325d13c839f0a7a1ff9cd Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 6 Apr 2026 11:14:47 -0400 Subject: [PATCH 71/94] Remove polls note that is no longer accurate --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b29de8..2e11d8f 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [x] Blurhashing - [ ] Downloading attachments - [x] Opening attachments in their own view - - [ ] Polls: Waiting on https://github.com/SwanFlutter/dynamic_polls/issues/1 + - [ ] Polls - [x] Mentions - [x] Users - [x] Clickable @@ -104,7 +104,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Threads - [x] Profile popouts - [ ] Working actions -- [ ] Copy link to: +- [x] Copy link to: - [x] Room - [x] Space - [x] Message From 624127f3a837b913ab7a0dcace7ba2aa260f2fb9 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 7 Apr 2026 12:28:06 -0400 Subject: [PATCH 72/94] add default height for emoticons --- lib/widgets/chat_page/html/html.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index 8e9bb58..fb533ad 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -33,7 +33,10 @@ class Html extends ConsumerWidget { return InlineCustomWidget(child: SpoilerText(text: element.text)); } - final height = int.tryParse(element.attributes["height"] ?? "") ?? 300; + final height = + int.tryParse(element.attributes["height"] ?? "") ?? + (element.attributes.keys.contains("data-mx-emoticon") ? 32 : null) ?? + 300; final width = int.tryParse(element.attributes["width"] ?? ""); final src = Uri.tryParse(element.attributes["src"] ?? "") ?.mxcToHttps( From 5f5ad911c2e04fd039fadb8fc03993d511e390dd Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 8 Apr 2026 15:47:53 -0400 Subject: [PATCH 73/94] Reaction support, currently readonly --- lib/controllers/message_controller.dart | 34 ++++++++++- lib/controllers/room_chat_controller.dart | 1 + .../chat_page/wrappers/message_wrapper.dart | 56 ++++++++++++++++++- 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index f278d91..24356c2 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -2,9 +2,11 @@ import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/configs/message_config.dart"; +import "package:nexus/models/requests/get_related_events_request.dart"; class MessageController extends AsyncNotifier { final MessageConfig config; @@ -13,7 +15,8 @@ class MessageController extends AsyncNotifier { @override Future build() async { try { - if (config.event.relationType == "m.replace" && !config.includeEdits) { + if ((config.event.relationType == "m.replace" && !config.includeEdits) || + config.room.metadata == null) { return null; } @@ -65,10 +68,35 @@ class MessageController extends AsyncNotifier { final replyId = config.event.content["m.relates_to"]?["m.in_reply_to"]?["event_id"]; + final reactionEvents = await ref + .watch(ClientController.provider.notifier) + .getRelatedEvents( + GetRelatedEventsRequest( + roomId: config.room.metadata!.id, + eventId: config.event.eventId, + relationType: "m.annotation", + ), + ); + + final reactions = reactionEvents + ?.fold>>(IMap(), (acc, event) { + final key = event.content["m.relates_to"]?["key"]; + if (key == null) return acc; + + return acc.update( + key, + (list) => list.add(event.authorId), + ifAbsent: () => IList([event.authorId]), + ); + }) + .map((key, value) => MapEntry(key, value.unlock)) + .unlock; + final asText = Message.text( metadata: metadata, id: config.event.eventId, + reactions: reactions, authorId: event.authorId, text: content["formatted_body"] ?? content["body"] ?? "", replyToMessageId: replyId, @@ -80,6 +108,7 @@ class MessageController extends AsyncNotifier { Message toSystemMessage(String content) => Message.system( metadata: {...metadata, "body": content}, id: config.event.eventId, + reactions: reactions, authorId: event.authorId, deliveredAt: config.event.timestamp, text: content, @@ -104,6 +133,7 @@ class MessageController extends AsyncNotifier { null || "m.image" => Message.image( id: config.event.eventId, authorId: event.authorId, + reactions: reactions, source: source, replyToMessageId: replyId, metadata: metadata, @@ -116,6 +146,7 @@ class MessageController extends AsyncNotifier { size: content["info"]["size"], metadata: metadata, id: config.event.eventId, + reactions: reactions, authorId: event.authorId, source: source, replyToMessageId: replyId, @@ -159,6 +190,7 @@ class MessageController extends AsyncNotifier { // ignore: dead_code ? Message.unsupported( metadata: metadata, + reactions: reactions, id: config.event.eventId, authorId: event.authorId, replyToMessageId: replyId, diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index aef5226..d3da7c7 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -77,6 +77,7 @@ class RoomChatController extends AsyncNotifier { ref.onDispose( ref.listen(NewEventsController.provider(roomId), (_, next) async { for (final event in next) { + // TODO: Handle new reactions if (event.type == "m.room.redaction") { final controller = await future; final message = controller.messages.firstWhereOrNull( diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart index 33de6db..79cb35f 100644 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/message_wrapper.dart @@ -1,19 +1,28 @@ +import "package:cross_cache/cross_cache.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; import "package:timeago/timeago.dart"; -class MessageWrapper extends StatelessWidget { +class MessageWrapper extends ConsumerWidget { final Message message; final Widget child; final MessageGroupStatus? groupStatus; const MessageWrapper(this.message, this.child, this.groupStatus, {super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final error = message.metadata?["error"]; + final clientState = ref.watch(ClientStateController.provider); + return ClipRRect( borderRadius: BorderRadius.all(Radius.circular(12)), child: AnimatedContainer( @@ -69,6 +78,49 @@ class MessageWrapper extends StatelessWidget { color: theme.colorScheme.error, ), ), + Wrap( + spacing: 4, + runSpacing: 4, + children: + clientState?.homeserverUrl == null || + message.reactions == null + ? [] + : message.reactions!.mapTo((reaction, reactors) { + final selected = reactors.contains( + clientState!.userId, + ); + return SizedBox( + child: ChoiceChip( + showCheckmark: false, + selected: selected, + label: Row( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + reaction.startsWith("mxc://") + ? Image( + height: 20, + image: CachedNetworkImage( + headers: ref.headers, + Uri.parse(reaction) + .mxcToHttps( + clientState.homeserverUrl!, + ) + .toString(), + ref.watch( + CrossCacheController.provider, + ), + ), + ) + : Text(reaction), + Text(reactors.length.toString()), + ], + ), + onSelected: (value) {}, // TODO + ), + ); + }).toList(), + ), ], ), ), From 116649e8d77d996d20a34d6dcaa7f949e4a3d1d4 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Wed, 8 Apr 2026 16:02:15 -0400 Subject: [PATCH 74/94] Add the ability to see reactors on hover --- .../chat_page/wrappers/message_wrapper.dart | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart index 79cb35f..75ed037 100644 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/message_wrapper.dart @@ -90,33 +90,37 @@ class MessageWrapper extends ConsumerWidget { clientState!.userId, ); return SizedBox( - child: ChoiceChip( - showCheckmark: false, - selected: selected, - label: Row( - mainAxisSize: MainAxisSize.min, - spacing: 8, - children: [ - reaction.startsWith("mxc://") - ? Image( - height: 20, - image: CachedNetworkImage( - headers: ref.headers, - Uri.parse(reaction) - .mxcToHttps( - clientState.homeserverUrl!, - ) - .toString(), - ref.watch( - CrossCacheController.provider, + child: Tooltip( + message: reactors.join(", "), + child: ChoiceChip( + showCheckmark: false, + selected: selected, + label: Row( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + reaction.startsWith("mxc://") + ? Image( + height: 20, + image: CachedNetworkImage( + headers: ref.headers, + Uri.parse(reaction) + .mxcToHttps( + clientState + .homeserverUrl!, + ) + .toString(), + ref.watch( + CrossCacheController.provider, + ), ), - ), - ) - : Text(reaction), - Text(reactors.length.toString()), - ], + ) + : Text(reaction), + Text(reactors.length.toString()), + ], + ), + onSelected: (value) {}, // TODO ), - onSelected: (value) {}, // TODO ), ); }).toList(), From 5e07cec14ddc5aafe4eeb026d3e15eabcbf995d8 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 9 Apr 2026 12:12:55 -0400 Subject: [PATCH 75/94] Fix aligning close button to right --- .../chat_page/composer/relation_preview.dart | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/lib/widgets/chat_page/composer/relation_preview.dart b/lib/widgets/chat_page/composer/relation_preview.dart index bc22e0d..c90b07b 100644 --- a/lib/widgets/chat_page/composer/relation_preview.dart +++ b/lib/widgets/chat_page/composer/relation_preview.dart @@ -32,28 +32,38 @@ class RelationPreview extends ConsumerWidget { child: Row( spacing: 8, children: [ - SizedBox(width: 4), if (relationType == RelationType.edit) Text( "Editing message:", style: TextStyle(fontWeight: FontWeight.bold), ), + MessageAvatar(relatedMessage!), - Flexible( - child: MessageDisplayname( - relatedMessage!, - style: theme.textTheme.labelMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ), - Flexible( - child: Text( - relatedMessage?.metadata?["body"] ?? - relatedMessage?.metadata?["eventType"], - overflow: TextOverflow.ellipsis, - style: theme.textTheme.labelMedium, - maxLines: 1, + + Expanded( + child: Row( + spacing: 8, + children: [ + Flexible( + child: MessageDisplayname( + relatedMessage!, + style: theme.textTheme.labelMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + Expanded( + child: Text( + relatedMessage?.metadata?["body"] ?? + relatedMessage?.metadata?["eventType"] ?? + "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, + style: theme.textTheme.labelMedium, + ), + ), + ], ), ), @@ -68,11 +78,12 @@ class RelationPreview extends ConsumerWidget { ), ), ), + IconButton( tooltip: "Cancel ${relationType == RelationType.edit ? "edit" : "reply"}", onPressed: onDismiss, - icon: Icon(Icons.close), + icon: const Icon(Icons.close), iconSize: 20, ), ], From 133e613214ac96a1526745e5b1f324395006ad81 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 10 Apr 2026 10:31:34 -0400 Subject: [PATCH 76/94] add reactions for twim --- assets/popover.png | Bin 23261 -> 0 bytes assets/reactions.png | Bin 0 -> 13144 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/popover.png create mode 100644 assets/reactions.png diff --git a/assets/popover.png b/assets/popover.png deleted file mode 100644 index 20f468e4cb9640ad4808cccb144eb9040ef12f63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23261 zcmeAS@N?(olHy`uVBq!ia0y~yVBF5Yz;Kj)E4S?5d%y>7qx?$LLL*fuztb9j|cS88gwd$+fB->dXJzob^&h|pNk z(Gk?u;Ne~VooRN?_nPPR>GKoIs>+kp-+eUS^x5Y7&X4=g-zoOLQydbw(f5jqr;>u; z1da+0CPzodGI>u24~y1gDggy9JU@T2&sw{G)uK6GpEj@5P;Wlf&3$It)^E>xo~K_p zA*lTF?)Q63E$^Q-ta#IV{oaCowZAX*h^Jk+lA;-1&8gw zBagTIdVh7%o4w!fcssZAES)h!BJlZ4A5E9~-Zv%D&$j^QYB&u&6KzF?KrSvQ!*o z4es{M^iS*+(lIi#dGlIc;nX8Zo71beon5gl=i(&2m>mgIHZJ_6>OGy~*@fNV@2$3f zeRKJI(C=?=S)G4p{_`(+@PP54#{7yuoVl@{ot`FtPrjToWr|Do^>s^APfrUh2{HNl z^6v3h-;x$3uh_L~7N5MG&b*ypr$uMpx{~?m*8A@I&OCq`?$!cZID?N6H zL*0H^iZi5_SOi^<_Y{+o(ozu%{_?!b{$=YaaSbi2Qz3$ruZORVa}^a8{c`cReCBDL z8T%VnY`*i8+YrR%wZ#<6o z$u7Q}?$`a=ynE%hv+{4=8~&4bdKN0J_heCFitBgCC=p(zpB#LEIoL%uA*bo!)#*9n|9rODeEvK0 z(Y*}|53eYFeeKGQ!sKZSlv-Br% zUjOQxtp4I{`Tc8g%*ltByLO9}+}(PebLEEI+1nn~xwy3a5YO`Y^Y{F%`mC#8<-S+y zco#qOFfuQGaiUs?v9lpui9tF#W%4sORkPBR>P0`KN@K3E1wG9(?-rDhd9_MIcg=R* zbt^V>Bqb;NZZpn!pz!JQ=ha6i_=sKGZTNTP%a-Sd4 zD}>B!lc!9a=%^jOPGtLnPc5{_oKJ|-YIf54;;NR=P89uZarh8|H5;5 zSCx^z`T8*97wMUiD>p9Ndi|AN_UyHO_oi@wlIgo%h8co6scN$BpZEn@RRoWIRmm#k zUdg;jmrtT};kjQc%maTObY^cgVU-s32;@BXuV&j1yP_u^AJ;#+(zyTXpZTYjZ#%L4 zeNOSyso`AJ*G>kjznthUzwn?l`{kBSVGV2R=qt+uJTp02+gYyfYSVu)*^udT<@3Ge zN7(%gmM&dtQg!7;lxzF`X5m&;I=U{HVIbC%4D2 z&LE~(W#(+lk|(yE4K4>>3os}*g)h+zS{b>@@)K{M+D{+VudGhjPdxnV@Avy#Lpd_n2kNx8v}CN; z%(8##R1~zSz$J2HQft`PcB{va9%Yq2Juy*PL{3a^j;%;j$klag-Fh=t-QTe<`@7z) z9hu6iDk>Lhzu#M~%T=m8Cf}4JLct;ILB~s!L!Vjg%f6M#;v-xtK237fcb$VA z{>Liq&n(Vfzq2i_@}=ml?fLRg#h-ACCLQOKWo7>HBDptvf6(=Jw!2$HzP&s?|I)hH z-5QF9jX}Y|*`{CL@Bhy={g>xtwZO~Ed^!6RY^%Nq{C{;xJibQJ#&EgcOs~w9nN1hV zUfw#IB|r1L-T$7dlJ|--uJ>mDoVViFmzS5X>?}^NdVO~Lk^5(7n=aqxHbeEw?8nFZ zms{RHw=mYaobwpB>GF4mtjRw-Po9;~`ZUYMLFvI(4wly0({629utM9jw2U>-(7N-- zChw|428$d{zw%uD%B<05kL3UF=WdsLdgA%;L`Vw>6@uU@}b>)gVx zFE7t-ejy?_N zIQi~5tG)D<@26I;zmUKGubYLJLD`##gA5B#>1Z16_Y64ce1|RK6gMa%ODiwP4`1~< zK_`W9zGdkJo+llGi%zt(pA3ZCBaJoJtSF{CywQ z=31Bct=s!AYIEw|m&;P}?wQ;!S|h}$?{FaO*|a+w=YQ>!*D+i<+fT(SExq%V*P_41 zH>-=vWuH3itZ@S(2qSj}0 zW46?pXrbrENfu3|2G9A6jvRQyJ7tYOxTWF2@}{3*My=%WP8|)MCo`tKDa=yO5;&== zaA5uRAp5D;+xYX4$q{ig{LwP7?A@N9B|k-H zxhzdS*0)@L|F3Dk3N(yri>!*BkJrj1*j$mH@kdW^0;Du;WmsbVkUwQ<@C8l3hOQpg z^sG(Qulug;G&9$>eiL_Dv059d@|z38pR-c`PrkjqUC{2>iIr!3E&h6+uTTE00ItYD z!RYWnDSP*uP4i6N&U&q3w{PPKrVpS0$Y&>-Bph(q_52?Di;c(S7Ck=R*I8|TzexJ| zp4ZO23#P5PYW~v|8e}R=Q7p&$_dETO6&7~he!tK>G%W0r`22dW?)rF_#cutE-B-3= zzvpnct#`rMIWkvQPMUo)?&NHc)u3$rmBS?aUQB{P)|Ci5c3GeEHh)!8Q|1U>Kk#Ci zpYhyl?0u@%WjV|H?S46U<;DG1^JIVo9k=3x=f`(`oH5HJ^AKmm=PfA}V$ks>s6)hex1XU{IZawR0t&B%Omz)5MSlP?5t9B5p!W5*1+x+jKG zhDjp6vus+~_$9M~bUW+T#qMsiDt+g){chRvlssP3!cDQ@RG=Ve(CeUg`?lTBAI=Ng z_Iz9xo$=(v#Vs$cx3X}a6{-DuHRo6IPorIH()Ek7!WbsLYP5g(^5wE$YU{lrjl2eN z<$_mVG}AKd#B#HzDNM@p^yWDE8|tD5WgI7p^UlpOlK=MUW9p8d@t=4Vrphm0_C0ZW zxP1Is_ctLXidRE)?ykD3Q>FYfv6OYCwzkFXe9QVD@3YR8hqTUdH@ecjd70lfWx)v? zJLH)r?X=7~cVOj#4NQkUMMaMB^hidXHNCimV@{fct?!O~pZC|CU9^oJbmHiVkf{I^NmAV=l?@qGY7q2#Bz6vHwvzMh=f> zX_uE6F(-4LYM10zF-^43)<|M|-E8-ReX~lq*VUstT_!wJY0h3DIq|I*iwYBOM?*^9 zSJwG&6IU(Zn09T(#-BSTT-JP3vqtPI>tt>1D+W7HUHa7*WT!4z;LWnk{uj$upM1yP z{BjGz?^hkxJ)W@Uvagi+GV%F!^91TYcIT&EzMuWyFw7?|nI$G8v~2p%gv?%5mH8`F zrrk)2>E8I#iDS7)S<1(!b*r4DZf1Nhp1yP9-zgjdvA6cJ$5wgrcXjtJZf@tR6j4b( zKX)!N;a zjy=ZE`SK&X{KDVwcU{-}dNb(cd>KjkDbr@T-Q8Wbd8t-^UDztswX^O?-Hu{0Hm%Uy zCGFTHd?`&JRD4cybj-xlGV&6ZB^JET5>|M2c8Z8ex!J9Gq^qmD^voNbTN|_0&;ELo zyxsBlH0iuWoXPsjn+`e6s=9o1PVnOP{w2FDugx`FwPMABOPh?WYM+^`{r5aS>cqz{ zb6hv4*Sc-&mAWXuCt}~6W6iucFT1t2(TEW%Kc+ zkD@X&IcxtMP2c*?`t#{Y_R}s-S-noI*gQ!?{aH$nilmal%-M=+RpB!>vVK}5Eq40S zEVc6!j=INeY2XTqsYni*{v>mcmh_FAPgc6iEsvZ2O@zmH`h*1=B8!4QzC7j_aQ}aW zSnmC;hg}2LMsGhgbLPu=-YqSHa$;@~6QVaRVX?kA`~BY7?OtyqtY$BjHHfLNuXoC^ zFJJ5_yl&Bj3ju=lAFdzWKl9DLjg=ie9ZURVt-5x4_W2l8}rN$J_yOtUK94ysQGl( zhT=kf+uuy3FRw(F{HsY*%e}cTRN}w>llTQq&>m9!FhPs-{C1HMhFa3SLjNV#r z*q_&2dhpW!**~{63LkiyeC#7%&5nd;%)h=ZKRhR=@Zh3W-b=qlSBDF!NZeR`#;~33 zhGL>wi`$VdQgTv%=m-wb9!P_pW&S*FUYZQd3V; zP^ipVc!_BakMvQKMRIp{U0$X;TY8tb-t~Q3cFvq5l$N7dlh>P@Gw;xiEzYT?s^=d6 z+%RkE(ZI_4Dh0;U3wbocZ(iN^kLhZJt}=eT?mbknoSW zX;zi5LgpIiBrY@kzP$3wy*1xg_FO%}$jE3i=kn?oLRT8s9k_8}`O-VnmHgP7SGZ62 z*ggB=&S{fo8%DG&SJeNv@PpBpfPeaL3S>N6mj;z4E(=!O;rN%mOa5tRu=>lJ)6*no zGZymt@MS)B=ap8RBi+U)qw~g@>+grJ-$i6ZT<-rhn7(iB_qmR?>-T*yD$Te)k5A6d zh2QpP#@gSqmF@O3LnAcL&JM1yX!m&h-|NP-Nrm5ZbmO+QOfy_;>h|{bHsSMIb1pVH zIY<0Gnzicl^Yb^)@c0!c9TQgf4?6yBqKL3?@`W$G*JJeeFg%tz8YIv!x$5wrrkYz@ z)3;C1+F&8Q`1P_F=i2J8&#a^a74&aJ2z)#ft>ZXLsd$H*tJ@{r>-UfReWPF|Yq!r% z>ABdieJMYKn4_m&a;yEFzpDS(I_FFY#8X(o;!LOxviL z+{SV|>v|STmL5}cagB?x!HS5u1&$Fsx)*=XInVh@TxZqaUtiW9y`y8B{mL$+VS7xy zaw|)Fhtks2r6G}#n)?nvn_Bc*Hiw z`6gGFgkE3m+u~RK^yhz$XaSCGOBOv+`t-cm??7R~e#R?P#J97{)!$gqyIj87%fc~1 znXy`MMtMPjz~f5Ey#{|Rn?Fs{YuIRCasKa=3p+YBxY(it12s1sQe)yS2}+uD&~5R) zzuT|K|NSkYoijthVCAo%#KeUaCI$)@UTJNx{B?e_@rvx_dWRNj^uL{2;V*Uk=;fBe z>D;?tS<9bW6DohD^ZVVx?PbP`_xLBQO314JyZ2Pi?+uD zF7*VsR;@a+%sy`6q7=6FH_RPbj}n5!GCgFkRk6w0mQC5UDY^5V->qqRezQxqCZxPL ze16WK5AE~oMXnxP`>QOhe$m{E%I=Z|+g~q_(-vE^XpxfQ%39;{z6!oGMW>EdR^+`qTDjxzvgj>6yxiZm7w^A(OYZ%ir)5np6VK^Ad~rMce(mC1`TL^B zwpG8EE9C!wXsPV?U*BFGo;_>VQAOA4gdEj3XSrU6O>|@1G`GO0U;gj>ik~bGm+mP3 zu6J?zuW$S99L&7))Z`AQrJmon_v=b08{w1Ent}ZJY-?0k7~bBNcliA!|1VGHtUsDO zA@AUx4|ZSQ-ktvJ-2cV`Ki`G3*X?#RF!Q+EJ?+oBTE6#}udG(PtZ;9~jKyo_n*KX$DnbS5djFWg-A7c=2(kQ!q-7a<>#vaBjO#`mhjSF?&$iJ`qu`fXL>Qs|H zp5#n6@1To;G90s2*O zaal}2gu~oh*-LyvV(**93NU5 zR2Wz#uzjuEfA8;>tFfg=r#||7$kxENRdEr+J@p$4T*U;9r<`x!>Bv|*!QgDj^TO?G z>UZ1by{KYtVmX}mDX+5cEMIZ+ne8HC?yq9_uC~S$@hRy%Q<(F1(=-Lm#y4M;|Gk_( zpHn`0%5gtguB{&4*Yyelb~pNctlV;cnx0vONYGWrZ|9m{os+RGy6>Dby)q!0CCemL z@UYYNU-M13+Nf@H&G2#~e|5CD0>NP*CuYql%T5*X^dH{RdfH zLrbU0`uSY6EYsO|#rz)wG@Ke{I z=GfSv(bu2jDr*LDZ zL+M6g4L*&dN3Y#>IP+CjYn!36d-rFbT`bW-$98VMVxoD|L6ybdS>V)+Mck&z#-;Mt zOwksxtE>du9G7fM3ro--VpiOh32jc(7D^(j%2+YMQHTS1z`i zr1d<$-P1?y_O<;7dE!J*9cQ-4Ht8#WNLQEeq4l46LZ?N2 zjqk|Z9UsywzSM5XvwijF(^o6JZ7lSC;IJbg`K6GAv_WIahVuV*x5^9lbFlR|?~ai8 z|M?6L^E0;H;vC5>Qa7$Jty_N)KS_6ZpacG)z4`ucLe>0iUJXFk0ly!!4I#WRAR zzmk7s7B*jb{lhCaNFNt$5*R)M)D!|F)S`J|#=gplomad%L8I?YqA%_P6)@U;A7)@o|uZ^tsED z$z5EFO`mU^dBLT)VaK%vIyH{FZYnw%tKe3}o0;k_H|y^gVDA)d zOZD-xEcmeC05_jDSkXyA7x2)c(p}lb5~j;oU=O zET7wbd$fh`*$2#-_swDZ`GD!O7pr}|z#%x-y8d2_;rhML@1F6k;QXYNcTz3w^y`C{ z`d^(9x+WM~5@HY)Yq0->ufDndL&@3;g5r@?k1J-KXnMdAb@F-nh96FU+YMs44*qLs z*s_5&ap94@51ZS&ZgaG<=o`fbiB%Z5)HX@)`Skeu-sLxJKdf07y)Ecjm*L~DtA*~1 zYAQ^Wl(;hMR}1sCQ%>vZ&tLkUU;o2zh4sTbhJ8yTV?V9EsG;y|vE9-Q-;+M4NqsNB zTitPZrB1~LZK=hDi;qdAK00>l(XD4|*Muf86}Ru+Z|C^;$C2j#OZ@S*>UaK&uJZr? z_4T^b-*xP6@ECpbyRuZyefr(a@x>vEGtaa3NgAG7V41UN3d039^Pfx~+&WAI_~v>W zxg1zMbGFt5<=_qzgFcmMH@t3eKD5|9V|MW8gFjVv39mdHZMn%f?E2cIW6LJqTT^%B zRnzAEf4z1rYF<$E`%km~i+Q)-3k7j6ip{yVDAJ>lf0C!!{O1Raf0b-H7AoC+K>53^ zM%@R673S~vJmp*bd52%Yq+vJa9fE_VVr z-rlXg9UBrGy1h90)aMtMr{zW;3D-H*VY*V_7~iY=+vTf2#g?cZk6f`_zWUOVDDRsN z2k!paufJc7PrUhq?8}@Dl>%kz+MIHej#mg??fIYO#KzKkL0{&|v6;H@-~Qd%kU9BV zTm|iy-mTMm$b2;Pnts!@vez%g=L@fLcRAgV%#5{aKP|qTfBf3}fa1pv|IW&2vcK1Vv^LK6qgG;J#^{kYw5*1toW-)!9BRw1>jW=7x!Z&SXh&bJQQ z2Dda{S- z_6wC|ma?WZC#pN|4sfkKu(CnqxnOeX{y)e5cXmjg5M^iMTQ2HxtRTwJ+%LKGp;XA` z4Z;7mR;k|h-Q2Q->!nj?VdtCzi*pOSw|%)EVt*#qU0$^F-n0(GXSX(7x6NrQSRnV_ z@Sc53$YJ*PwtS_JGdc~MHupSA*}CxAT*I()x7V*+lr(!rMCMz5-m-@|ISao?DewFn zy&nS5BS(+*t`h1bR zU$yU+4GyK>-UwfAeZ}i162YP3$)cmkpnvCf-X7PSY4hrTvY1Q1fvp8_-F}v}<3r2@ zyMsTL-K&{fcH_M5$1NO-cujxie!rK=$+vxX{i`pU_pWS8JL|S~R=HQ6Oq#cF)+tV{ zlTqu%s{PY8KTkQR_}PcMXT7TQ)4vL)raGK2uRSdDe4N~(CA7-DLfDCS`Hd?*)2D^2 zGD)!r#PKGcR0w@_OFx!Zeop?L|LdxK%e2?-JeIdYd+y=C`)jRzx6H9GHAoSoM*?Da#A19}}_%r|&z`MIn!Ny$)6v-!H_#umr5eHC*%q?nt; z*Kiy;d($96zIFPCYME@ImQMv%O=4%JJ-D#0rL`{pjq1)y`QqlqEJ+?l&Bg1yg3`He z&RZ7fU(}~o{O+3t%f8?HmuOj?OCPmEc`_vZb|UjZKepW*sopY{8N zip=bs&s7byml!b}WaC-J#`M_vQCz-KwW0a_1rs(%o?|-~VdS>_E2E!~jKU2?LC-aE zEUVQ|-#lvFboU?gS(!U*kGZ>^o=}aJ4y$T@w|h=~#iX5)wud(@a5*5vbi%5CPd>-u z)-{YDtXxY(UGy7mbC0f?suVMOyP(XJg#67{pFOLcBwg+K%gn2=@q6pl$sLwAKWZw~ z8~5p6m09-uMfnn|?W-Jr$HW%&T~QU|TV$TWpZXXyRIRR%pnvl0YZI#KT^k5jSf{39pNOp-p~wnDO(BQS08_erHYP6UB;x*RBD+0(zX@JRJ^ z#d9rdtoF~^tKL8Jr|2PSDTdR-5wUgjtMi4|kjpoGE-}i)KD( zj$f$pm(qjMGiSq;Q)?T|zRy`TMOko8e6m~BxSFg5_!bpSi}4;%{)5m@ZD!!MP4$clNEWVTONqoeh%(KkPej zQty?>LZ=gJg}FU?58tg*n-h28`wzP%&p6aqt>OK%YDPe^)g#rW4H@oRRrpr)J*&xE zS)*WTqT+JITSrGr#871a|H63}9ZvR1Oj~Mozw)iG*OLc_Ca-%uA+u~ryCpN@qW$$B zPU`%sy(#poM3P6dzrR(H$K3cLSwA2_Mjk|n=QQmt3zYwO-Q zmIr38`+4T>nC!G-@1cAj})aq!_s`7`p$8$NTN3NxRb>YC!=Hs!z0xzw-s z%m2Hx^UJQe>aCJlGbea=jQLZkTZeo2Z!+#_l&gDXxSUV;=j(*}dpoPu)59umugpDt zOoi#3!-3y3W*Y~rEtg)C<|5;{G!Ki`=kmJ z?*A(>_1?kRJG;ZgA^pe9_jNzwmaJxslT*>fPYyWyZLH~Y>o>7^UuO{TI>{xi;|>B1IAGn=f+U#P5;d%$~xd;LC@dw~L*WXd`8*kPml7Pe`qHW&0*ZNOW;*QZ~p0TYq!kVEYAMp zcX7k5lPsESUoP14cd+tuzkXGJts`Y#M)3R#>toKbw(m)MZgjr;c~#7%xqULWUft96 zm&Wj|G^zOzFw5Xw(MJs<-N)6I&qJKgSs3)bi?z&L%jxCsz1Z49Q)^Re{*66>2L1;B ztn>E8{%%>i@kN;Ik;*$vf0!-y1TWaJr|Ea^Sw;>|?~RHM?>4wcCcV48nqStotY=#B zI>RT+o%iSOeV7(_ZB}8#D(8?RENoQ|9x$F;{heRYUQ%dNTU*N|tz*yU^S(Z!_H3K9 z>Dxv7yIA`7Dg71XkQP)~eY9p>#MkK?4E<)?gwFQ8d+X<#my^EDpYcM%Z-KObebL+s zajOE6f4B2@h2A-S=T6<_TqWMit4vp0zW%|sk?~+n!@Z}xyhV3;-&~L16Mn%VLHTR) zSvIfzy2_Zbn>CJen}Q?~Bs={a@!5lwDlfvpGGRb+fL9&XT#+WtpmNiaNS>M}ihl zQk1qXObMI!^`^Sc#kJz0XaBsvt@LZR;jWr57F^FOnoqrxPu=h%Il1AeXy7~DUf1n$ zIUI7;-#m96xH;eP#HZdl=_fwb%v;Ljda2c<;>8EWs;pziw~p`s`NjLy#kWGgrYz0O zK9;zWZ)uWBP3_O#@U3qhPBk7`?Xm3ro_}Jho0a=7-S^gisrWDK7uQL#iU!X8!OD?J z$8T=#_kV6FtMY1Nncdd8D}FNP|NN}K|A3EH^qzA0fB9-luk7i5?ADulOzhN7*8kcP z#xLgGekbx?@x z%!kg21xc6fi9h$V&Gu`Z$o2b=S>-Q@aNOJ>%HBOC?d;L5&3is*eYt()HFG zKeK;I;(cS@(6-S;NKnwxF!16d!;}?mUl@*cn~8iC7TfMK?Tpm*FVUIIL9_K7Uv=h+ zc<1l=vMQj})zz)@cEHre53=1;4&5l=y}8@h(ELG*#z*ge5o+_;6^%FR&6M_D+hiKA zv$Xc05>Mt5*DCI&SD}i&4AE!Jy&rE_#<%p|=BmhL6H{wmEKpQpwp0=I@V_B2W0Bd~ zn|fzk(mw=p{5>jjCDrchZH=U>7SH5LzTL5$$SC`sLt~>`=#L-G{X%xTmh8EILcLrv zKVt!FgVm?U=k3>qp1UNTfcz!PZ_pT*YpuqG!A$Q@`ZVAUA zZtqk+g9A-hjx|p4*H836S)}k)fM>og+EE$=+DLR=C^C;fm|A1^ahM z${g6f!Smw$r!V_$6qPn4Z@h7M_rrv3+Ky5em+soum_BhjH@EehjP`!Hg`a26EP49l zR$T3`SksKyIA^P=z7{wA!Dp)fEO>f}xjdW2YyJZZ+|K^Hcp~+!YK~3XW^?_Vg}X}L{3v{T z-DBFmfOFF?-)x8sZhI;9^?q;)(~a_kIRUZiYHD21?snR`CPs&u#VyO8TrR@0=lnCa zlgC*-dw)zkTj6L@dW^T$?f&`yhcyc(T)A-au|u($(RP_KYxbiXrxdF>f6mU2-pqWw z)IhFv!|yO70qyQ^?`c{hQs!Ab9>R+R%1$h}F(cyb)?**%lrC&@CKd}Goy~1=Ut@ks znnA=8$7#nBePxgRy0*1AJaPK3lyB)@&eYEn@3a*Cy&x#=W%DQ5SHS{HJeRJwzVTVu z?CPUSLW#4trr9-eZrC_YVQF$+f_%s=*3ZXxvrRSQ(DL+ny5XdgaO2M46PK#F-ZGj9 zWiV^~h<>*(Hf2%%8M6a%wtAbl&E6!V{9x&%+1+Ptmuzz>d&|FnM}_oT#peydbN>d* zNM;CZ+IV+=q|%PbvZ@d6e!E(JpMx{~$_BxZqv>}hO`hB;>?pc=(YiGC{%J3DU%n_j z+b1)#Bk}x!L;r-V=QdP2yj`MwGxz4Usb0&cTw40(#z#l76 zYkvPNpPdy}p>}BDle^|7?j{d>n9uxAlooz7*DSl(DZp|fBwvZx4X+YZeL@W)@NP%LgN{?5Bp=5ZO0>sqBG%dREbil4D#y`OcT}hHK7GcT7@RA6*mw`IU#vC%a=+ z-!FZ>BdG43oAe^5aP_9%m5yI>Y&^2{ z=w`>+`-p|5=^;(H!NL zD&DxsAHH2)nt#(brXro~2lul^yEW?zbqvGQ`#cULvYc6={{C8^dXByPrpv4+W^H_N zEjlpJ@u}!jCC+7*O;-HukXVm9=!USRPU3|M5)t z{aS4qFM)$;Cl(w$bHhvW_xsk%lT}^|e%O_=)Gp9-373K3y;D_p-h@or`gToQ$o8G{ zmzst3&iFKeCHeF{pHvnvh`pB7yDj`LaJ*0(&RtW4{kZdm`|&b8HhG9Dd| zi=SovJNi@#8$;;=(Bh;6#sV9{HwCQ^_Fi?mJGOnNp>go>@9D>;CC%FC==FHVx8jbiyX`yMY-!=1$*o=->2v+k=cKJ%j2tBZru107J0cdF>d=S`{VjK}>M+Bb1MsZ;4qIQA@im*tI}0%@y%OnGVK zw`1|T2QPT0_4tQBHj5KienoGEHab*dft+_`}>3P2GI+mEY@Hzm`Tc^Och)NjzWpu5xzAQi`dvBa@srTlk~s$9cLKj( zsbM(~mM-M})%O}?Ibsi}hU=}AdXvX{{C0$_z zhO$j3YFNMRI$bgK$<4F(($>eFtb+8iEm`(V*l8$J6XzX!u20o(dPhW7_K}D43L146!E(=n9Q5xm+5Dw1YSJ(rm3Oa$h>Xcu8ze@FOIft=3b8nAnH-m9Yc~CQ|0Z6lbV+aeDIe3szopY|dCfd%wkGV#uXm>=fyXQsuraQ; z=2|fK=c=M>Kc;@3xjo_e9-cYbUQ0GTl$fNmO)_8j%$hmD(Q_Vtkyw}TIO%$QspJy( zJ(j)8ovi@f3yH6AX$r40uZ1eXU)aycNu#Nio! zD6moMwDbk(+Y!g#6r8P|DQzrNnrXN4&&-<^zmD_2KjCKEnY3$GxO>>9#itkQTRf^i zyjr<%+1gz#&4RZjicVj#?#g(XC>Y4{Y}&nKjisfsE`gky%et!XmtNCKnP=tDZmzfZ zvGui}R98t+kq^g?cRje;=(teijN-mSQmO&B_dV9!`8QQWLg-|D=#&_XWFhZY1dulXLCIi zR;K1DT%D*dt!J=_&#d6@`g5uNzS7OA($&(nmOBb6Tue+~U+J&%pSEywPvVmL+@J3L z!QW2)Em72OS6(pF&sIc6?3_@v1n-VID^t()vJz4I{eH$OEl}7{cF(#b>G=JS3G=m^ zrkktWd_8A&et^dfo^*be#Az|j5qFPqw`xrYUTfi1<;-*`E&O9*Zkdwd(moam)#n|2 z^`2Vd1xk%lLP5Gyuln6hnr|5#m=dC#b+=k3w#xOcQReOCwcppM?)@!bRbQhYDiXWE zh)*lGhl#0KQ^jPCn&1B>rAL!{MJHZYVR949xV29`^h0%M!9SL%MN1a6siZVCXx$Af z-F4k+ipJ#jWX7v}-FpuDscc-+*Yoz^*_j)w9jvT&HiXQ%bWGIatl*`qM?IXA>Z79# z(yRG;^PFPc??2#jE?sTMv+rdkSA%Tt<)0FhW=Td%e0{*%93Rl}Ft^fGDS?skkJ`uE zNAj--pLjh}deNQLpU=+>{d%Q((cg-Rj%jbRc3j?Ud-e*u;=zhIPhrkr;m>_cWK1_ zE-ZUDV~NRat!e(=EE0@=+-hr!Z^|Atyd|C9sB?7^*QJ2tqH{#k@9&v-CE&bRn$?$@ zxwFppMa)pW+1Ajtx8|!T&xXEVKUi;sZLEn>YWUt!zxdnsZPxNpC-0OPw*Rh93|lL^ z@$>Q}j8P&zy1Ug>W-{O2{#gInLifDI!s`7u_iny+<@cr2{udt}W`A>U+nNg|OhUz% zZ=A3`COF}CMs3WEdrS0AsD!#CpWbHlH~sF%>a&TtrJC&S--4nxCUO4!`8)W3o%0%} z1^)9JMFa&iLnF&|L(Z1HFuzw5=(2vszbD=z0tspd=GOA5+tlfOQ^P}+AOX6EnCaKER$XI=F#i`MHd{q)I5$}(wB*_p(|Ywi_}dw=}+ zQSsr{MTLClyHZonoRx^Hu=LgoFx^zvZuZHCt=-VcE7f$(Vi}Jf$L#aj6STzs{Zh`o zw`ZD^x!)2&@DOwYc@~YHI447r0qn>)Xog+uP1wS?XQ>P;6h_-}`K5cwgPN&Z}gideYV+)EPvz%lFKgavb#*7}dhkZhnp|mLqkF1(r$dsbr|)vj$@vPg zX97c6kKc;* z`^$0p{g+=Ft5?conv{K+Dk3cG{afNTfBIh4Q|s=3*#6^tiNjqoOFjjoje-@D~^Z2~DkPMT2HJ4*j^0T!QZ^S&viM1_^-Sb3T$}UIeYpUtP ze}AiAY(AfM$=Lt@{DioK0Qve)dSBn%>=$oY?!x{0+Il6OdFCIzYbg45dZO7x2^KB*TpS}OTCRS6-HGDZ!pTNe- z&sI{_&%8c84#+HiR^xZ`=FD41<6i#1l)As`BkvpgDQ0<5-uKVW)fIc^y({+E?2d;v z_d-qT4}LHCz4m)|hVZp>C*OD1FPojWXLcK}?AeWcy!ZV*T;w)2;<^@87ud`h4jvUoZc$bQ0SA zy01NQ;$!Ffe?P5j&nNHwI_-Cs>qQ3USw&xaMUE+65SSp*ldGF-ka?+8L{{`H&;R&Y zKmWZp`MYxZ)*ivPjX!3KVU%4J<%#(cN*`C+!Kle@ln*4y0s z5_{@wTKa5Tg^1knqh=OIqQ0;=#T?4&vs?7B$0Ww$PG*m-{%iNQ73Mv$dNU#{ZXYx< z^6OT7b7yO>knrSISzRkDw;DGd`SV)aY)kAuuPK|cyDb0l-MZZ9uANoB51E&-zGMw@ zpMQ_J8?qVS=gF4bOszed)UC|6x6KJoe*JZBP3DxhbH2Q|7kx(i%EW&;+*g+yOD=z*pT;zK-L5c? z)D87!*|VH7S4tfdm5}gQp{!>XXCUbDxtYCRrN?`E&)K~jw_RHse|FN36vO@%!a}ba zo2y$}mU%Zv6uDG;y;yO-Znl5&ala)Y)3gpY{Qj)QaLMjX#;^0OEiGNVVd3{Oa&PQ# z%VF^mJeHq3-{-s?-Bq!mt^d9d)-^enrSx55)0w9L!4vn2v~t^LN>K)XE?dcDr@t(vwbMcggRc7fDELa9Z{( zZvVbHb8@`=vsJIG}e*PSv(*vrKJFSyl>t-*_;HucZBW0`d;XXCUzfd~v@_E5_L&Di{zRAF)9gzUa<+3^lU=UvE_~y zw&d`zX&*b@zL=aZTGyxf1VEI z@3Hcbj9O*cvg~!hfy9K?y~nP;e6sTKmg?_i%qy3ySP>vopK*27+A@wdi-+lnE|N#y z&u`N?&v`2H)Z6mHzTU+<%isG&ZOajitv;)p)9st^Qoq4$V{F;;bKRGguiR-KwfNt{ zL$jQIT})bdXlvUCy%$2~bfqSU88=(c;tuY(U?_nQs?TkIlo^No8G?u$)&1cFDtKYV$Aee|UPfT1C&RpxWO{;pLK!7^6;ll!l)NmzeWU(?)tc0bP+ z0e41+xmIP5A2U9Gxp(IJj}JdHB+5&lcQ^j__O|Xj`KQJ;rQoKEusgTij}N|fri;bg z8INt)y>Dqv^4=RKIiJ?g`M>rBe;?DG>hq7c+NkRGO7a;OO#AiYayjedkUpj>+x@5A z`n=@#y}6yY$7Hl~t_ndj|u6=Iz;(BMUXrCEny6ETTUx7!Ci%go8ZD?lC z=OsITiq~WL*{Mka=XURTFun3k=5(b##vRgs4U(mvrp;e1dc({}t*+>CJ<~6PPcRZi_%cq(%_da>uV^(^5L%3R==FXeR&lb!|NoOn-y#4sbzNK6o z7i<1lu2hkjul%OS^o)0GpxPYQ0)=Rc?j5UUU6Hf6y^+sg+qKdSwKq0qMusvgD?Go~ z#aZy_-kmPFt2;YV@=DvzoMBF|2%TfLti^t5&g_I^%eZ*NQlIL_L?kiof*I6 zV)Q$k?{4fV&%1y0u8Q$~8N6Q5>sgrojWrb^3sk1Je7g|&c2|IP z#FURG?J6D89q`b31mF2_jkhmYSP8)pV4)YO|X( z_8t%LdTrIQNGRx4?EK@lC%Ao{tz<5#cx+^~FIhMA)5QKJrf&zXavx2}xzp&l@R31T z#A24L+vPT@+xPb{Ij`~Gmbu2S^6Y1U6Q?cQ+_(L6Z8!g`cy5bmkwRg3fy&0~mgXn# z*u0-xx>~Str(#j-(uAFPrW)2|+cs?qx^ZJw#I3d5iO;1)WV{&XoIew!(!1-5_US4y zCXVN=U#xgb6K+e)`j(V;Df*Jcu1~Wr3moWQwV+W+(IR&O?*#!HE$=SwqwbSj4oC}5 z;8VS4@w(^?cx>jtVZjM!&K_2e;DhY^tx#r~w6pT@n$w1m4VV1x4o2qu^Ajv>Alndo zIXs>{>)ot(Tn9WjWFy0P@{UbS)6)--aVB%nRx$H)yJR+l$E!XRu&5M&{=hly@MQ4H zkb9sFDs;5m#%jLwBoj6^wn_8n+yDIids2VDe0_+V-O@g3xuTYptFMNfTQ+O)2meSOOw%HF!L zA%Co#Xe%X9-g&FaGRe#r)@neaVgtYVQS!zKu z=dWMRfA#Hg^_jEpi8w`Z^YaU9d3f@+I}{djii(QfTFV^1amlwUGZvPuJ};}Sm3sAk zH}~|5mtudvz4rH?yI(-nw!f0g?pnQQ=;C5#%HYkPV5D?NCSdoPOK}`Gx_AC$Yi&8Z zA$<9owd!hW$8IeOoBBC@Z&gZ)$j@J=VrPanSZ)w_#w6XlX_L|H=$*9=`c(x#7-qjN z{Lb~~$6mQh>qKKUG#B1oe*Tt;%>P$fQiTsROi!QQzEZBd#-d>>x9*IE&s$jn=b1$A zcq8&m{E6@%)ejq6@7#+P;?8t&SyCdh@u}{H&zlP#b#`=YSb6Prb@_5@wr`tO^7&W) z_!!un88e;Fj5qtk!Q?9~bHn${WRtb>^L%5Yd~&m3LG?4X&?R3QO7>0{;n6eMQn&5= zzBimd4{Z3i&Rj$M#l@b}?ZLm7sC|9qx&HC;8`76{OU`DB*}qs_V+o^8?Q7G*PYbIQ zA6z@bdrtY%v9R@(<)@q#6&Kd9EY4f*TN5Gr@x=*8znAlOn|(Ub(OCMf>hm4$E6-}m z=Fb0og}q5z>*XckbeqzOqL`kB+eYRql$VAtdAeZ!byQla*-Q03AE~C0+`p=D9=jWZiQ_$x->9w-J0lBPy{}{tcO(vM@ zT>rM~$f`~E;xeMnzYB9_Q15yn^jrSohF#2hF#_p-R4Ub&@=o4);QIIl^R4NzWs+at z-JNY!`X;C3S@o9}itox_UOM`sw(_GAlcVD`*=6##cgnUJ7?+5M#+_Kc`?%j?S?Pak zuO8V}I{V1i()u+**Z=>mmfidHNOwp~L`rD5+4?y3dF$7$+qPzsm%9M?nUhrfc7l9H6XmH$0ao7g#raq3MoCQFq`rY0MDOS`w4mPSTDV{vEt`D!hH z=9ej)SBg81b*kzp&6yN5yHlBWoe%TcguD)`Ne@`mg=8Mte|RnQ(Q;>+Evvix#iifx zxcTlXDM{sUy`RuOUR>->oUmwd@`_o9#X?ul z-5zYt*cu(vwxsuL!%~YWcY|kHmfBd(jQn!-_U0AE>*Y*7z0%(DJS(dFox_hER;B(M zWi~GLY%JcReD%zoX||tw#5Nt&w{?E27VoHgyl=tg+hw~gRRh?1z<=v*+LBO4UCrv@MZ^?dmjz=4NLNTidx0j&`!{?U`?1Zr1EK-%@v% z^kJWkk)P8jpcXEc0dktnox3_!s+&=&e~N zdvtp~efp$ftUY-`!vf9e{fTYNS=aVhva+%ThJ}gEO6dUSZ6xl@q# z{>{$KQ|8WfUHB$*#Xg#{2ogJxX@_3{}*_ zKGQt3EVAmA_j(xUMsE{3v%VwQVV?c`lV`T~Jo-8F%9N16c4bbUn=+~eEamB!-WS&` znk&8i(w6G{g#mWjnSI~#@4LRY`^;sUKhy2HeaZ5G$U|!EYRmj*NcaK3fL9CZrtJLA@b=$7 zb*)D`rsb}e@{i5mw@s`i^Dx_`o4VF79xPfaa^mx)x7S{ORowRbJ44^v&KIhaY`7Vp z7jRl9KRe@myHZ^5!#lg4Stgl2tTJM1R$5)M>{m+O{ju!o?Y-K^Q}4W}S)}a61;s+! zwwZyi&2lyE%=(HpZ9X~waa>rD)WqcD{oK!{#0MO>6ueV|d*%7-{lR518X}X#UO3g+ zJ^jpH^5+NVsq}UZSFWD^?&V^-(H#}D7DS{)d3YATxR98(Zq|&(g0jZ50Shy>H7mS( z{O<1VE!V3`_D@;#!%FsYT+s=6E$)?Vhnm-HK7Dg>@$B%mOJiqm>sobgV|PtWP}9w< zaJ?wES~s3WiNeRZI%R~SO}xbztF|3siaWEv?zi%*3wybF6Q4cf;o6>Xo~Pu}qPM4K zZ+`v%kE`_)uW3Eo>wn6ozPHy{x3>1Nt<~O-@oPn{r5%bm_(AIR%_jkAC!75{)0rZ@ zCm!N4tUZ!owWp-j`5{ZQ_VkCpWx6(Q)-Lyc_DpAj#V*OYX0|FPH} zdTP}?r!=2kOFYU7&y;)!2}7~J};G;${8|)@r}pjjn3{17lphES!r^}TrBEIM6iVPD;I&})}0IM z19;murQBQ{{yyq&aORKI-)1fUzp?bKTGfN6FSmT1-}~jWc(KM*S>r=rLe6usP2IzJ zb6=kE-47pPw>aO+rx6_4f9B)h(NAf0t$5vuRzrv4ATvRO3zdkLvI9?q1oJYyGjD z^E$6u#I#_yPS4J*yAmICu6um$q)k|W;BBW(5nWeQD|T7Vn(*X`jOY}r{=rCB!rv%Mtm^dGo$to9S1YRPLK%O3w{^ZuSWVzlD5?sZAN{f#jbPS4GF zZz{88&m#_&JC~k?#m2tm(DAu8bM|cSfYnDgX|3AjQ^%Ix^gllwwqCOT^ZLc- zCa>S2GQIa?QY{RFTvB{lrf`(4+4<<$$}jBtM>edMRJNE^@~VMA^)IxHLS9EVLg7E6+T-Ez>nJC;xZe z8pzrFZMon!{gAn-tAj{nPTb1w$H#kHSn@^8-3uk)Je8CruFE zW;Un3`w0*0+uK{88>Rj!XQ>Wh+gP=sqiBYsSsJ?liw*bFsw-`b?F+ZI?mD;p5_pdpY6==36-al#m;@sX{!Rr?? z&rm$O^mn}1;soV`^Vm08Tg!MoidZ}C)-qM&UCdiv+?Id2@&CSnY>68TrEiNhd8O

c{%tke^yzd;_@oC$1Y*#-zd%I(Myx`G<`tID@U4MSm z+U^fN{6u>}QU1LtwrcaA{{DAmi@(2jsMXuLvW}jzCng)_-k1>WmA66HayD0%P{FgS zHyC@i*1ve*_^?i1Ow8|NVc{EvN2m3Eq|dO-QrY`hy>BzKr|N2D>CftYT9LCX51MTc z+O|4%W4Y>`uM5BB9Fps?Fbg*^%7h;uWZo2id{ZTV$o|!wH!`>O->>sD=zYl4dHjwc!_%2(kNZhf z-&uJ5fr3%htEp2Czj`9NU&riLn6#g9@%{RFix$rj{k{7C*+rM#`?-%@UDLX7`pTeq zf#*EGpPt_7lvOU0YgcwD>1w)eMN`z81E-km&fJOWlhd#&7MDMoh;ye_oT6 zv*KkyPR~=Wy<3jV>@YZa!=~ij-AOwW^%k6(Tq4N0Lch3pQ>!d*`lkigbab^J8W$gz z5>Yb?e7!GUxlCfj8%E^ zbDzGs+srR(yyxN3@V-sg-)O44AN!cJW3FAv_NjLoLKLeLOk0XOFQ=dMm1ap^oxnb8 zSAgm2IsbP*S-;EXQEBS^m|dIBCEJ}V)cAfho6qn1&qV&31uf3^b92Ie&so9FcX@UD zlWS*qjoHnsWL`~s62&p)V<5+o4Qu$D+=pL6W{%jO$g2;M6@Tr?_FMk^3)e>-Ztm>kch|gs@z1udt0>~@ zo%NwnwW~jLJomEvb*7km+6DdS^4m7^viKMn-k;=1%QK97Rv2@l{dD*ikBMbwv+pY% z4^x;qTW()ML4oD>#J>I)a+BtCGw^AP7x4H!nYUT}?L&uM*?e+|?2|K`{u$*jo?27z zXXDQ93~uA9YN4D0RT)i{dC$+B=f3ibedfadM|*Vr=cR9cp8aIPt~W>T-1#K1UDZYK zubkzLIiltlgX(R5)``0NYCiOB-8*xZ;hDDb4Cl$a$`|G3Sbr*sI=%DJv%EWc61$a8 z&Y#@5&3?M)TFd=W9O~DJ(+|=DSAw&skP~W8t%uKDE2u#>Xe0IoKm?>0GvWkxbf~Wfi%Rk2_YL6TvI!g&GN9qNJ5x?E zLKd2JHl*Zzg1e^Q*}=%Xe|C_i0?1O3_A|cwn4y+DDC98VC|+GQIbn^Q`E97(2U-Ok z3d*V{ex50 z@Pu2RK7W2|Hj6DWFEur_g}<-)t_+mfjwCLJJ(U4!lfS^3^)MLVoE=3O8m>xHiReOD*5rb9as~v{VJ9E1Yu) z+@>lxfuq8NDX6G;|4Y#|YuYwvO3vv$TwqtcTCzpLhj;SkLY_~B;ILibdLS&iw`EhJ zz=hO;Z!a7V8<;PBUUq5iEoP3$cF?LsNzg#3-2C2JLE za@Yon|Fs;ao~tr})f{*VN&!!oxs<_DK#r?}k$JS>l=F}=*UpC1iV8DlpZ4v1GXa_| zZge%IgTe~DWM4fehI?1 diff --git a/assets/reactions.png b/assets/reactions.png new file mode 100644 index 0000000000000000000000000000000000000000..c413051e9398b704519a75b95a126b2a50350e78 GIT binary patch literal 13144 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 literal 0 HcmV?d00001 From 3cfbe7c078cb6441177cd2a2a700695b2be842ff Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 10 Apr 2026 11:12:22 -0400 Subject: [PATCH 77/94] make errors on sync noisy --- lib/controllers/client_controller.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 3de4bc0..6a06e10 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -15,6 +15,7 @@ import "package:nexus/controllers/space_edges_controller.dart"; import "package:nexus/controllers/sync_status_controller.dart"; import "package:nexus/controllers/top_level_spaces_controller.dart"; import "package:nexus/helpers/extensions/gomuks_buffer.dart"; +import "package:nexus/main.dart"; import "package:nexus/models/client_state.dart"; import "package:nexus/models/event.dart"; import "package:nexus/models/paginate.dart"; @@ -122,6 +123,7 @@ class ClientController extends AsyncNotifier { debugPrint("Finished handling $muksEventType..."); } catch (error, stackTrace) { debugger(); + showError(error, stackTrace); debugPrintStack(stackTrace: stackTrace, label: error.toString()); } }); From 5154e0fc6bfaaae6314caf847c616f29aac29cc7 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 10 Apr 2026 11:53:32 -0400 Subject: [PATCH 78/94] Try to fix error handling on sync --- lib/controllers/sync_status_controller.dart | 8 ++++- lib/main.dart | 5 +-- lib/models/sync_status.dart | 2 +- lib/pages/chat_page.dart | 38 ++++++++++++++------- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/lib/controllers/sync_status_controller.dart b/lib/controllers/sync_status_controller.dart index fe65732..8475d9d 100644 --- a/lib/controllers/sync_status_controller.dart +++ b/lib/controllers/sync_status_controller.dart @@ -1,11 +1,17 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/main.dart"; import "package:nexus/models/sync_status.dart"; class SyncStatusController extends Notifier { @override Null build() => null; - void set(SyncStatus newStatus) => state = newStatus; + void set(SyncStatus newStatus) { + if (newStatus.type == SyncStatusType.permanentlyFailed) { + showError(newStatus.error ?? "Syncing failed"); + } + state = newStatus; + } static final provider = NotifierProvider( SyncStatusController.new, diff --git a/lib/main.dart b/lib/main.dart index 192ca29..846f075 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,6 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/header_controller.dart"; -import "package:nexus/controllers/init_complete_controller.dart"; import "package:nexus/controllers/multi_provider_controller.dart"; import "package:nexus/controllers/shared_prefs_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; @@ -127,9 +126,7 @@ class App extends StatelessWidget { } else if (!clientState.isVerified) { return VerifyPage(); } else { - return ref.watch(InitCompleteController.provider) - ? ChatPage() - : Loading(); + return ChatPage(); } }, ), diff --git a/lib/models/sync_status.dart b/lib/models/sync_status.dart index 42c5f2a..7848fbe 100644 --- a/lib/models/sync_status.dart +++ b/lib/models/sync_status.dart @@ -14,5 +14,5 @@ abstract class SyncStatus with _$SyncStatus { _$SyncStatusFromJson(json); } -@JsonEnum(fieldRename: FieldRename.snake) +@JsonEnum(fieldRename: FieldRename.kebab) enum SyncStatusType { ok, waiting, erroring, permanentlyFailed } diff --git a/lib/pages/chat_page.dart b/lib/pages/chat_page.dart index 7aa8156..671891c 100644 --- a/lib/pages/chat_page.dart +++ b/lib/pages/chat_page.dart @@ -1,7 +1,10 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/init_complete_controller.dart"; +import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/chat_page/sidebar.dart"; import "package:nexus/widgets/chat_page/room_chat.dart"; +import "package:nexus/widgets/loading.dart"; class ChatPage extends ConsumerWidget { const ChatPage({super.key}); @@ -11,22 +14,33 @@ class ChatPage extends ConsumerWidget { builder: (context, constraints) { final isDesktop = constraints.maxWidth > 650; final showMembersByDefault = constraints.maxWidth > 1000; + final initComplete = ref.watch(InitCompleteController.provider); return Scaffold( - body: Builder( - builder: (context) => Row( - children: [ - if (isDesktop) Sidebar(isDesktop: isDesktop), - Expanded( - child: RoomChat( - isDesktop: isDesktop, - showMembersByDefault: showMembersByDefault, + appBar: initComplete ? null : Appbar(), + body: initComplete + ? Builder( + builder: (context) => Row( + children: [ + if (isDesktop) Sidebar(isDesktop: isDesktop), + Expanded( + child: RoomChat( + isDesktop: isDesktop, + showMembersByDefault: showMembersByDefault, + ), + ), + ], + ), + ) + : Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [Loading(), Text("Syncing...")], ), ), - ], - ), - ), - drawer: isDesktop ? null : Sidebar(isDesktop: isDesktop), + drawer: isDesktop || !initComplete + ? null + : Sidebar(isDesktop: isDesktop), ); }, ); From 7b2a6b84adbabd6b7e10728c6c021438cf802a4c Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 10 Apr 2026 12:15:48 -0400 Subject: [PATCH 79/94] Update progress list [skip ci] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e11d8f..a21b5cc 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Searching - [ ] Creating (Rooms, Spaces, and DMs) - [x] Joining - - [ ] Parse vias + - [x] Parse vias - [x] Using a text/uri/link - [x] Plain text - [x] `matrix:` Uri @@ -103,7 +103,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Creating - [ ] Threads - [x] Profile popouts - - [ ] Working actions + - [x] Working actions - [x] Copy link to: - [x] Room - [x] Space From e9b78a14d5046429e47ad6fcf0f63b8b9722ec59 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 10 Apr 2026 16:31:55 -0400 Subject: [PATCH 80/94] Update reactions as they are modified --- lib/controllers/message_controller.dart | 3 +- lib/controllers/room_chat_controller.dart | 63 +++++++++++++++++++++-- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index 24356c2..79bc77e 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -79,7 +79,8 @@ class MessageController extends AsyncNotifier { ); final reactions = reactionEvents - ?.fold>>(IMap(), (acc, event) { + ?.where((event) => event.redactedBy == null) + .fold>>(IMap(), (acc, event) { final key = event.content["m.relates_to"]?["key"]; if (key == null) return acc; diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index d3da7c7..1ecf0ea 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -9,6 +9,7 @@ import "package:nexus/controllers/message_controller.dart"; import "package:nexus/controllers/messages_controller.dart"; import "package:nexus/controllers/new_events_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/models/configs/messages_config.dart"; import "package:nexus/models/configs/message_config.dart"; import "package:nexus/models/requests/get_room_state_request.dart"; @@ -77,15 +78,67 @@ class RoomChatController extends AsyncNotifier { ref.onDispose( ref.listen(NewEventsController.provider(roomId), (_, next) async { for (final event in next) { - // TODO: Handle new reactions + if (event.type == "m.reaction") { + final message = controller.messages.firstWhereOrNull( + (message) => + message.id == event.content["m.relates_to"]?["event_id"], + ); + final key = event.content["m.relates_to"]?["key"]; + if (message == null || key == null || !ref.mounted) return; + + return await controller.updateMessage( + message, + message.copyWith( + reactions: IMap(message.reactions) + .update( + key, + (reactors) => [...reactors, event.authorId], + ifAbsent: () => [event.authorId], + ) + .unlock, + ), + ); + } + if (event.type == "m.room.redaction") { final controller = await future; - final message = controller.messages.firstWhereOrNull( - (message) => message.id == event.content["redacts"], + final redactsId = event.content["redacts"]; + final originalMessage = controller.messages.firstWhereOrNull( + (message) => message.id == redactsId, ); - if (message == null || !ref.mounted) return; + if (!ref.mounted) return; - await controller.removeMessage(message); + if (originalMessage != null) { + return await controller.removeMessage(originalMessage); + } + + final redacts = ref + .read(SelectedRoomController.provider) + ?.events + .firstWhere((event) => event.eventId == redactsId); + + if (redacts?.type == "m.reaction") { + final message = controller.messages.firstWhereOrNull( + (message) => + message.id == redacts!.content["m.relates_to"]?["event_id"], + ); + final key = redacts!.content["m.relates_to"]?["key"]; + if (message == null || key == null || !ref.mounted) return; + + return await controller.updateMessage( + message, + message.copyWith( + reactions: IMap(message.reactions) + .update( + key, + (reactors) => + IList(reactors).remove(redacts.authorId).unlock, + ) + .where((_, value) => value.isNotEmpty) + .unlock, + ), + ); + } } else { final message = await ref.watch( MessageController.provider( From 07decc10e243c3f13cc6261393b36f4f6bc65b3c Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 10 Apr 2026 16:33:53 -0400 Subject: [PATCH 81/94] potential loading time optimization --- lib/controllers/message_controller.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index 79bc77e..51f94fe 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -68,15 +68,17 @@ class MessageController extends AsyncNotifier { final replyId = config.event.content["m.relates_to"]?["m.in_reply_to"]?["event_id"]; - final reactionEvents = await ref - .watch(ClientController.provider.notifier) - .getRelatedEvents( - GetRelatedEventsRequest( - roomId: config.room.metadata!.id, - eventId: config.event.eventId, - relationType: "m.annotation", - ), - ); + final reactionEvents = event.reactions.isEmpty + ? null + : await ref + .watch(ClientController.provider.notifier) + .getRelatedEvents( + GetRelatedEventsRequest( + roomId: config.room.metadata!.id, + eventId: config.event.eventId, + relationType: "m.annotation", + ), + ); final reactions = reactionEvents ?.where((event) => event.redactedBy == null) From f997e257a22629ddeb78cf2def8abcae68b3eea3 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 13:05:08 -0400 Subject: [PATCH 82/94] update submodule remote --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 17d64ba..145276a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "gomuks"] path = gomuks - url = https://github.com/zachatrocity/gomuks - branch = init-root-dir + url = https://github.com/gomuks/gomuks + branch = main From 6b8eef3f17ba8bedd098382ffcb6352c10424d30 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 13:23:05 -0400 Subject: [PATCH 83/94] fix reactions on edited messages --- lib/controllers/message_controller.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index 51f94fe..c65d18d 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -15,8 +15,8 @@ class MessageController extends AsyncNotifier { @override Future build() async { try { - if ((config.event.relationType == "m.replace" && !config.includeEdits) || - config.room.metadata == null) { + final isEdit = config.event.relationType == "m.replace"; + if ((isEdit && !config.includeEdits) || config.room.metadata == null) { return null; } @@ -68,14 +68,16 @@ class MessageController extends AsyncNotifier { final replyId = config.event.content["m.relates_to"]?["m.in_reply_to"]?["event_id"]; - final reactionEvents = event.reactions.isEmpty + final reactionEvents = config.event.reactions.isEmpty && !isEdit ? null : await ref .watch(ClientController.provider.notifier) .getRelatedEvents( GetRelatedEventsRequest( roomId: config.room.metadata!.id, - eventId: config.event.eventId, + eventId: + (isEdit ? config.event.relatesTo : null) ?? + config.event.eventId, relationType: "m.annotation", ), ); From 1dcf3018a279c5ea9f4d93bb1625147191d17e05 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 14:11:18 -0400 Subject: [PATCH 84/94] Allow sending reactions (but not redacting them yet) --- .vscode/settings.json | 1 + lib/controllers/client_controller.dart | 14 +++- lib/controllers/room_chat_controller.dart | 20 +++++ lib/models/requests/send_event_request.dart | 16 ++++ .../chat_page/wrappers/message_wrapper.dart | 61 +-------------- .../chat_page/wrappers/reaction_row.dart | 77 +++++++++++++++++++ 6 files changed, 129 insertions(+), 60 deletions(-) create mode 100644 lib/models/requests/send_event_request.dart create mode 100644 lib/widgets/chat_page/wrappers/reaction_row.dart diff --git a/.vscode/settings.json b/.vscode/settings.json index 8708bf5..105b321 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "cSpell.words": [ "Appbar", "Displayname", + "fluttertagger", "Homeserver", "localpart", "prefs", diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 6a06e10..103a9d4 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -28,6 +28,7 @@ import "package:nexus/models/profile.dart"; import "package:nexus/models/requests/paginate_request.dart"; import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/requests/report_request.dart"; +import "package:nexus/models/requests/send_event_request.dart"; import "package:nexus/models/requests/send_message_request.dart"; import "package:nexus/models/requests/set_membership_request.dart"; import "package:nexus/models/room.dart"; @@ -80,9 +81,13 @@ class ClientController extends AsyncNotifier { case "send_complete": final event = Event.fromJson(decodedMuksEvent["event"]); - ref - .watch(NewEventsController.provider(event.roomId).notifier) - .add(IList([event])); + if (event.type == "m.room.message") { + ref + .watch( + NewEventsController.provider(event.roomId).notifier, + ) + .add(IList([event])); + } break; case "sync_complete": final syncData = SyncData.fromJson(decodedMuksEvent); @@ -164,6 +169,9 @@ class ClientController extends AsyncNotifier { Future sendMessage(SendMessageRequest request) async => Event.fromJson(await _sendCommand("send_message", request.toJson())); + Future sendEvent(SendEventRequest request) async => + Event.fromJson(await _sendCommand("send_event", request.toJson())); + Future verify(String recoveryKey) async { try { await _sendCommand("verify", {"recovery_key": recoveryKey}); diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 1ecf0ea..aac51b2 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -16,6 +16,7 @@ import "package:nexus/models/requests/get_room_state_request.dart"; import "package:nexus/models/requests/paginate_request.dart"; import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/relation_type.dart"; +import "package:nexus/models/requests/send_event_request.dart"; import "package:nexus/models/requests/send_message_request.dart"; import "package:nexus/models/room.dart"; @@ -328,6 +329,25 @@ class RoomChatController extends AsyncNotifier { return await controller.scrollToMessage(message.id); } + Future sendReaction(String reaction, Message message) async { + final client = ref.watch(ClientController.provider.notifier); + + await client.sendEvent( + SendEventRequest( + roomId: roomId, + type: "m.reaction", + content: { + "m.relates_to": { + "event_id": message.id, + "rel_type": "m.annotation", + "key": reaction, + }, + }, + disableEncryption: true, + ), + ); + } + static final provider = AsyncNotifierProvider.family .autoDispose( RoomChatController.new, diff --git a/lib/models/requests/send_event_request.dart b/lib/models/requests/send_event_request.dart new file mode 100644 index 0000000..8b2d5c8 --- /dev/null +++ b/lib/models/requests/send_event_request.dart @@ -0,0 +1,16 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +part "send_event_request.freezed.dart"; +part "send_event_request.g.dart"; + +@freezed +abstract class SendEventRequest with _$SendEventRequest { + const factory SendEventRequest({ + required String roomId, + required String type, + required Map content, + @Default(false) bool disableEncryption, + }) = _SendEventRequest; + + factory SendEventRequest.fromJson(Map json) => + _$SendEventRequestFromJson(json); +} diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart index 75ed037..9c70c27 100644 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ b/lib/widgets/chat_page/wrappers/message_wrapper.dart @@ -1,27 +1,20 @@ -import "package:cross_cache/cross_cache.dart"; -import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client_state_controller.dart"; -import "package:nexus/controllers/cross_cache_controller.dart"; -import "package:nexus/helpers/extensions/get_headers.dart"; -import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; +import "package:nexus/widgets/chat_page/wrappers/reaction_row.dart"; import "package:timeago/timeago.dart"; -class MessageWrapper extends ConsumerWidget { +class MessageWrapper extends StatelessWidget { final Message message; final Widget child; final MessageGroupStatus? groupStatus; const MessageWrapper(this.message, this.child, this.groupStatus, {super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { + Widget build(BuildContext context) { final theme = Theme.of(context); final error = message.metadata?["error"]; - final clientState = ref.watch(ClientStateController.provider); return ClipRRect( borderRadius: BorderRadius.all(Radius.circular(12)), @@ -78,53 +71,7 @@ class MessageWrapper extends ConsumerWidget { color: theme.colorScheme.error, ), ), - Wrap( - spacing: 4, - runSpacing: 4, - children: - clientState?.homeserverUrl == null || - message.reactions == null - ? [] - : message.reactions!.mapTo((reaction, reactors) { - final selected = reactors.contains( - clientState!.userId, - ); - return SizedBox( - child: Tooltip( - message: reactors.join(", "), - child: ChoiceChip( - showCheckmark: false, - selected: selected, - label: Row( - mainAxisSize: MainAxisSize.min, - spacing: 8, - children: [ - reaction.startsWith("mxc://") - ? Image( - height: 20, - image: CachedNetworkImage( - headers: ref.headers, - Uri.parse(reaction) - .mxcToHttps( - clientState - .homeserverUrl!, - ) - .toString(), - ref.watch( - CrossCacheController.provider, - ), - ), - ) - : Text(reaction), - Text(reactors.length.toString()), - ], - ), - onSelected: (value) {}, // TODO - ), - ), - ); - }).toList(), - ), + ReactionRow(message), ], ), ), diff --git a/lib/widgets/chat_page/wrappers/reaction_row.dart b/lib/widgets/chat_page/wrappers/reaction_row.dart new file mode 100644 index 0000000..1924cd2 --- /dev/null +++ b/lib/widgets/chat_page/wrappers/reaction_row.dart @@ -0,0 +1,77 @@ +import "package:cross_cache/cross_cache.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:flutter/material.dart"; +import "package:flutter_chat_core/flutter_chat_core.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; +import "package:nexus/controllers/room_chat_controller.dart"; +import "package:nexus/controllers/selected_room_controller.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; +import "package:nexus/helpers/extensions/mxc_to_https.dart"; + +class ReactionRow extends ConsumerWidget { + final Message message; + const ReactionRow(this.message, {super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final clientState = ref.watch(ClientStateController.provider); + + return Wrap( + spacing: 4, + runSpacing: 4, + children: clientState?.homeserverUrl == null || message.reactions == null + ? [] + : message.reactions!.mapTo((reaction, reactors) { + final selected = reactors.contains(clientState!.userId); + return SizedBox( + child: Tooltip( + message: reactors.join(", "), + child: ChoiceChip( + showCheckmark: false, + selected: selected, + label: Row( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + reaction.startsWith("mxc://") + ? Image( + height: 20, + image: CachedNetworkImage( + headers: ref.headers, + Uri.parse(reaction) + .mxcToHttps(clientState.homeserverUrl!) + .toString(), + ref.watch(CrossCacheController.provider), + ), + ) + : Text(reaction), + Text(reactors.length.toString()), + ], + ), + onSelected: (value) async { + final roomId = ref.watch( + SelectedRoomController.provider.select( + (value) => value?.metadata?.id, + ), + ); + if (roomId == null) return; + + final controller = ref.watch( + RoomChatController.provider(roomId).notifier, + ); + + if (selected) { + // TODO: remove + } else { + await controller.sendReaction(reaction, message); + } + }, + ), + ), + ); + }).toList(), + ); + } +} From 4ff507e93f00c0ec43d2635a4258bb6362023901 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 14:19:43 -0400 Subject: [PATCH 85/94] fix spellchecker complaining --- .vscode/settings.json | 5 +++-- lib/controllers/client_controller.dart | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 105b321..da80f4b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,9 +3,10 @@ "Appbar", "Displayname", "fluttertagger", + "Gomuks", "Homeserver", "localpart", - "prefs", - "vodozemac" + "muks", + "prefs" ] } diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 103a9d4..cc68871 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -266,7 +266,7 @@ class ClientController extends AsyncNotifier { Future discoverHomeserver(Uri homeserver) async { try { final response = await _sendCommand("discover_homeserver", { - "user_id": "@fakeuser:${homeserver.host}", + "user_id": "@fake-user:${homeserver.host}", }); return response["m.homeserver"]?["base_url"]; } catch (error) { From 4954fb8c09f593c08c167b4360124d0b2500d1f0 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 14:56:08 -0400 Subject: [PATCH 86/94] allow redacting/removing reactions --- lib/controllers/room_chat_controller.dart | 34 +++++++++++++++++++ .../chat_page/wrappers/reaction_row.dart | 15 ++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index aac51b2..94a7ccb 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -12,6 +12,7 @@ import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/models/configs/messages_config.dart"; import "package:nexus/models/configs/message_config.dart"; +import "package:nexus/models/requests/get_related_events_request.dart"; import "package:nexus/models/requests/get_room_state_request.dart"; import "package:nexus/models/requests/paginate_request.dart"; import "package:nexus/models/requests/redact_event_request.dart"; @@ -329,6 +330,39 @@ class RoomChatController extends AsyncNotifier { return await controller.scrollToMessage(message.id); } + Future removeReaction( + String reaction, + Message message, + String userId, + ) async { + final client = ref.watch(ClientController.provider.notifier); + final allReactionEvents = await client.getRelatedEvents( + GetRelatedEventsRequest( + roomId: roomId, + eventId: message.id, + relationType: "m.annotation", + ), + ); + + final reactionEvents = allReactionEvents + ?.where((event) => event.redactedBy == null) + .toIList(); + + final reactionEvent = reactionEvents?.firstWhereOrNull( + (event) => + event.authorId == userId && + event.content["m.relates_to"]?["key"] == reaction, + ); + + if (reactionEvent != null) { + await ref + .watch(ClientController.provider.notifier) + .redactEvent( + RedactEventRequest(eventId: reactionEvent.eventId, roomId: roomId), + ); + } + } + Future sendReaction(String reaction, Message message) async { final client = ref.watch(ClientController.provider.notifier); diff --git a/lib/widgets/chat_page/wrappers/reaction_row.dart b/lib/widgets/chat_page/wrappers/reaction_row.dart index 1924cd2..62f58ee 100644 --- a/lib/widgets/chat_page/wrappers/reaction_row.dart +++ b/lib/widgets/chat_page/wrappers/reaction_row.dart @@ -9,6 +9,7 @@ import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; +import "package:nexus/main.dart"; class ReactionRow extends ConsumerWidget { final Message message; @@ -56,16 +57,24 @@ class ReactionRow extends ConsumerWidget { (value) => value?.metadata?.id, ), ); - if (roomId == null) return; + if (roomId == null || clientState.userId == null) return; final controller = ref.watch( RoomChatController.provider(roomId).notifier, ); if (selected) { - // TODO: remove + await controller + .removeReaction( + reaction, + message, + clientState.userId!, + ) + .onError(showError); } else { - await controller.sendReaction(reaction, message); + await controller + .sendReaction(reaction, message) + .onError(showError); } }, ), From 3e8eba0872faf37e88ae3b1a56da5ed3309e5653 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 15:07:49 -0400 Subject: [PATCH 87/94] disable react button while last action is pending --- lib/controllers/room_chat_controller.dart | 1 + lib/models/requests/send_event_request.dart | 1 + .../chat_page/wrappers/reaction_row.dart | 130 +++++++++++------- 3 files changed, 79 insertions(+), 53 deletions(-) diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 94a7ccb..fa32bf8 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -377,6 +377,7 @@ class RoomChatController extends AsyncNotifier { "key": reaction, }, }, + synchronous: true, disableEncryption: true, ), ); diff --git a/lib/models/requests/send_event_request.dart b/lib/models/requests/send_event_request.dart index 8b2d5c8..da5de32 100644 --- a/lib/models/requests/send_event_request.dart +++ b/lib/models/requests/send_event_request.dart @@ -8,6 +8,7 @@ abstract class SendEventRequest with _$SendEventRequest { required String roomId, required String type, required Map content, + @Default(false) bool synchronous, @Default(false) bool disableEncryption, }) = _SendEventRequest; diff --git a/lib/widgets/chat_page/wrappers/reaction_row.dart b/lib/widgets/chat_page/wrappers/reaction_row.dart index 62f58ee..f20d2ad 100644 --- a/lib/widgets/chat_page/wrappers/reaction_row.dart +++ b/lib/widgets/chat_page/wrappers/reaction_row.dart @@ -2,6 +2,7 @@ import "package:cross_cache/cross_cache.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/cross_cache_controller.dart"; @@ -24,63 +25,86 @@ class ReactionRow extends ConsumerWidget { runSpacing: 4, children: clientState?.homeserverUrl == null || message.reactions == null ? [] - : message.reactions!.mapTo((reaction, reactors) { - final selected = reactors.contains(clientState!.userId); - return SizedBox( - child: Tooltip( - message: reactors.join(", "), - child: ChoiceChip( - showCheckmark: false, - selected: selected, - label: Row( - mainAxisSize: MainAxisSize.min, - spacing: 8, - children: [ - reaction.startsWith("mxc://") - ? Image( - height: 20, - image: CachedNetworkImage( - headers: ref.headers, - Uri.parse(reaction) - .mxcToHttps(clientState.homeserverUrl!) - .toString(), - ref.watch(CrossCacheController.provider), - ), - ) - : Text(reaction), - Text(reactors.length.toString()), - ], - ), - onSelected: (value) async { - final roomId = ref.watch( - SelectedRoomController.provider.select( - (value) => value?.metadata?.id, + : message.reactions! + .mapTo( + (reaction, reactors) => HookBuilder( + builder: (context) { + final enabled = useState(true); + final selected = reactors.contains(clientState!.userId); + return SizedBox( + child: Tooltip( + message: reactors.join(", "), + child: ChoiceChip( + showCheckmark: false, + selected: selected, + label: Row( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + reaction.startsWith("mxc://") + ? Image( + height: 20, + image: CachedNetworkImage( + headers: ref.headers, + Uri.parse(reaction) + .mxcToHttps( + clientState.homeserverUrl!, + ) + .toString(), + ref.watch( + CrossCacheController.provider, + ), + ), + ) + : Text(reaction), + Text(reactors.length.toString()), + ], + ), + onSelected: enabled.value + ? (value) async { + enabled.value = false; + try { + final roomId = ref.watch( + SelectedRoomController.provider.select( + (value) => value?.metadata?.id, + ), + ); + if (roomId == null || + clientState.userId == null) { + return; + } + + final controller = ref.watch( + RoomChatController.provider( + roomId, + ).notifier, + ); + + if (selected) { + await controller + .removeReaction( + reaction, + message, + clientState.userId!, + ) + .onError(showError); + } else { + await controller + .sendReaction(reaction, message) + .onError(showError); + } + } finally { + enabled.value = true; + } + } + : null, + ), ), ); - if (roomId == null || clientState.userId == null) return; - - final controller = ref.watch( - RoomChatController.provider(roomId).notifier, - ); - - if (selected) { - await controller - .removeReaction( - reaction, - message, - clientState.userId!, - ) - .onError(showError); - } else { - await controller - .sendReaction(reaction, message) - .onError(showError); - } }, ), - ), - ); - }).toList(), + ) + .toList(), ); } } From dc1eb52fe058faa9c17b4b0d86ab1fe990f7d36c Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 15:30:01 -0400 Subject: [PATCH 88/94] re-order room menu items --- lib/widgets/chat_page/room_menu.dart | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/widgets/chat_page/room_menu.dart b/lib/widgets/chat_page/room_menu.dart index 382e20f..4405707 100644 --- a/lib/widgets/chat_page/room_menu.dart +++ b/lib/widgets/chat_page/room_menu.dart @@ -18,6 +18,16 @@ class RoomMenu extends ConsumerWidget { return PopupMenuButton( itemBuilder: (_) => [ + PopupMenuItem( + onTap: () async { + await client.markRead(room); + await Future.wait(children.map((child) => client.markRead(child))); + }, + child: ListTile( + leading: Icon(Icons.check), + title: Text("Mark as Read"), + ), + ), PopupMenuItem( onTap: () async { final vias = ref.watch(ViaController.provider(room)); @@ -30,16 +40,6 @@ class RoomMenu extends ConsumerWidget { }, child: ListTile(leading: Icon(Icons.link), title: Text("Copy Link")), ), - PopupMenuItem( - onTap: () async { - await client.markRead(room); - await Future.wait(children.map((child) => client.markRead(child))); - }, - child: ListTile( - leading: Icon(Icons.check), - title: Text("Mark as Read"), - ), - ), PopupMenuItem( onTap: () => showDialog( context: context, From e16a780fa3784eadb4c5fbb696e68ad3876a02b2 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 16:29:03 -0400 Subject: [PATCH 89/94] add support for sending emojis through a custom picker --- lib/widgets/chat_page/room_chat.dart | 59 ++++++++++++++++++++++++++++ pubspec.lock | 9 +++++ pubspec.yaml | 3 ++ 3 files changed, 71 insertions(+) diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index a36bec8..8b4f17b 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -1,3 +1,5 @@ +import "package:emoji_text_field/emoji_text_field.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; @@ -6,6 +8,7 @@ import "package:flutter_hooks/flutter_hooks.dart"; import "package:flyer_chat_file_message/flyer_chat_file_message.dart"; import "package:flyer_chat_system_message/flyer_chat_system_message.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:nexus/controllers/account_data_controller.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/power_level_controller.dart"; @@ -84,6 +87,62 @@ class RoomChat extends HookConsumerWidget { List getMessageOptions(Message message) { final isSentByMe = message.authorId == userId; return [ + PopupMenuItem( + child: Row( + children: [ + ...{ + ...ref.watch( + AccountDataController.provider.select( + (value) => IList( + value["m.recent_emoji"]?.content["recent_emoji"] ?? + [], + ).map((entry) => entry["emoji"]), + ), + ), + "👍", + "🤣", + "😭", + "🤔", + } + .toIList() + .sublist(0, 4) + .map( + (emoji) => IconButton( + onPressed: () async { + Navigator.of(context).pop(); + await notifier + .sendReaction(emoji, message) + .onError(showError); + }, + icon: Text(emoji), + ), + ), + IconButton( + onPressed: () { + Navigator.of(context).pop(); + final controller = TextEditingController(); + showBottomSheet( + context: context, + builder: (context) => EmojiKeyboardView( + config: EmojiViewConfig( + backgroundColor: theme.colorScheme.surfaceContainer, + height: 600, + ), + textController: controller + ..addListener(() async { + Navigator.of(context).pop(); + await notifier + .sendReaction(controller.text, message) + .onError(showError); + }), + ), + ); + }, + icon: Icon(Icons.emoji_emotions), + ), + ], + ), + ), PopupMenuItem( onTap: () { relatedMessage.value = message; diff --git a/pubspec.lock b/pubspec.lock index ce90832..ef7fcd9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -354,6 +354,15 @@ packages: url: "https://github.com/hasali19/flutter_dynamic_system_colors" source: git version: "1.8.0" + emoji_text_field: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: "0e90703a6e876939be70bd1816c49cf14474de61" + url: "https://github.com/Henry-Hiles/emoji_text_field" + source: git + version: "1.0.0" encrypt: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 72bf939..7ecefa1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,9 @@ dependencies: timeago: ^3.7.1 http: ^1.6.0 flutter_linkify: ^6.0.0 + emoji_text_field: + git: + url: https://github.com/Henry-Hiles/emoji_text_field dev_dependencies: build_runner: ^2.4.11 From 6ca974e6fcef5b4b54e06306e5c995b8b6b552b0 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 16:46:13 -0400 Subject: [PATCH 90/94] add emoji button to composer --- lib/widgets/chat_page/composer/chat_box.dart | 6 +++ .../chat_page/emoji_picker_button.dart | 40 +++++++++++++++++++ lib/widgets/chat_page/room_chat.dart | 29 +++----------- 3 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 lib/widgets/chat_page/emoji_picker_button.dart diff --git a/lib/widgets/chat_page/composer/chat_box.dart b/lib/widgets/chat_page/composer/chat_box.dart index 1b41037..478974e 100644 --- a/lib/widgets/chat_page/composer/chat_box.dart +++ b/lib/widgets/chat_page/composer/chat_box.dart @@ -9,6 +9,7 @@ import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/relation_type.dart"; import "package:nexus/widgets/chat_page/composer/mention_overlay.dart"; import "package:nexus/widgets/chat_page/composer/relation_preview.dart"; +import "package:nexus/widgets/chat_page/emoji_picker_button.dart"; class ChatBox extends HookConsumerWidget { final Message? relatedMessage; @@ -91,6 +92,11 @@ class ChatBox extends HookConsumerWidget { child: Row( spacing: 8, children: [ + EmojiPickerButton( + context: context, + onSelection: (_) => node?.requestFocus(), + controller: controller.value, + ), PopupMenuButton( tooltip: "Add media", enabled: canSendMessages, diff --git a/lib/widgets/chat_page/emoji_picker_button.dart b/lib/widgets/chat_page/emoji_picker_button.dart new file mode 100644 index 0000000..7e0e0d4 --- /dev/null +++ b/lib/widgets/chat_page/emoji_picker_button.dart @@ -0,0 +1,40 @@ +import "package:emoji_text_field/emoji_text_field.dart"; +import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; + +class EmojiPickerButton extends HookWidget { + final TextEditingController? controller; + final void Function(String emoji)? onSelection; + final VoidCallback? onPressed; + final BuildContext context; + const EmojiPickerButton({ + this.controller, + this.onPressed, + this.onSelection, + required this.context, + super.key, + }); + + @override + Widget build(_) => IconButton( + onPressed: () { + onPressed?.call(); + final controller = this.controller ?? TextEditingController(); + showBottomSheet( + context: context, + builder: (context) => EmojiKeyboardView( + config: EmojiViewConfig( + backgroundColor: Theme.of(context).colorScheme.surfaceContainer, + height: 600, + ), + textController: controller + ..addListener(() async { + Navigator.of(context).pop(); + onSelection?.call(controller.text); + }), + ), + ); + }, + icon: Icon(Icons.emoji_emotions), + ); +} diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index 8b4f17b..5166d87 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -1,4 +1,3 @@ -import "package:emoji_text_field/emoji_text_field.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; @@ -21,6 +20,7 @@ import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/relation_type.dart"; import "package:nexus/models/requests/report_request.dart"; import "package:nexus/widgets/chat_page/composer/chat_box.dart"; +import "package:nexus/widgets/chat_page/emoji_picker_button.dart"; import "package:nexus/widgets/chat_page/expandable_image_message.dart"; import "package:nexus/widgets/chat_page/member_list.dart"; import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart"; @@ -117,28 +117,11 @@ class RoomChat extends HookConsumerWidget { icon: Text(emoji), ), ), - IconButton( - onPressed: () { - Navigator.of(context).pop(); - final controller = TextEditingController(); - showBottomSheet( - context: context, - builder: (context) => EmojiKeyboardView( - config: EmojiViewConfig( - backgroundColor: theme.colorScheme.surfaceContainer, - height: 600, - ), - textController: controller - ..addListener(() async { - Navigator.of(context).pop(); - await notifier - .sendReaction(controller.text, message) - .onError(showError); - }), - ), - ); - }, - icon: Icon(Icons.emoji_emotions), + EmojiPickerButton( + context: context, + onPressed: Navigator.of(context).pop, + onSelection: (emoji) => + notifier.sendReaction(emoji, message).onError(showError), ), ], ), From 1282a8b897b153e6f038e5a78ab623aead09ebc9 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 16:49:24 -0400 Subject: [PATCH 91/94] don't show recents in emoji picker modal --- lib/widgets/chat_page/emoji_picker_button.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/chat_page/emoji_picker_button.dart b/lib/widgets/chat_page/emoji_picker_button.dart index 7e0e0d4..0c43c48 100644 --- a/lib/widgets/chat_page/emoji_picker_button.dart +++ b/lib/widgets/chat_page/emoji_picker_button.dart @@ -20,10 +20,11 @@ class EmojiPickerButton extends HookWidget { onPressed: () { onPressed?.call(); final controller = this.controller ?? TextEditingController(); - showBottomSheet( + showModalBottomSheet( context: context, builder: (context) => EmojiKeyboardView( config: EmojiViewConfig( + showRecentTab: false, backgroundColor: Theme.of(context).colorScheme.surfaceContainer, height: 600, ), From 327c4066f38016fa07ff2f01c29e0f2e4dbc0ffa Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 16:50:09 -0400 Subject: [PATCH 92/94] Check off reactions in the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a21b5cc..0fe2a1b 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] Forwards - [x] Editing - [x] Deleting -- [ ] Reactions +- [x] Reactions - [ ] Pins - [ ] Displaying - [ ] Creating From b701da19dcd29044f6d0a61a6171aded5f5dd043 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 12 Apr 2026 16:53:30 -0400 Subject: [PATCH 93/94] fix nix build --- linux/nix/pkg/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/nix/pkg/default.nix b/linux/nix/pkg/default.nix index ae1ddeb..26f2a17 100644 --- a/linux/nix/pkg/default.nix +++ b/linux/nix/pkg/default.nix @@ -26,6 +26,7 @@ flutter.buildFlutterApplication { dynamic_system_colors = "sha256-es6rjMK1drkqZBKYUP77yw/q5+0uLwWOEDOXRawy3Dc="; flutter_chat_ui = "sha256-4fuag7lRH5cMBFD3fUzj2K541JwXLoz8HF/4OMr3uhk="; flutter_link_previewer = "sha256-4fuag7lRH5cMBFD3fUzj2K541JwXLoz8HF/4OMr3uhk="; + emoji_text_field = "sha256-F0QbIHP3wpKoL6QbJ20Oun0SsOdwnXe84IqsK2ad85w="; }; postInstall = '' From b93f4c979c2d0b40391218082e8ec22b96d24ae6 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Mon, 13 Apr 2026 09:39:59 -0400 Subject: [PATCH 94/94] Make reactions flexible to fix overflow issues --- .../chat_page/wrappers/reaction_row.dart | 112 +++++++++--------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/lib/widgets/chat_page/wrappers/reaction_row.dart b/lib/widgets/chat_page/wrappers/reaction_row.dart index f20d2ad..5e8fe86 100644 --- a/lib/widgets/chat_page/wrappers/reaction_row.dart +++ b/lib/widgets/chat_page/wrappers/reaction_row.dart @@ -31,17 +31,17 @@ class ReactionRow extends ConsumerWidget { builder: (context) { final enabled = useState(true); final selected = reactors.contains(clientState!.userId); - return SizedBox( - child: Tooltip( - message: reactors.join(", "), - child: ChoiceChip( - showCheckmark: false, - selected: selected, - label: Row( - mainAxisSize: MainAxisSize.min, - spacing: 8, - children: [ - reaction.startsWith("mxc://") + return Tooltip( + message: reactors.join(", "), + child: ChoiceChip( + showCheckmark: false, + selected: selected, + label: Row( + mainAxisSize: MainAxisSize.min, + spacing: 8, + children: [ + Flexible( + child: reaction.startsWith("mxc://") ? Image( height: 20, image: CachedNetworkImage( @@ -56,49 +56,55 @@ class ReactionRow extends ConsumerWidget { ), ), ) - : Text(reaction), - Text(reactors.length.toString()), - ], - ), - onSelected: enabled.value - ? (value) async { - enabled.value = false; - try { - final roomId = ref.watch( - SelectedRoomController.provider.select( - (value) => value?.metadata?.id, - ), - ); - if (roomId == null || - clientState.userId == null) { - return; - } - - final controller = ref.watch( - RoomChatController.provider( - roomId, - ).notifier, - ); - - if (selected) { - await controller - .removeReaction( - reaction, - message, - clientState.userId!, - ) - .onError(showError); - } else { - await controller - .sendReaction(reaction, message) - .onError(showError); - } - } finally { - enabled.value = true; - } - } - : null, + : Text( + reaction, + overflow: TextOverflow.ellipsis, + ), + ), + Text( + reactors.length.toString(), + overflow: TextOverflow.ellipsis, + ), + ], ), + onSelected: enabled.value + ? (value) async { + enabled.value = false; + try { + final roomId = ref.watch( + SelectedRoomController.provider.select( + (value) => value?.metadata?.id, + ), + ); + if (roomId == null || + clientState.userId == null) { + return; + } + + final controller = ref.watch( + RoomChatController.provider( + roomId, + ).notifier, + ); + + if (selected) { + await controller + .removeReaction( + reaction, + message, + clientState.userId!, + ) + .onError(showError); + } else { + await controller + .sendReaction(reaction, message) + .onError(showError); + } + } finally { + enabled.value = true; + } + } + : null, ), ); },