forked from Henry-Hiles/nexus
working message rendering
This commit is contained in:
parent
5f96c8e57f
commit
e59632bb07
20 changed files with 305 additions and 197 deletions
|
|
@ -1,23 +1,36 @@
|
|||
import "package:collection/collection.dart";
|
||||
import "dart:developer";
|
||||
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:matrix/matrix.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";
|
||||
|
||||
extension EventToMessage on Event {
|
||||
Future<Message?> toMessage(
|
||||
Timeline timeline, {
|
||||
ClientController client, {
|
||||
bool mustBeText = false,
|
||||
bool includeEdits = false,
|
||||
}) async {
|
||||
final replyId = inReplyToEventId();
|
||||
final newEvent = (unsigned?["m.relations"] as Map?)?["m.replace"];
|
||||
final event = newEvent == null ? this : Event.fromJson(newEvent, room);
|
||||
if (relationType == "m.replace" && !includeEdits) return null;
|
||||
|
||||
final newEvents = await client.getRelatedEvents(
|
||||
GetRelatedEventsRequest(
|
||||
roomId: roomId,
|
||||
eventId: eventId,
|
||||
relationType: "m.replace",
|
||||
),
|
||||
);
|
||||
final event = newEvents?.lastOrNull ?? this;
|
||||
|
||||
final replyId = this.content["m.relates_to"]?["m.in_reply_to"]?["event_id"];
|
||||
final replyEvent = replyId == null
|
||||
? null
|
||||
: await room.getEventById(replyId);
|
||||
: await client.getEvent(
|
||||
GetEventRequest(roomId: roomId, eventId: replyId),
|
||||
);
|
||||
|
||||
final sender =
|
||||
await event.fetchSenderUser() ?? event.senderFromMemoryOrFallback;
|
||||
final author = await client.getProfile(event.authorId);
|
||||
final newContent = event.content["m.new_content"] as Map?;
|
||||
final metadata = {
|
||||
"formatted":
|
||||
|
|
@ -26,109 +39,108 @@ extension EventToMessage on Event {
|
|||
event.content["formatted_body"] ??
|
||||
event.content["body"] ??
|
||||
"",
|
||||
"reply": await replyEvent?.toMessage(mustBeText: true, timeline),
|
||||
"reply": await replyEvent?.toMessage(client, mustBeText: true),
|
||||
"body": newContent?["body"] ?? event.content["body"],
|
||||
"eventType": event.type,
|
||||
"avatarUrl": sender.avatarUrl.toString(),
|
||||
"displayName": sender.displayName ?? sender.id,
|
||||
"avatarUrl": author?.avatarUrl,
|
||||
"displayName": author?.displayName ?? authorId,
|
||||
"txnId": transactionId,
|
||||
};
|
||||
|
||||
final editedAt = event.relationshipType == RelationshipTypes.edit
|
||||
? event.originServerTs
|
||||
: null;
|
||||
final editedAt = event.relationType == "m.replace" ? event.timestamp : null;
|
||||
|
||||
if ((redacted && !mustBeText) ||
|
||||
(!includeEdits && (relationshipType == RelationshipTypes.edit))) {
|
||||
if ((event.redactedBy != null && !mustBeText) ||
|
||||
(!includeEdits && (relationType == "m.replace"))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Use server-generated preview if enabled when https://github.com/famedly/matrix-dart-sdk/issues/2195 is fixed.
|
||||
// TODO: Use server-generated preview if enabled
|
||||
|
||||
// final match = Uri.tryParse(
|
||||
// RegExp(regexLink, caseSensitive: false).firstMatch(body)?.group(0) ?? "",
|
||||
// );
|
||||
|
||||
// final preview = match == null
|
||||
// ? null
|
||||
// : await room.client.getUrlPreview(match);
|
||||
|
||||
final asText =
|
||||
Message.text(
|
||||
metadata: metadata,
|
||||
id: eventId,
|
||||
authorId: senderId,
|
||||
text: redacted ? "This message has been deleted..." : event.body,
|
||||
authorId: authorId,
|
||||
text: redactedBy == null
|
||||
? event.content["body"] ?? ""
|
||||
: "This message has been deleted...",
|
||||
replyToMessageId: replyId,
|
||||
deliveredAt: originServerTs,
|
||||
deliveredAt: timestamp,
|
||||
editedAt: editedAt,
|
||||
)
|
||||
as TextMessage;
|
||||
|
||||
final content = (decrypted ?? this.content);
|
||||
|
||||
if (mustBeText) return asText;
|
||||
return switch (type) {
|
||||
EventTypes.Encrypted => asText.copyWith(
|
||||
"m.room.encrypted" => asText.copyWith(
|
||||
text: "Unable to decrypt message.",
|
||||
metadata: {...metadata, "formatted": "Unable to decrypt message."},
|
||||
),
|
||||
PollEventContent.startType => Message.custom(
|
||||
metadata: {
|
||||
...metadata,
|
||||
"poll": event.parsedPollEventContent.pollStartContent,
|
||||
"responses": event.getPollResponses(timeline),
|
||||
},
|
||||
id: eventId,
|
||||
deliveredAt: originServerTs,
|
||||
authorId: senderId,
|
||||
),
|
||||
(EventTypes.Sticker || EventTypes.Message) => switch (messageType) {
|
||||
(MessageTypes.Sticker || MessageTypes.Image) => Message.image(
|
||||
// "org.matrix.msc3381.poll.start" => Message.custom(
|
||||
// metadata: {
|
||||
// ...metadata,
|
||||
// "poll": event.parsedPollEventContent.pollStartContent,
|
||||
// "responses": event.getPollResponses(timeline),
|
||||
// },
|
||||
// id: eventId,
|
||||
// deliveredAt: originServerTs,
|
||||
// authorId: senderId,
|
||||
// ),
|
||||
("m.sticker" || "m.room.message") => switch (content["msgtype"]) {
|
||||
("m.sticker" || "m.image") => Message.image(
|
||||
metadata: metadata,
|
||||
id: eventId,
|
||||
authorId: senderId,
|
||||
text: event.text,
|
||||
source: (await getAttachmentUri()).toString(),
|
||||
authorId: authorId,
|
||||
text: event.localContent?.sanitizedHtml,
|
||||
source: "(await getAttachmentUri()).toString()", // TODO
|
||||
replyToMessageId: replyId,
|
||||
deliveredAt: originServerTs,
|
||||
deliveredAt: timestamp,
|
||||
blurhash: (event.content["info"] as Map?)?["xyz.amorgan.blurhash"],
|
||||
),
|
||||
MessageTypes.Audio => Message.audio(
|
||||
"m.audio" => Message.audio(
|
||||
metadata: metadata,
|
||||
id: eventId,
|
||||
authorId: senderId,
|
||||
text: event.text,
|
||||
authorId: authorId,
|
||||
text: event.content["body"],
|
||||
replyToMessageId: replyId,
|
||||
source: (await event.getAttachmentUri()).toString(),
|
||||
deliveredAt: originServerTs,
|
||||
source: "(await event.getAttachmentUri()).toString()", // TODO
|
||||
deliveredAt: timestamp,
|
||||
// TODO: See if we can figure out duration
|
||||
duration: Duration(hours: 1),
|
||||
),
|
||||
MessageTypes.File => Message.file(
|
||||
"m.file" => Message.file(
|
||||
name: event.content["filename"].toString(),
|
||||
metadata: metadata,
|
||||
id: eventId,
|
||||
authorId: senderId,
|
||||
source: (await event.getAttachmentUri()).toString(),
|
||||
authorId: authorId,
|
||||
source: "(await event.getAttachmentUri()).toString()", // TODO
|
||||
replyToMessageId: replyId,
|
||||
deliveredAt: originServerTs,
|
||||
deliveredAt: timestamp,
|
||||
),
|
||||
_ => asText,
|
||||
},
|
||||
EventTypes.RoomMember => Message.system(
|
||||
"m.room.member" => Message.system(
|
||||
metadata: metadata,
|
||||
id: eventId,
|
||||
authorId: senderId,
|
||||
authorId: authorId,
|
||||
deliveredAt: timestamp,
|
||||
text:
|
||||
"${event.asUser.displayName ?? event.asUser.id} ${switch (Membership.values.firstWhereOrNull((membership) => membership.name == event.content["membership"])) {
|
||||
Membership.invite => "was invited to",
|
||||
Membership.join => "joined",
|
||||
Membership.leave => "left",
|
||||
Membership.knock => "asked to join",
|
||||
Membership.ban => "was banned from",
|
||||
"${content["displayname"] ?? event.stateKey} ${switch (event.content["membership"]) {
|
||||
"invite" => "was invited to",
|
||||
"join" => "joined",
|
||||
"leave" => "left",
|
||||
"knock" => "asked to join",
|
||||
"ban" => "was banned from",
|
||||
_ => "did something relating to",
|
||||
}} the room.",
|
||||
),
|
||||
EventTypes.Redaction => null,
|
||||
"m.room.redaction" => null,
|
||||
_ =>
|
||||
// Turn this on for debugging purposes
|
||||
false
|
||||
|
|
@ -136,7 +148,7 @@ extension EventToMessage on Event {
|
|||
? Message.unsupported(
|
||||
metadata: metadata,
|
||||
id: eventId,
|
||||
authorId: senderId,
|
||||
authorId: authorId,
|
||||
replyToMessageId: replyId,
|
||||
)
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import "dart:typed_data";
|
|||
import "package:ffi/ffi.dart";
|
||||
import "package:nexus/src/third_party/gomuks.g.dart";
|
||||
|
||||
extension GomuksOwnedBufferToJson on GomuksOwnedBuffer {
|
||||
extension GomuksOwnedBufferToX on GomuksOwnedBuffer {
|
||||
Uint8List toBytes() {
|
||||
try {
|
||||
if (base == nullptr || length <= 0) return Uint8List(0);
|
||||
|
|
@ -14,14 +14,7 @@ extension GomuksOwnedBufferToJson on GomuksOwnedBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final bytes = toBytes();
|
||||
if (bytes.isEmpty) return {};
|
||||
final json = jsonDecode(utf8.decode(bytes));
|
||||
|
||||
if (json is Map<String, dynamic>?) return json ?? {};
|
||||
throw json;
|
||||
}
|
||||
dynamic toJson() => jsonDecode(utf8.decode(toBytes()));
|
||||
}
|
||||
|
||||
extension JsonToGomuksBuffer on Map<String, dynamic> {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:matrix/matrix.dart";
|
||||
import "package:nexus/controllers/client_controller.dart";
|
||||
import "package:nexus/helpers/extensions/event_to_message.dart";
|
||||
import "package:nexus/models/event.dart";
|
||||
|
||||
extension ListToMessages on List<MatrixEvent> {
|
||||
Future<List<Message>> toMessages(Room room, Timeline timeline) async =>
|
||||
extension ListToMessages on IList<Event> {
|
||||
Future<List<Message>> toMessages(ClientController client) async =>
|
||||
(await Future.wait(
|
||||
map((event) => Event.fromMatrixEvent(event, room).toMessage(timeline)),
|
||||
)).nonNulls.toList().reversed.toList();
|
||||
map((event) => event.toMessage(client)),
|
||||
)).nonNulls.toList();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue