From b154b4ee2e7b0e553f4db307475c608358b11dcb Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Tue, 3 Nov 2026 15:16:38 -0500 Subject: [PATCH] add GomuksEvent model --- lib/controllers/account_data.dart | 6 ++ lib/controllers/gomuks_listener.dart | 120 +++++++------------------- lib/controllers/invites.dart | 10 +++ lib/controllers/rooms.dart | 25 ++++++ lib/controllers/space_edges.dart | 4 +- lib/controllers/top_level_spaces.dart | 3 +- lib/controllers/typing.dart | 5 ++ lib/models/gomuks_event.dart | 34 ++++++++ 8 files changed, 114 insertions(+), 93 deletions(-) create mode 100644 lib/models/gomuks_event.dart diff --git a/lib/controllers/account_data.dart b/lib/controllers/account_data.dart index c9dc0b4..7d6e449 100644 --- a/lib/controllers/account_data.dart +++ b/lib/controllers/account_data.dart @@ -8,6 +8,7 @@ import "package:nexus/controllers/client.dart"; import "package:nexus/models/account_data.dart"; import "package:nexus/models/push_rules.dart"; import "package:nexus/models/requests/update_push_rule.dart"; +import "package:nexus/models/sync_data.dart"; class AccountDataController extends Notifier { @override @@ -26,6 +27,11 @@ class AccountDataController extends Notifier { } }); + void handleSync(SyncData sync) { + if (sync.clearState) state = .new(); + update(sync.accountData); + } + Future setEnabledImagePacks(ImagePackRooms packs) { ISet<(String, String)> keysOf(ImagePackRooms rooms) => rooms.rooms.entries .expand((entry) => entry.value.keys.map((key) => (entry.key, key))) diff --git a/lib/controllers/gomuks_listener.dart b/lib/controllers/gomuks_listener.dart index 0298727..3100253 100644 --- a/lib/controllers/gomuks_listener.dart +++ b/lib/controllers/gomuks_listener.dart @@ -4,7 +4,6 @@ import "dart:io"; import "dart:isolate"; import "package:collection/collection.dart"; -import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/foundation.dart"; import "package:nexus/controllers/account_data.dart"; import "package:nexus/controllers/accounts.dart"; @@ -22,9 +21,7 @@ import "package:nexus/controllers/typing.dart"; import "package:nexus/helpers/extensions/gomuks_buffer.dart"; import "package:nexus/main.dart"; import "package:ffi/ffi.dart"; -import "package:nexus/models/event.dart"; -import "package:nexus/models/events_decrypted.dart"; -import "package:nexus/models/sync_data.dart"; +import "package:nexus/models/gomuks_event.dart"; import "package:nexus/src/third_party/gomuks.g.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; @@ -42,100 +39,43 @@ class GomuksListenerController extends AsyncNotifier { int requestId, GomuksOwnedBuffer data, ) { + final type = command.cast().toDartString(); try { - final muksEventType = command.cast().toDartString(); - debugPrint("Handling $muksEventType..."); - final decodedMuksEvent = data.toJsonAndFree(); - - switch (muksEventType) { - case "client_state": - ref - .read(ClientStateController.provider.notifier) - .set(.fromJson(decodedMuksEvent)); - case "sync_status": - ref - .read(SyncStatusController.provider.notifier) - .set(.fromJson(decodedMuksEvent)); - case "init_complete": + debugPrint("Handling $type..."); + switch (GomuksEvent.parse(type, data.toJsonAndFree())) { + case ClientStateEvent(:final state): + ref.read(ClientStateController.provider.notifier).set(state); + case SyncStatusEvent(:final status): + ref.read(SyncStatusController.provider.notifier).set(status); + case InitCompleteEvent(): ref.read(InitCompleteController.provider.notifier).complete(); - case "send_complete": - final event = Event.fromJson(decodedMuksEvent["event"]); + case SendCompleteEvent(:final event): + ref.read(RoomsController.provider.notifier).storeEvents([ + event, + ]); + case TypingEvent(:final typing): + ref.read(TypingController.provider.notifier).set(typing); + case EventsDecryptedEvent(:final decrypted): ref .read(RoomsController.provider.notifier) - .update( - .new({ - event.roomId: .new(events: .new({event.rowId: event})), - }), - ); - case "typing": + .handleDecrypted(decrypted); + case SyncCompleteEvent(:final sync): + ref.read(RoomsController.provider.notifier).handleSync(sync); ref - .read(TypingController.provider.notifier) - .set(.fromJson(decodedMuksEvent)); - case "events_decrypted": - final decrypted = EventsDecrypted.fromJson(decodedMuksEvent); - final metadata = ref - .read(RoomsController.provider)[decrypted.roomId] - ?.metadata; - + .read(AccountDataController.provider.notifier) + .handleSync(sync); + ref.read(InvitesController.provider.notifier).handleSync(sync); + ref.read(TypingController.provider.notifier).handleSync(sync); ref - .read(RoomsController.provider.notifier) - .update( - .new({ - decrypted.roomId: .new( - events: decrypted.events, - metadata: metadata?.copyWith( - previewEventRowID: - decrypted.previewEventRowID ?? - metadata.previewEventRowID, - sortingTimestamp: - decrypted.sortingTimestamp ?? - metadata.sortingTimestamp, - ), - ), - }), - ); - case "sync_complete": - final syncData = SyncData.fromJson(decodedMuksEvent); - final roomProvider = RoomsController.provider; - final accountDataProvider = AccountDataController.provider; - final invitesProvider = InvitesController.provider; - - if (syncData.clearState) { - ref.invalidate(roomProvider); - ref.invalidate(accountDataProvider); - ref.invalidate(invitesProvider); - ref.invalidate(TypingController.provider); - } - + .read(TopLevelSpacesController.provider.notifier) + .handleSync(sync); ref - .read(roomProvider.notifier) - .update(syncData.rooms, syncData.leftRooms); - ref - .read(accountDataProvider.notifier) - .update(syncData.accountData); - ref - .read(invitesProvider.notifier) - .update( - syncData.invitedRooms, - joinedRooms: syncData.rooms.keys.toISet(), - leftRooms: syncData.leftRooms, - ); - - if (syncData.topLevelSpaces case final topLevelSpaces?) { - ref - .read(TopLevelSpacesController.provider.notifier) - .set(topLevelSpaces); - } - - if (syncData.spaceEdges case final spaceEdges?) { - ref - .read(SpaceEdgesController.provider.notifier) - .set(spaceEdges); - } - default: - debugPrint("Unhandled event: $muksEventType"); + .read(SpaceEdgesController.provider.notifier) + .handleSync(sync); + case UnknownGomuksEvent(): + debugPrint("Unhandled event: $type"); } - debugPrint("Finished handling $muksEventType..."); + debugPrint("Finished handling $type..."); } catch (error, stackTrace) { if (kDebugMode) { debugPrintStack(stackTrace: stackTrace, label: error.toString()); diff --git a/lib/controllers/invites.dart b/lib/controllers/invites.dart index 9c8a6e4..42821ac 100644 --- a/lib/controllers/invites.dart +++ b/lib/controllers/invites.dart @@ -17,6 +17,7 @@ import "package:nexus/models/content/name.dart"; import "package:nexus/models/content/topic.dart"; import "package:nexus/models/invited_room.dart"; import "package:nexus/models/room_summary.dart"; +import "package:nexus/models/sync_data.dart"; /// Pending invites, newest first. class InvitesController extends Notifier> { @@ -103,6 +104,15 @@ class InvitesController extends Notifier> { .removeWhere((invite) => removed.contains(invite.roomId)); } + void handleSync(SyncData sync) { + if (sync.clearState) state = .new(); + update( + sync.invitedRooms, + joinedRooms: sync.rooms.keys.toISet(), + leftRooms: sync.leftRooms, + ); + } + void remove(String roomId) => state = state.removeWhere((invite) => invite.roomId == roomId); diff --git a/lib/controllers/rooms.dart b/lib/controllers/rooms.dart index 67e219b..66ffa3b 100644 --- a/lib/controllers/rooms.dart +++ b/lib/controllers/rooms.dart @@ -4,8 +4,10 @@ import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/models/event.dart"; +import "package:nexus/models/events_decrypted.dart"; import "package:nexus/models/read_receipt.dart"; import "package:nexus/models/room.dart"; +import "package:nexus/models/sync_data.dart"; class RoomsController extends Notifier> { @override @@ -82,6 +84,29 @@ class RoomsController extends Notifier> { ), ); + void handleSync(SyncData sync) { + if (sync.clearState) state = .new(); + update(sync.rooms, sync.leftRooms); + } + + void handleDecrypted(EventsDecrypted decrypted) { + final metadata = state[decrypted.roomId]?.metadata; + + update( + .new({ + decrypted.roomId: .new( + events: decrypted.events, + metadata: metadata?.copyWith( + previewEventRowID: + decrypted.previewEventRowID ?? metadata.previewEventRowID, + sortingTimestamp: + decrypted.sortingTimestamp ?? metadata.sortingTimestamp, + ), + ), + }), + ); + } + static IMap> addRelations( IMap> relations, Iterable events, diff --git a/lib/controllers/space_edges.dart b/lib/controllers/space_edges.dart index 81347c5..a0ddab7 100644 --- a/lib/controllers/space_edges.dart +++ b/lib/controllers/space_edges.dart @@ -1,13 +1,13 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/models/space_edge.dart"; +import "package:nexus/models/sync_data.dart"; class SpaceEdgesController extends Notifier>> { @override IMap> build() => .new(); - void set(IMap> newEdges) => - state = state.addAll(newEdges); + void handleSync(SyncData sync) => state = sync.spaceEdges ?? state; static final provider = NotifierProvider>>( diff --git a/lib/controllers/top_level_spaces.dart b/lib/controllers/top_level_spaces.dart index 321e29d..acdacad 100644 --- a/lib/controllers/top_level_spaces.dart +++ b/lib/controllers/top_level_spaces.dart @@ -1,11 +1,12 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/models/sync_data.dart"; class TopLevelSpacesController extends Notifier> { @override IList build() => .new(); - void set(IList newSpaces) => state = newSpaces; + void handleSync(SyncData sync) => state = sync.topLevelSpaces ?? state; static final provider = NotifierProvider>( diff --git a/lib/controllers/typing.dart b/lib/controllers/typing.dart index d1f3d28..b0e3f93 100644 --- a/lib/controllers/typing.dart +++ b/lib/controllers/typing.dart @@ -1,5 +1,6 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/models/sync_data.dart"; import "package:nexus/models/typing.dart"; class TypingController extends Notifier>> { @@ -8,6 +9,10 @@ class TypingController extends Notifier>> { void set(Typing typing) => state = state.add(typing.roomId, typing.userIds); + void handleSync(SyncData sync) { + if (sync.clearState) state = .new(); + } + static final provider = NotifierProvider>>( TypingController.new, diff --git a/lib/models/gomuks_event.dart b/lib/models/gomuks_event.dart new file mode 100644 index 0000000..821ea4a --- /dev/null +++ b/lib/models/gomuks_event.dart @@ -0,0 +1,34 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +import "package:nexus/models/client_state.dart"; +import "package:nexus/models/event.dart"; +import "package:nexus/models/events_decrypted.dart"; +import "package:nexus/models/sync_data.dart"; +import "package:nexus/models/sync_status.dart"; +import "package:nexus/models/typing.dart"; + +part "gomuks_event.freezed.dart"; + +@freezed +sealed class GomuksEvent with _$GomuksEvent { + const factory GomuksEvent.clientState(ClientState state) = ClientStateEvent; + const factory GomuksEvent.syncStatus(SyncStatus status) = SyncStatusEvent; + const factory GomuksEvent.initComplete() = InitCompleteEvent; + const factory GomuksEvent.sendComplete(Event event) = SendCompleteEvent; + const factory GomuksEvent.typing(Typing typing) = TypingEvent; + const factory GomuksEvent.eventsDecrypted(EventsDecrypted decrypted) = + EventsDecryptedEvent; + const factory GomuksEvent.syncComplete(SyncData sync) = SyncCompleteEvent; + const factory GomuksEvent.unknown(String type) = UnknownGomuksEvent; + + factory GomuksEvent.parse(String type, Map json) => + switch (type) { + "client_state" => .clientState(.fromJson(json)), + "sync_status" => .syncStatus(.fromJson(json)), + "init_complete" => const .initComplete(), + "send_complete" => .sendComplete(.fromJson(json["event"])), + "typing" => .typing(.fromJson(json)), + "events_decrypted" => .eventsDecrypted(.fromJson(json)), + "sync_complete" => .syncComplete(.fromJson(json)), + _ => .unknown(type), + }; +}