This commit is contained in:
Henry Hiles 2026-01-30 02:13:46 +01:00
commit c2214fcc44
No known key found for this signature in database
15 changed files with 186 additions and 89 deletions

View file

@ -15,6 +15,7 @@ import "package:nexus/models/event.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/get_room_state_request.dart";
import "package:nexus/models/requests/login_request.dart";
import "package:nexus/models/profile.dart";
import "package:nexus/models/requests/paginate_request.dart";
@ -117,7 +118,9 @@ class ClientController extends AsyncNotifier<int> {
calloc.free(bufferPointer);
return response.buf.toJson();
final json = response.buf.toJson();
if (json is String) throw json;
return json;
}
Future<void> redactEvent(RedactEventRequest report) =>
@ -140,6 +143,12 @@ class ClientController extends AsyncNotifier<int> {
await _sendCommand("leave_room", {"room_id": room.metadata!.id});
}
Future<IList<Event>> getRoomState(GetRoomStateRequest request) async {
final response =
(await _sendCommand("get_room_state", request.toJson())) as List;
return response.map((event) => Event.fromJson(event)).toIList();
}
Future<IList<Event>?> getRelatedEvents(
GetRelatedEventsRequest request,
) async {
@ -157,11 +166,8 @@ class ClientController extends AsyncNotifier<int> {
Future<Paginate> paginate(PaginateRequest request) async =>
Paginate.fromJson(await _sendCommand("paginate", request.toJson()));
Future<Profile?> getProfile(String userId) async {
final json = await _sendCommand("get_profile", {"user_id": userId});
return json == null ? null : Profile.fromJson(json);
}
Future<Profile> getProfile(String userId) async =>
Profile.fromJson(await _sendCommand("get_profile", {"user_id": userId}));
Future<void> reportEvent(ReportRequest report) =>
_sendCommand("report_event", report.toJson());

View file

@ -1,22 +1,26 @@
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:matrix/matrix.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/room.dart";
class MembersController extends AsyncNotifier<IList<MatrixEvent>> {
class MembersController extends AsyncNotifier<IList<Event>> {
final Room room;
MembersController(this.room);
@override
Future<IList<MatrixEvent>> build() async => IList(
(await room.client.getMembersByRoom(
room.id,
notMembership: Membership.leave,
)) ??
[],
);
Future<IList<Event>> build() async =>
(room.state["m.room.member"]?.values ?? [])
.map(
(eventRowId) => room.events.firstWhereOrNull(
(event) => event.rowId == eventRowId,
),
)
.nonNulls
.toIList();
static final provider = AsyncNotifierProvider.family
.autoDispose<MembersController, IList<MatrixEvent>, Room>(
.autoDispose<MembersController, IList<Event>, Room>(
MembersController.new,
);
}

View file

@ -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<Profile> {
final String userId;
ProfileController(this.userId);
@override
Future<Profile> build() =>
ref.watch(ClientController.provider.notifier).getProfile(userId);
static final provider =
AsyncNotifierProvider.family<ProfileController, Profile, String>(
ProfileController.new,
);
}

View file

@ -6,14 +6,17 @@ import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:fluttertagger/fluttertagger.dart" as tagger;
import "package:nexus/controllers/client_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/helpers/extensions/event_to_message.dart";
import "package:nexus/helpers/extensions/list_to_messages.dart";
import "package:nexus/models/event.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";
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<ChatController> {
final String roomId;
@ -22,9 +25,8 @@ class RoomChatController extends AsyncNotifier<ChatController> {
@override
Future<ChatController> build() async {
final client = ref.watch(ClientController.provider.notifier);
final events =
ref.read(SelectedRoomController.provider)?.events ??
const IList.empty();
final room = ref.read(SelectedRoomController.provider);
if (room == null) return InMemoryChatController();
ref.onDispose(
ref.listen(NewEventsController.provider(roomId), (_, next) async {
@ -38,7 +40,7 @@ class RoomChatController extends AsyncNotifier<ChatController> {
await controller.removeMessage(message);
} else {
final message = await event.toMessage(client, includeEdits: true);
final message = await event.toMessage(ref, includeEdits: true);
if (event.relationType == "m.replace") {
final controller = await future;
final oldMessage = controller.messages.firstWhereOrNull(
@ -69,11 +71,26 @@ class RoomChatController extends AsyncNotifier<ChatController> {
}).close,
);
final messages = await events.toMessages(client);
final messages = await room.timeline
.map(
(timelineRowTuple) => room.events.firstWhereOrNull(
(event) => event.rowId == timelineRowTuple.eventRowId,
),
)
.nonNulls
.toMessages(ref);
final controller = InMemoryChatController(messages: messages);
ref.onDispose(controller.dispose);
if (messages.length < 20) await loadOlder(controller);
await client.getRoomState(
GetRoomStateRequest(
roomId: roomId,
fetchMembers: room.metadata?.hasMemberList == false,
),
);
return controller;
}
@ -109,23 +126,47 @@ class RoomChatController extends AsyncNotifier<ChatController> {
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 response = await client.paginate(
PaginateRequest(
roomId: roomId,
maxTimelineId: controller.messages.firstOrNull?.metadata?["timelineId"],
),
);
// final controller = await future;
// await controller.insertAllMessages(
// await timeline.events
// .where(
// (event) => !currentEvents.messages.any(
// (existingEvent) => existingEvent.id == event.eventId,
// ),
// )
// .toList()
// .toMessages(room, timeline),
// index: 0,
// );
// ref.notifyListeners();
ref
.watch(RoomsController.provider.notifier)
.update(
IMap({
roomId: Room(
events: response.events.addAll(response.relatedEvents),
hasMore: response.hasMore,
timeline: response.events
.map(
(event) => TimelineRowTuple(
timelineRowId: event.timelineRowId,
eventRowId: event.rowId,
),
)
.toIList(),
),
}),
const ISet.empty(),
);
final existingIds = controller.messages.map((m) => m.id).toSet();
final messages = await response.events
.where((event) => !existingIds.contains(event.eventId))
.fold(<String, Event>{}, (acc, event) {
acc[event.eventId] =
event; // overwrites duplicates in response.events
return acc;
})
.values
.toIList()
.reversed
.toMessages(ref);
await controller.insertAllMessages(messages, index: 0);
}
Future<void> updateMessage(Message message, Message newMessage) async =>
@ -167,7 +208,7 @@ class RoomChatController extends AsyncNotifier<ChatController> {
.getProfile(id);
return chat.User(
id: id,
name: user?.displayName,
name: user.displayName,
// imageSource: user.avatarUrl == null
// ? null
// : (await ref.watch(

View file

@ -1,3 +1,4 @@
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/new_events_controller.dart";
@ -28,16 +29,22 @@ class RoomsController extends Notifier<IMap<String, Room>> {
),
state: incoming.state.entries.fold(
existing.state,
(stateAcc, event) => stateAcc.add(
(previousValue, event) => previousValue.add(
event.key,
(stateAcc[event.key] ?? IMap<dynamic, dynamic>()).addAll(
(previousValue[event.key] ?? const IMap.empty()).addAll(
event.value,
),
),
),
timeline: incoming.reset
? incoming.timeline
: existing.timeline.addAll(incoming.timeline),
timeline:
(incoming.reset
? incoming.timeline
: existing.timeline.updateById(
incoming.timeline,
(item) => item.timelineRowId,
))
.sortedBy((element) => element.timelineRowId)
.toIList(),
receipts: incoming.receipts.entries.fold(
existing.receipts,
(receiptAcc, event) => receiptAcc.add(