big performance improvements!!

This commit is contained in:
Henry Hiles 2026-02-13 16:56:39 -05:00
commit 2d90a2adfe
No known key found for this signature in database
8 changed files with 86 additions and 45 deletions

View file

@ -1,6 +1,7 @@
import "dart:developer"; import "dart:developer";
import "dart:ffi"; import "dart:ffi";
import "dart:isolate"; import "dart:isolate";
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:ffi/ffi.dart"; import "package:ffi/ffi.dart";
import "package:flutter/foundation.dart"; import "package:flutter/foundation.dart";
@ -170,8 +171,12 @@ class ClientController extends AsyncNotifier<int> {
} }
Future<Event?> getEvent(GetEventRequest request) async { Future<Event?> getEvent(GetEventRequest request) async {
final json = await _sendCommand("get_event", request.toJson()); final event = request.room.events.firstWhereOrNull(
(event) => event.eventId == request.eventId,
);
if (event != null) return event;
final json = await _sendCommand("get_event", request.toJson());
return json == null ? null : Event.fromJson(json); return json == null ? null : Event.fromJson(json);
} }

View file

@ -1,3 +1,4 @@
import "package:collection/collection.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_controller.dart";
@ -6,7 +7,6 @@ import "package:nexus/controllers/profile_controller.dart";
import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart";
import "package:nexus/models/message_config.dart"; import "package:nexus/models/message_config.dart";
import "package:nexus/models/requests/get_event_request.dart"; import "package:nexus/models/requests/get_event_request.dart";
import "package:nexus/models/requests/get_related_events_request.dart";
class MessageController extends AsyncNotifier<Message?> { class MessageController extends AsyncNotifier<Message?> {
final MessageConfig config; final MessageConfig config;
@ -19,22 +19,20 @@ class MessageController extends AsyncNotifier<Message?> {
} }
final client = ref.watch(ClientController.provider.notifier); final client = ref.watch(ClientController.provider.notifier);
final newEvents = await client.getRelatedEvents(
GetRelatedEventsRequest(
roomId: config.event.roomId,
eventId: config.event.eventId,
relationType: "m.replace",
),
);
if (!ref.mounted) return null; if (!ref.mounted) return null;
final event = newEvents?.lastOrNull ?? config.event; final event = config.event.lastEditRowId == null
? config.event
: config.room.events.firstWhereOrNull(
(e) => e.rowId == config.event.lastEditRowId,
) ??
config.event;
final replyId = final replyId =
config.event.content["m.relates_to"]?["m.in_reply_to"]?["event_id"]; config.event.content["m.relates_to"]?["m.in_reply_to"]?["event_id"];
final replyEvent = replyId == null final replyEvent = replyId == null
? null ? null
: await client.getEvent( : await client.getEvent(
GetEventRequest(roomId: config.event.roomId, eventId: replyId), GetEventRequest(room: config.room, eventId: replyId),
); );
if (!ref.mounted) return null; if (!ref.mounted) return null;
@ -58,7 +56,11 @@ class MessageController extends AsyncNotifier<Message?> {
if (replyEvent != null) if (replyEvent != null)
"reply": await ref.watch( "reply": await ref.watch(
MessageController.provider( MessageController.provider(
MessageConfig(event: replyEvent, mustBeText: true), MessageConfig(
event: replyEvent,
room: config.room,
mustBeText: true,
),
).future, ).future,
), ),
"body": newContent?["body"] ?? content["body"], "body": newContent?["body"] ?? content["body"],
@ -152,21 +154,24 @@ class MessageController extends AsyncNotifier<Message?> {
), ),
_ => asText, _ => asText,
}, },
"m.room.member" => Message.system( "m.room.member" =>
metadata: metadata, content["membership"] == event.unsigned["prev_content"]?["membership"]
id: config.event.eventId, ? null
authorId: event.authorId, : Message.system(
deliveredAt: config.event.timestamp, metadata: metadata,
text: id: config.event.eventId,
"${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) { authorId: event.authorId,
"invite" => "was invited to", deliveredAt: config.event.timestamp,
"join" => "joined", text:
"leave" => "left", "${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) {
"knock" => "asked to join", "invite" => "was invited to",
"ban" => "was banned from", "join" => "joined",
_ => "did something relating to", "leave" => "left",
}} the room.", "knock" => "asked to join",
), "ban" => "was banned from",
_ => "did something relating to",
}} the room.",
),
"m.room.redaction" => null, "m.room.redaction" => null,
_ => _ =>
// Turn this on for debugging purposes // Turn this on for debugging purposes

View file

@ -2,24 +2,26 @@ 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";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/message_controller.dart"; import "package:nexus/controllers/message_controller.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/message_config.dart"; import "package:nexus/models/message_config.dart";
import "package:nexus/models/messages_config.dart";
class MessagesController extends AsyncNotifier<IList<Message>> { class MessagesController extends AsyncNotifier<IList<Message>> {
final IList<Event> events; final MessagesConfig config;
MessagesController(this.events); MessagesController(this.config);
@override @override
Future<IList<Message>> build() async => (await Future.wait( Future<IList<Message>> build() async => (await Future.wait(
events.map( config.events.map(
(event) => ref.watch( (event) => ref.watch(
MessageController.provider(MessageConfig(event: event)).future, MessageController.provider(
MessageConfig(event: event, room: config.room),
).future,
), ),
), ),
)).nonNulls.toIList(); )).nonNulls.toIList();
static final provider = AsyncNotifierProvider.family static final provider = AsyncNotifierProvider.family
.autoDispose<MessagesController, IList<Message>, IList<Event>>( .autoDispose<MessagesController, IList<Message>, MessagesConfig>(
MessagesController.new, MessagesController.new,
); );
} }

View file

@ -11,6 +11,7 @@ import "package:nexus/controllers/new_events_controller.dart";
import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart"; import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/models/message_config.dart"; import "package:nexus/models/message_config.dart";
import "package:nexus/models/messages_config.dart";
import "package:nexus/models/requests/get_room_state_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/paginate_request.dart";
import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/requests/redact_event_request.dart";
@ -30,14 +31,17 @@ class RoomChatController extends AsyncNotifier<ChatController> {
final messages = await ref.watch( final messages = await ref.watch(
MessagesController.provider( MessagesController.provider(
room.timeline MessagesConfig(
.map( room: room,
(timelineRowTuple) => room.events.firstWhereOrNull( events: room.timeline
(event) => event.rowId == timelineRowTuple.eventRowId, .map(
), (timelineRowTuple) => room.events.firstWhereOrNull(
) (event) => event.rowId == timelineRowTuple.eventRowId,
.nonNulls ),
.toIList(), )
.nonNulls
.toIList(),
),
).future, ).future,
); );
final controller = InMemoryChatController(messages: messages.toList()); final controller = InMemoryChatController(messages: messages.toList());
@ -57,7 +61,7 @@ class RoomChatController extends AsyncNotifier<ChatController> {
} else { } else {
final message = await ref.watch( final message = await ref.watch(
MessageController.provider( MessageController.provider(
MessageConfig(event: event, includeEdits: true), MessageConfig(event: event, room: room, includeEdits: true),
).future, ).future,
); );
if (event.relationType == "m.replace") { if (event.relationType == "m.replace") {
@ -191,8 +195,13 @@ class RoomChatController extends AsyncNotifier<ChatController> {
const ISet.empty(), const ISet.empty(),
); );
final room = ref.watch(RoomsController.provider)[roomId];
if (room == null) return;
final messages = await ref.watch( final messages = await ref.watch(
MessagesController.provider(response.events.reversed).future, MessagesController.provider(
MessagesConfig(room: room, events: response.events.reversed),
).future,
); );
await controller.insertAllMessages( await controller.insertAllMessages(
messages messages

View file

@ -27,7 +27,7 @@ abstract class Event with _$Event {
String? decryptionError, String? decryptionError,
String? sendError, String? sendError,
@Default(IMap.empty()) IMap<String, int> reactions, @Default(IMap.empty()) IMap<String, int> reactions,
int? lastEditRowId, @JsonKey(name: "last_edit_rowid") int? lastEditRowId,
@UnreadTypeConverter() UnreadType? unreadType, @UnreadTypeConverter() UnreadType? unreadType,
}) = _Event; }) = _Event;

View file

@ -1,5 +1,6 @@
import "package:freezed_annotation/freezed_annotation.dart"; import "package:freezed_annotation/freezed_annotation.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/models/room.dart";
part "message_config.freezed.dart"; part "message_config.freezed.dart";
part "message_config.g.dart"; part "message_config.g.dart";
@ -8,6 +9,7 @@ abstract class MessageConfig with _$MessageConfig {
const factory MessageConfig({ const factory MessageConfig({
@Default(false) bool mustBeText, @Default(false) bool mustBeText,
@Default(false) bool includeEdits, @Default(false) bool includeEdits,
required Room room,
required Event event, required Event event,
}) = _MessageConfig; }) = _MessageConfig;

View file

@ -0,0 +1,17 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:freezed_annotation/freezed_annotation.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/room.dart";
part "messages_config.freezed.dart";
part "messages_config.g.dart";
@freezed
abstract class MessagesConfig with _$MessagesConfig {
const factory MessagesConfig({
required Room room,
required IList<Event> events,
}) = _MessagesConfig;
factory MessagesConfig.fromJson(Map<String, Object?> json) =>
_$MessagesConfigFromJson(json);
}

View file

@ -1,11 +1,12 @@
import "package:freezed_annotation/freezed_annotation.dart"; import "package:freezed_annotation/freezed_annotation.dart";
import "package:nexus/models/room.dart";
part "get_event_request.freezed.dart"; part "get_event_request.freezed.dart";
part "get_event_request.g.dart"; part "get_event_request.g.dart";
@freezed @freezed
abstract class GetEventRequest with _$GetEventRequest { abstract class GetEventRequest with _$GetEventRequest {
const factory GetEventRequest({ const factory GetEventRequest({
required String roomId, required Room room,
required String eventId, required String eventId,
@Default(false) bool unredact, @Default(false) bool unredact,
}) = _GetEventRequest; }) = _GetEventRequest;