Working images

This commit is contained in:
Henry Hiles 2026-01-30 16:50:25 +01:00
commit 2372ecd141
No known key found for this signature in database
20 changed files with 388 additions and 375 deletions

View file

@ -1,161 +0,0 @@
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart";
import "package:nexus/controllers/profile_controller.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/requests/get_event_request.dart";
import "package:nexus/models/requests/get_related_events_request.dart";
extension EventToMessage on Event {
Future<Message?> toMessage(
Ref ref, {
bool mustBeText = false,
bool includeEdits = false,
}) async {
if (relationType == "m.replace" && !includeEdits) return null;
final client = ref.watch(ClientController.provider.notifier);
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 client.getEvent(
GetEventRequest(roomId: roomId, eventId: replyId),
);
final author = await ref.watch(
ProfileController.provider(event.authorId).future,
);
final content = (decrypted ?? this.content);
final type = (decryptedType ?? this.type);
final newContent = content["m.new_content"] as Map?;
final metadata = {
"timelineId": event.timelineRowId,
"formatted":
newContent?["formatted_body"] ??
newContent?["body"] ??
content["formatted_body"] ??
content["body"] ??
"",
"reply": await replyEvent?.toMessage(ref, mustBeText: true),
"body": newContent?["body"] ?? content["body"],
"eventType": type,
"avatarUrl": author.avatarUrl,
"displayName": author.displayName ?? authorId,
"txnId": transactionId,
};
final editedAt = event.relationType == "m.replace" ? event.timestamp : null;
if ((event.redactedBy != null && !mustBeText) ||
(!includeEdits && (relationType == "m.replace"))) {
return null;
}
// TODO: Use server-generated preview if enabled
// final match = Uri.tryParse(
// RegExp(regexLink, caseSensitive: false).firstMatch(body)?.group(0) ?? "",
// );
final asText =
Message.text(
metadata: metadata,
id: eventId,
authorId: authorId,
text: redactedBy == null
? content["body"] ?? ""
: "This message has been deleted...",
replyToMessageId: replyId,
deliveredAt: timestamp,
editedAt: editedAt,
)
as TextMessage;
if (mustBeText) return asText;
return switch (type) {
"m.room.encrypted" => asText.copyWith(
text: "Unable to decrypt message.",
metadata: {...metadata, "formatted": "Unable to decrypt message."},
),
// "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(
id: eventId,
metadata: metadata,
authorId: authorId,
text: event.localContent?.sanitizedHtml,
source: "(await getAttachmentUri()).toString()", // TODO
replyToMessageId: replyId,
deliveredAt: timestamp,
blurhash: (content["info"] as Map?)?["xyz.amorgan.blurhash"],
),
"m.audio" => Message.audio(
id: eventId,
metadata: metadata,
authorId: authorId,
text: content["body"],
replyToMessageId: replyId,
source: "(await event.getAttachmentUri()).toString()", // TODO
deliveredAt: timestamp,
// TODO: See if we can figure out duration
duration: Duration(hours: 1),
),
"m.file" => Message.file(
name: content["filename"].toString(),
metadata: metadata,
id: eventId,
authorId: authorId,
source: "(await event.getAttachmentUri()).toString()", // TODO
replyToMessageId: replyId,
deliveredAt: timestamp,
),
_ => asText,
},
"m.room.member" => Message.system(
metadata: metadata,
id: eventId,
authorId: authorId,
deliveredAt: timestamp,
text:
"${content["displayname"] ?? event.stateKey} ${switch (content["membership"]) {
"invite" => "was invited to",
"join" => "joined",
"leave" => "left",
"knock" => "asked to join",
"ban" => "was banned from",
_ => "did something relating to",
}} the room.",
),
"m.room.redaction" => null,
_ =>
// Turn this on for debugging purposes
false
// ignore: dead_code
? Message.unsupported(
metadata: metadata,
id: eventId,
authorId: authorId,
replyToMessageId: replyId,
)
: null,
};
}
}

View file

@ -1,10 +0,0 @@
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/helpers/extensions/event_to_message.dart";
import "package:nexus/models/event.dart";
extension ListToMessages on Iterable<Event> {
Future<List<Message>> toMessages(Ref ref) async => (await Future.wait(
map((event) => event.toMessage(ref)),
)).nonNulls.toList();
}

View file

@ -0,0 +1,4 @@
extension MxcToHttps on Uri {
Uri mxcToHttps(String homeserver) =>
Uri.parse("${homeserver}_matrix/client/v1/media/download/$host$path");
}