From 0df327d12598207b1aeab022d3502060f4403fe0 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 29 Jan 2026 11:52:56 +0000 Subject: [PATCH] wip thing --- lib/controllers/client_controller.dart | 44 +++++++++------ lib/controllers/header_controller.dart | 11 ++-- lib/controllers/room_chat_controller.dart | 55 ++++++++++++------- lib/controllers/spaces_controller.dart | 16 +++--- lib/helpers/extensions/event_to_message.dart | 8 +-- lib/models/client_state.dart | 2 +- lib/models/paginate.dart | 16 ++++++ lib/models/redact_event_request.dart | 3 - .../{ => requests}/get_event_request.dart | 0 .../get_related_events_request.dart | 0 lib/models/{ => requests}/login_request.dart | 0 lib/models/requests/paginate_request.dart | 14 +++++ lib/models/requests/redact_event_request.dart | 3 + lib/models/{ => requests}/report_request.dart | 0 lib/models/requests/send_message_request.dart | 55 +++++++++++++++++++ lib/pages/login_page.dart | 2 +- lib/widgets/chat_page/room_chat.dart | 2 +- 17 files changed, 173 insertions(+), 58 deletions(-) create mode 100644 lib/models/paginate.dart delete mode 100644 lib/models/redact_event_request.dart rename lib/models/{ => requests}/get_event_request.dart (100%) rename lib/models/{ => requests}/get_related_events_request.dart (100%) rename lib/models/{ => requests}/login_request.dart (100%) create mode 100644 lib/models/requests/paginate_request.dart create mode 100644 lib/models/requests/redact_event_request.dart rename lib/models/{ => requests}/report_request.dart (100%) create mode 100644 lib/models/requests/send_message_request.dart diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 5c9ed8f..15d06c9 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -12,12 +12,15 @@ import "package:nexus/controllers/top_level_spaces_controller.dart"; import "package:nexus/helpers/extensions/gomuks_buffer.dart"; import "package:nexus/models/client_state.dart"; import "package:nexus/models/event.dart"; -import "package:nexus/models/get_event_request.dart"; -import "package:nexus/models/get_related_events_request.dart"; -import "package:nexus/models/login_request.dart"; +import "package:nexus/models/paginate.dart"; +import "package:nexus/models/requests/get_event_request.dart"; +import "package:nexus/models/requests/get_related_events_request.dart"; +import "package:nexus/models/requests/login_request.dart"; import "package:nexus/models/profile.dart"; -import "package:nexus/models/redact_event_request.dart"; -import "package:nexus/models/report_request.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_message_request.dart"; import "package:nexus/models/room.dart"; import "package:nexus/models/sync_data.dart"; import "package:nexus/models/sync_status.dart"; @@ -98,7 +101,10 @@ class ClientController extends AsyncNotifier { throw Exception("GomuksStart returned error code $errorCode"); } - Future sendCommand(String command, Map data) async { + Future _sendCommand( + String command, + Map data, + ) async { final bufferPointer = data.toGomuksBufferPtr(); final handle = await future; final response = await Isolate.run( @@ -115,11 +121,14 @@ class ClientController extends AsyncNotifier { } Future redactEvent(RedactEventRequest report) => - sendCommand("redact_event", report.toJson()); + _sendCommand("redact_event", report.toJson()); + + Future sendMessage(SendMessageRequest request) => + _sendCommand("send_message", request.toJson()); Future verify(String recoveryKey) async { try { - await sendCommand("verify", {"recovery_key": recoveryKey}); + await _sendCommand("verify", {"recovery_key": recoveryKey}); return true; } catch (error) { return false; @@ -128,35 +137,38 @@ class ClientController extends AsyncNotifier { Future leaveRoom(Room room) async { if (room.metadata == null) return; - await sendCommand("leave_room", {"room_id": room.metadata!.id}); + await _sendCommand("leave_room", {"room_id": room.metadata!.id}); } Future?> getRelatedEvents( GetRelatedEventsRequest request, ) async { final response = - (await sendCommand("get_related_events", request.toJson())) as List?; + (await _sendCommand("get_related_events", request.toJson())) as List?; return response?.map((event) => Event.fromJson(event)).toIList(); } Future getEvent(GetEventRequest request) async { - final json = await sendCommand("get_event", request.toJson()); + final json = await _sendCommand("get_event", request.toJson()); return json == null ? null : Event.fromJson(json); } + Future paginate(PaginateRequest request) async => + Paginate.fromJson(await _sendCommand("paginate", request.toJson())); + Future getProfile(String userId) async { - final json = await sendCommand("get_profile", {"user_id": userId}); + final json = await _sendCommand("get_profile", {"user_id": userId}); return json == null ? null : Profile.fromJson(json); } Future reportEvent(ReportRequest report) => - sendCommand("report_event", report.toJson()); + _sendCommand("report_event", report.toJson()); Future markRead(Room room) async { if (room.events.isEmpty || room.metadata == null) return; - await sendCommand("mark_read", { + await _sendCommand("mark_read", { "room_id": room.metadata?.id, "receipt_type": "m.read", "event_id": room.events.last.eventId, @@ -165,7 +177,7 @@ class ClientController extends AsyncNotifier { Future login(LoginRequest login) async { try { - await sendCommand("login", login.toJson()); + await _sendCommand("login", login.toJson()); return true; } catch (error) { return false; @@ -174,7 +186,7 @@ class ClientController extends AsyncNotifier { Future discoverHomeserver(Uri homeserver) async { try { - final response = await sendCommand("discover_homeserver", { + final response = await _sendCommand("discover_homeserver", { "user_id": "@fakeuser:${homeserver.host}", }); return response["m.homeserver"]?["base_url"]; diff --git a/lib/controllers/header_controller.dart b/lib/controllers/header_controller.dart index dcb73ea..ead8f0d 100644 --- a/lib/controllers/header_controller.dart +++ b/lib/controllers/header_controller.dart @@ -1,3 +1,5 @@ +import "dart:ffi"; +import "dart:isolate"; import "package:ffi/ffi.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; @@ -7,13 +9,14 @@ class HeaderController extends AsyncNotifier> { @override Future> build() async { final handle = await ref.watch(ClientController.provider.future); - final info = GomuksGetAccountInfo(handle); + final info = await Isolate.run(() => GomuksGetAccountInfo(handle)); final headers = { - "authorization": - "Bearer ${info.access_token.cast().toDartString()}", + if (info.access_token != nullptr) + "authorization": + "Bearer ${info.access_token.cast().toDartString()}", }; - GomuksFreeAccountInfo(info); + await Isolate.run(() => GomuksFreeAccountInfo(info)); return headers; } diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 88ae53f..4490fdd 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -9,8 +9,11 @@ import "package:nexus/controllers/new_events_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/helpers/extensions/event_to_message.dart"; import "package:nexus/helpers/extensions/list_to_messages.dart"; -import "package:nexus/models/redact_event_request.dart"; +import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/relation_type.dart"; +import "package:nexus/models/requests/send_message_request.dart"; +import "package:nexus/models/room.dart"; +import "package:nexus/models/sync_data.dart"; class RoomChatController extends AsyncNotifier { final String roomId; @@ -67,7 +70,11 @@ class RoomChatController extends AsyncNotifier { ); final messages = await events.toMessages(client); - return InMemoryChatController(messages: messages); + final controller = InMemoryChatController(messages: messages); + + if (messages.length < 20) await loadOlder(controller); + + return controller; } Future insertMessage(Message message) async { @@ -98,9 +105,12 @@ class RoomChatController extends AsyncNotifier { ); } - Future loadOlder() async { - // final currentEvents = await future; - // await ref.watch(EventsController.provider(room).notifier).prev(); + Future loadOlder([InMemoryChatController? chatController]) async { + final controller = chatController ?? await future; + final client = ref.watch(ClientController.provider.notifier); + + client. + // await ref.watchInMemoryChatController? chatController(EventsController.provider(room).notifier).prev(); // final timeline = await ref.watch(EventsController.provider(room).future); // final controller = await future; @@ -127,25 +137,28 @@ class RoomChatController extends AsyncNotifier { required RelationType relationType, Message? relation, }) async { - // var taggedMessage = message; + var taggedMessage = message; - // for (final tag in tags) { - // final escaped = RegExp.escape(tag.id); - // final pattern = RegExp(r"@+(" + escaped + r")(#[^#]*#)?"); + for (final tag in tags) { + final escaped = RegExp.escape(tag.id); // TODO: Fix + final pattern = RegExp(r"@+(" + escaped + r")(#[^#]*#)?"); - // taggedMessage = taggedMessage.replaceAllMapped( - // pattern, - // (match) => match.group(1)!, - // ); - // } + taggedMessage = taggedMessage.replaceAllMapped( + pattern, + (match) => match.group(1)!, + ); + } - // await room.sendTextEvent( - // taggedMessage, - // editEventId: relationType == RelationType.edit ? relation?.id : null, - // inReplyTo: (relationType == RelationType.reply && relation != null) - // ? await room.getEventById(relation.id) - // : null, - // ); + final client = ref.watch(ClientController.provider.notifier); + client.sendMessage( + SendMessageRequest( + roomId: roomId, + text: taggedMessage, + relation: relation == null + ? null + : Relation(eventId: relation.id, relationType: relationType), + ), + ); } Future resolveUser(String id) async { diff --git a/lib/controllers/spaces_controller.dart b/lib/controllers/spaces_controller.dart index f0d7a6b..292e323 100644 --- a/lib/controllers/spaces_controller.dart +++ b/lib/controllers/spaces_controller.dart @@ -45,19 +45,21 @@ class SpacesController extends Notifier> { ) .toISet(); - final dmRooms = rooms.values - .where((room) => room.metadata?.dmUserId != null) - .toIList(); - - final homeRooms = rooms.entries + final otherRooms = rooms.entries .where( (e) => - e.value.metadata?.dmUserId == null && !allNestedRoomIds.contains(e.key) && !topLevelSpaceIds.contains(e.key) && !spaceEdges.containsKey(e.key), ) - .map((e) => e.value) + .map((e) => e.value); + + final homeRooms = otherRooms + .where((room) => room.metadata?.dmUserId == null) + .toIList(); + + final dmRooms = otherRooms + .where((room) => room.metadata?.dmUserId != null) .toIList(); final topLevelSpacesList = topLevelSpaceIds diff --git a/lib/helpers/extensions/event_to_message.dart b/lib/helpers/extensions/event_to_message.dart index b79901a..699b9f3 100644 --- a/lib/helpers/extensions/event_to_message.dart +++ b/lib/helpers/extensions/event_to_message.dart @@ -1,8 +1,8 @@ import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/models/event.dart"; -import "package:nexus/models/get_event_request.dart"; -import "package:nexus/models/get_related_events_request.dart"; +import "package:nexus/models/requests/get_event_request.dart"; +import "package:nexus/models/requests/get_related_events_request.dart"; extension EventToMessage on Event { Future toMessage( @@ -92,8 +92,8 @@ extension EventToMessage on Event { // ), ("m.sticker" || "m.room.message") => switch (content["msgtype"]) { ("m.sticker" || "m.image") => Message.image( - metadata: metadata, id: eventId, + metadata: metadata, authorId: authorId, text: event.localContent?.sanitizedHtml, source: "(await getAttachmentUri()).toString()", // TODO @@ -102,8 +102,8 @@ extension EventToMessage on Event { blurhash: (content["info"] as Map?)?["xyz.amorgan.blurhash"], ), "m.audio" => Message.audio( - metadata: metadata, id: eventId, + metadata: metadata, authorId: authorId, text: content["body"], replyToMessageId: replyId, diff --git a/lib/models/client_state.dart b/lib/models/client_state.dart index a8781d0..4063cc5 100644 --- a/lib/models/client_state.dart +++ b/lib/models/client_state.dart @@ -8,7 +8,7 @@ abstract class ClientState with _$ClientState { required bool isInitialized, required bool isLoggedIn, required bool isVerified, - required String userId, + required String? userId, }) = _ClientState; factory ClientState.fromJson(Map json) => diff --git a/lib/models/paginate.dart b/lib/models/paginate.dart new file mode 100644 index 0000000..4faf4e9 --- /dev/null +++ b/lib/models/paginate.dart @@ -0,0 +1,16 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:freezed_annotation/freezed_annotation.dart"; +import "package:nexus/models/event.dart"; +part "paginate.freezed.dart"; +part "paginate.g.dart"; + +@freezed +abstract class Paginate with _$Paginate { + const factory Paginate({ + required IList events, + required bool hasMore, + }) = _Paginate; + + factory Paginate.fromJson(Map json) => + _$PaginateFromJson(json); +} diff --git a/lib/models/redact_event_request.dart b/lib/models/redact_event_request.dart deleted file mode 100644 index b742351..0000000 --- a/lib/models/redact_event_request.dart +++ /dev/null @@ -1,3 +0,0 @@ -import "package:nexus/models/report_request.dart"; - -typedef RedactEventRequest = ReportRequest; diff --git a/lib/models/get_event_request.dart b/lib/models/requests/get_event_request.dart similarity index 100% rename from lib/models/get_event_request.dart rename to lib/models/requests/get_event_request.dart diff --git a/lib/models/get_related_events_request.dart b/lib/models/requests/get_related_events_request.dart similarity index 100% rename from lib/models/get_related_events_request.dart rename to lib/models/requests/get_related_events_request.dart diff --git a/lib/models/login_request.dart b/lib/models/requests/login_request.dart similarity index 100% rename from lib/models/login_request.dart rename to lib/models/requests/login_request.dart diff --git a/lib/models/requests/paginate_request.dart b/lib/models/requests/paginate_request.dart new file mode 100644 index 0000000..5941499 --- /dev/null +++ b/lib/models/requests/paginate_request.dart @@ -0,0 +1,14 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +part "paginate_request.freezed.dart"; +part "paginate_request.g.dart"; + +@freezed +abstract class PaginateRequest with _$PaginateRequest { + const factory PaginateRequest({ + required String roomId, + @Default(20) int limit, + }) = _PaginateRequest; + + factory PaginateRequest.fromJson(Map json) => + _$PaginateRequestFromJson(json); +} diff --git a/lib/models/requests/redact_event_request.dart b/lib/models/requests/redact_event_request.dart new file mode 100644 index 0000000..fed2255 --- /dev/null +++ b/lib/models/requests/redact_event_request.dart @@ -0,0 +1,3 @@ +import "package:nexus/models/requests/report_request.dart"; + +typedef RedactEventRequest = ReportRequest; diff --git a/lib/models/report_request.dart b/lib/models/requests/report_request.dart similarity index 100% rename from lib/models/report_request.dart rename to lib/models/requests/report_request.dart diff --git a/lib/models/requests/send_message_request.dart b/lib/models/requests/send_message_request.dart new file mode 100644 index 0000000..6825d85 --- /dev/null +++ b/lib/models/requests/send_message_request.dart @@ -0,0 +1,55 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:freezed_annotation/freezed_annotation.dart"; +import "package:nexus/models/relation_type.dart"; +part "send_message_request.freezed.dart"; +part "send_message_request.g.dart"; + +@freezed +abstract class SendMessageRequest with _$SendMessageRequest { + const factory SendMessageRequest({ + required String roomId, + required String text, + @Default(Mentions()) @JsonKey(name: "m.mentions") Mentions mentions, + @JsonKey(name: "m.relates_to") Relation? relation, + }) = _SendMessageRequest; + + factory SendMessageRequest.fromJson(Map json) => + _$SendMessageRequestFromJson(json); +} + +@freezed +abstract class Mentions with _$Mentions { + const factory Mentions({ + @Default(false) bool room, + @Default(IList.empty()) IList userIds, + }) = _Mentions; + + factory Mentions.fromJson(Map json) => + _$MentionsFromJson(json); +} + +@freezed +abstract class Relation with _$Relation { + const Relation._(); // required for custom methods + + const factory Relation({ + required String eventId, + required RelationType relationType, + }) = _Relation; + + @override + Map toJson() { + switch (relationType) { + case RelationType.reply: + return { + "m.in_reply_to": {"event_id": eventId}, + }; + + case RelationType.edit: + return {"rel_type": "m.replace", "event_id": eventId}; + } + } + + factory Relation.fromJson(Map json) => + _$RelationFromJson(json); +} diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index 83d1298..b15f6d4 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -5,7 +5,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/helpers/launch_helper.dart"; import "package:nexus/models/homeserver.dart"; -import "package:nexus/models/login_request.dart"; +import "package:nexus/models/requests/login_request.dart"; import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/divider_text.dart"; import "package:nexus/widgets/loading.dart"; diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index c124c24..4214e89 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -18,7 +18,7 @@ import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/show_context_menu.dart"; import "package:nexus/models/relation_type.dart"; -import "package:nexus/models/report_request.dart"; +import "package:nexus/models/requests/report_request.dart"; import "package:nexus/widgets/chat_page/chat_box.dart"; import "package:nexus/widgets/chat_page/html/html.dart"; import "package:nexus/widgets/chat_page/room_appbar.dart";