shows room but not really

This commit is contained in:
Henry Hiles 2026-01-27 19:09:43 +00:00
commit a28bced44d
No known key found for this signature in database
23 changed files with 885 additions and 805 deletions

View file

@ -11,6 +11,8 @@ 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/login.dart";
import "package:nexus/models/report.dart";
import "package:nexus/models/room.dart";
import "package:nexus/models/sync_data.dart";
import "package:nexus/models/sync_status.dart";
import "package:nexus/src/third_party/gomuks.g.dart";
@ -118,6 +120,23 @@ class ClientController extends AsyncNotifier<int> {
}
}
Future<void> leaveRoom(Room room) async {
if (room.metadata == null) return;
await sendCommand("leave_room", {"room_id": room.metadata!.id});
}
Future<void> reportEvent(Report report) =>
sendCommand("report_event", report.toJson());
Future<void> markRead(Room room) async {
if (room.events.isEmpty || room.metadata == null) return;
await sendCommand("mark_read", {
"room_id": room.metadata?.id,
"receipt_type": "m.read",
"event_id": room.events.last.eventId,
});
}
Future<bool> login(Login login) async {
try {
await sendCommand("login", login.toJson());

View file

@ -0,0 +1,24 @@
import "package:ffi/ffi.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart";
import "package:nexus/src/third_party/gomuks.g.dart";
class HeaderController extends AsyncNotifier<Map<String, String>> {
@override
Future<Map<String, String>> build() async {
final handle = await ref.watch(ClientController.provider.future);
final info = GomuksGetAccountInfo(handle);
final headers = {
"authorization":
"Bearer ${info.access_token.cast<Utf8>().toDartString()}",
};
GomuksFreeAccountInfo(info);
return headers;
}
static final provider =
AsyncNotifierProvider<HeaderController, Map<String, String>>(
HeaderController.new,
);
}

View file

@ -2,13 +2,9 @@ import "package:collection/collection.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:matrix/matrix.dart";
import "package:nexus/controllers/avatar_controller.dart";
import "package:nexus/controllers/events_controller.dart";
import "package:nexus/helpers/extensions/event_to_message.dart";
import "package:nexus/helpers/extensions/list_to_messages.dart";
import "package:fluttertagger/fluttertagger.dart" as tagger;
import "package:nexus/models/relation_type.dart";
import "package:nexus/models/room.dart";
class RoomChatController extends AsyncNotifier<ChatController> {
final Room room;
@ -16,52 +12,54 @@ class RoomChatController extends AsyncNotifier<ChatController> {
@override
Future<ChatController> build() async {
final timeline = await ref.watch(EventsController.provider(room).future);
// final timeline = await ref.watch(EventsController.provider(room).future);
ref.onDispose(
room.client.onTimelineEvent.stream.listen((event) async {
if (event.roomId != room.id) return;
return InMemoryChatController();
if (event.type == EventTypes.Redaction) {
final controller = await future;
final message = controller.messages.firstWhereOrNull(
(message) => message.id == event.redacts,
);
if (message == null) return;
// ref.onDispose(
// room.client.onTimelineEvent.stream.listen((event) async {
// if (event.roomId != room.metadata.id) return;
await controller.removeMessage(message);
} else {
final message = await event.toMessage(includeEdits: true, timeline);
if (event.relationshipType == RelationshipTypes.edit) {
final controller = await future;
final oldMessage = controller.messages.firstWhereOrNull(
(element) => element.id == event.relationshipEventId,
);
if (oldMessage == null || message == null) return;
return await updateMessage(
oldMessage,
message.copyWith(
id: oldMessage.id,
replyToMessageId: oldMessage.replyToMessageId,
metadata: {
...(oldMessage.metadata ?? {}),
...((message.metadata ?? {}).filterMap(
(key, value) => value == null ? null : MapEntry(key, value),
)),
},
),
);
}
if (message != null) {
return await insertMessage(message);
}
}
}).cancel,
);
// if (event.type == "m.room.redaction") {
// final controller = await future;
// final message = controller.messages.firstWhereOrNull(
// (message) => message.id == event.redacts,
// );
// if (message == null) return;
return InMemoryChatController(
messages: await timeline.events.toMessages(room, timeline),
);
// await controller.removeMessage(message);
// } else {
// final message = await event.toMessage(includeEdits: true, timeline);
// if (event.relationshipType == RelationshipTypes.edit) {
// final controller = await future;
// final oldMessage = controller.messages.firstWhereOrNull(
// (element) => element.id == event.relationshipEventId,
// );
// if (oldMessage == null || message == null) return;
// return await updateMessage(
// oldMessage,
// message.copyWith(
// id: oldMessage.id,
// replyToMessageId: oldMessage.replyToMessageId,
// metadata: {
// ...(oldMessage.metadata ?? {}),
// ...((message.metadata ?? {}).filterMap(
// (key, value) => value == null ? null : MapEntry(key, value),
// )),
// },
// ),
// );
// }
// if (message != null) {
// return await insertMessage(message);
// }
// }
// }).cancel,
// );
// return InMemoryChatController(
// messages: await timeline.events.toMessages(room, timeline),
// );
}
Future<void> insertMessage(Message message) async {
@ -79,37 +77,29 @@ class RoomChatController extends AsyncNotifier<ChatController> {
}
Future<void> deleteMessage(Message message, {String? reason}) async {
final controller = await future;
await controller.removeMessage(message);
await room.redactEvent(message.id, reason: reason);
// final controller = await future;
// await controller.removeMessage(message);
// await room.redactEvent(message.id, reason: reason);
}
Future<void> loadOlder() async {
final currentEvents = await future;
await ref.watch(EventsController.provider(room).notifier).prev();
final timeline = await ref.watch(EventsController.provider(room).future);
// final currentEvents = await future;
// await ref.watch(EventsController.provider(room).notifier).prev();
// final timeline = await ref.watch(EventsController.provider(room).future);
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();
}
Future<void> markRead() async {
if (!room.hasNewMessages) return;
final controller = await future;
final id = controller.messages.last.id;
await room.setReadMarker(id, mRead: id);
// 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();
}
Future<void> updateMessage(Message message, Message newMessage) async =>
@ -121,37 +111,37 @@ class RoomChatController extends AsyncNotifier<ChatController> {
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);
// 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,
);
// await room.sendTextEvent(
// taggedMessage,
// editEventId: relationType == RelationType.edit ? relation?.id : null,
// inReplyTo: (relationType == RelationType.reply && relation != null)
// ? await room.getEventById(relation.id)
// : null,
// );
}
Future<chat.User> resolveUser(String id) async {
final user = await room.client.getUserProfile(id);
// final user = await room.client.getUserProfile(id);
return chat.User(
id: id,
name: user.displayname,
imageSource: user.avatarUrl == null
? null
: (await ref.watch(
AvatarController.provider(user.avatarUrl!.toString()).future,
)).toString(),
// name: user.displayname,
// imageSource: user.avatarUrl == null
// ? null
// : (await ref.watch(
// AvatarController.provider(user.avatarUrl!.toString()).future,
// )).toString(),
);
}

View file

@ -15,35 +15,34 @@ class SpacesController extends Notifier<IList<Space>> {
final topLevelSpaceIds = ref.watch(TopLevelSpacesController.provider);
final spaceEdges = ref.watch(SpaceEdgesController.provider);
ISet<String> collectChildIds(String spaceId) {
ISet<String> result = ISet<String>();
void walk(String currentId) {
final children = spaceEdges[currentId] ?? IList<SpaceEdge>();
for (final edge in children) {
final childId = edge.childId;
if (!result.contains(childId)) {
result = result.add(childId);
walk(childId);
}
}
}
walk(spaceId);
return result;
}
final spaceIdToChildren = IMap.fromEntries(
final childRoomsBySpaceId = IMap.fromEntries(
topLevelSpaceIds.map((spaceId) {
final children = collectChildIds(
ISet<String> walk(String currentId) {
final children = spaceEdges[currentId] ?? IList<SpaceEdge>();
return children.fold<ISet<String>>(const ISet.empty(), (acc, edge) {
final childId = edge.childId;
final isSpace = spaceEdges.containsKey(childId);
return acc
.addAll(!isSpace ? ISet([childId]) : const ISet.empty())
.addAll(isSpace ? walk(childId) : const ISet.empty());
});
}
return MapEntry(
spaceId,
).map((id) => rooms[id]).nonNulls.toIList();
return MapEntry(spaceId, children);
walk(spaceId).map((id) => rooms[id]).nonNulls.toIList(),
);
}),
);
final allNestedRoomIds = spaceIdToChildren.values
final allNestedRoomIds = childRoomsBySpaceId.values
.expand((l) => l)
.map((r) => rooms.entries.firstWhere((e) => e.value == r).key)
.map(
(room) =>
rooms.entries.firstWhere((entry) => entry.value == room).key,
)
.toISet();
final dmRooms = rooms.values
@ -55,7 +54,8 @@ class SpacesController extends Notifier<IList<Space>> {
(e) =>
e.value.metadata?.dmUserId == null &&
!allNestedRoomIds.contains(e.key) &&
!topLevelSpaceIds.contains(e.key),
!topLevelSpaceIds.contains(e.key) &&
!spaceEdges.containsKey(e.key),
)
.map((e) => e.value)
.toIList();
@ -65,7 +65,7 @@ class SpacesController extends Notifier<IList<Space>> {
final room = rooms[id];
if (room == null) return null;
final children = spaceIdToChildren[id] ?? IList<Room>();
final children = childRoomsBySpaceId[id] ?? IList<Room>();
return Space(
id: id,
title: room.metadata?.name ?? "Unnamed Room",