improve reply handling

This commit is contained in:
Henry Hiles 2026-03-10 19:22:41 -04:00
commit 0769ab4dbb
No known key found for this signature in database
4 changed files with 83 additions and 49 deletions

View file

@ -47,25 +47,21 @@ class MessageController extends AsyncNotifier<Message?> {
final type = (config.event.decryptedType ?? config.event.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"] ??
"",
"body": config.event.redactedBy == null
? (newContent?["body"] ?? content["body"] ?? "")
: "Deleted Message",
if (replyEvent != null)
"reply": await ref.watch(
MessageController.provider(
MessageConfig(
event: replyEvent,
room: config.room,
mustBeText: true,
alwaysReturn: true,
),
).future,
),
"timelineId": event.timelineRowId,
"big": event.localContent?.bigEmoji == true,
"body": newContent?["body"] ?? content["body"],
"eventType": type,
"avatarUrl": author?.content["avatar_url"],
"editSource":
@ -82,7 +78,7 @@ class MessageController extends AsyncNotifier<Message?> {
final editedAt = event.relationType == "m.replace" ? event.timestamp : null;
if ((event.redactedBy != null && !config.mustBeText) ||
if ((event.redactedBy != null && !config.alwaysReturn) ||
(!config.includeEdits && (config.event.relationType == "m.replace"))) {
return null;
}
@ -98,17 +94,18 @@ class MessageController extends AsyncNotifier<Message?> {
metadata: metadata,
id: config.event.eventId,
authorId: event.authorId,
text: config.event.redactedBy == null
? content["body"] ?? ""
: "This message has been deleted...",
text:
newContent?["formatted_body"] ??
newContent?["body"] ??
content["formatted_body"] ??
content["body"] ??
"",
replyToMessageId: replyId,
deliveredAt: config.event.timestamp,
editedAt: editedAt,
)
as TextMessage;
if (config.mustBeText) return asText;
final homeserver = ref.read(ClientStateController.provider)?.homeserverUrl;
final source = homeserver == null || content["url"] == null
? "null"
@ -117,7 +114,7 @@ class MessageController extends AsyncNotifier<Message?> {
return switch (type) {
"m.room.encrypted" => asText.copyWith(
text: "Unable to decrypt message.",
metadata: {...metadata, "formatted": "Unable to decrypt message."},
metadata: {...metadata, "body": "Unable to decrypt message."},
),
// "org.matrix.msc3381.poll.start" => Message.custom(
// metadata: {
@ -134,7 +131,11 @@ class MessageController extends AsyncNotifier<Message?> {
id: config.event.eventId,
metadata: metadata,
authorId: event.authorId,
text: event.localContent?.sanitizedHtml,
text:
newContent?["formatted_body"] ??
newContent?["body"] ??
content["formatted_body"] ??
content["body"],
source: source,
replyToMessageId: replyId,
deliveredAt: config.event.timestamp,
@ -156,7 +157,18 @@ class MessageController extends AsyncNotifier<Message?> {
content["membership"] == event.unsigned["prev_content"]?["membership"]
? null
: Message.system(
metadata: metadata,
metadata: {
...metadata,
"body":
"${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.",
},
id: config.event.eventId,
authorId: event.authorId,
deliveredAt: config.event.timestamp,
@ -170,18 +182,30 @@ class MessageController extends AsyncNotifier<Message?> {
_ => "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: config.event.eventId,
authorId: event.authorId,
replyToMessageId: replyId,
"m.room.redaction" =>
config.alwaysReturn
? asText.copyWith(
metadata: {
...(asText.metadata ?? {}),
"body": "Deleted Message",
},
)
: null,
_ =>
config.alwaysReturn
? asText
: (
// Turn this on for debugging purposes
false
// ignore: dead_code
? Message.unsupported(
metadata: metadata,
id: config.event.eventId,
authorId: event.authorId,
replyToMessageId: replyId,
)
: null),
};
}

View file

@ -7,7 +7,7 @@ part "message_config.g.dart";
@freezed
abstract class MessageConfig with _$MessageConfig {
const factory MessageConfig({
@Default(false) bool mustBeText,
@Default(false) bool alwaysReturn,
@Default(false) bool includeEdits,
required Room room,
required Event event,

View file

@ -395,8 +395,7 @@ class RoomChat extends HookConsumerWidget {
message.metadata?["big"] == true
? TextStyle(fontSize: 32)
: null,
(message.metadata?["formatted"]
as String)
message.text
.replaceAllMapped(
RegExp(
"(<a\\b[^>]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)",
@ -466,20 +465,29 @@ class RoomChat extends HookConsumerWidget {
: CrossAxisAlignment.start,
children: [
SizedBox(height: 12),
FlyerChatTextMessage(
topWidget: TopWidget(
message,
groupStatus: groupStatus,
alwaysShow: true,
if (message.text?.isNotEmpty == true)
FlyerChatTextMessage(
topWidget: TopWidget(
message,
groupStatus: groupStatus,
alwaysShow: true,
),
message: TextMessage(
id: "${message.id}-text",
authorId: message.authorId,
text: message.text!,
),
index: index,
),
message: TextMessage(
id: "${message.id}-text",
authorId: message.authorId,
text: message.metadata?["formatted"],
),
index: index,
),
FlyerChatImageMessage(
topWidget:
message.text?.isNotEmpty == true
? null
: TopWidget(
message,
groupStatus: groupStatus,
alwaysShow: true,
),
customImageProvider: CachedNetworkImage(
message.source,
ref.watch(

View file

@ -22,11 +22,13 @@ class TopWidget extends ConsumerWidget {
children: [
Builder(
builder: (_) {
final replyMessage = message.metadata?["reply"] as TextMessage?;
final replyMessage = message.metadata?["reply"] as Message?;
if (replyMessage == null) return SizedBox.shrink();
final smallerText = message is TextMessage
? replyMessage.text.substring(
final smallerText =
message is TextMessage && replyMessage.metadata!["body"] != null
? replyMessage.metadata!["body"].substring(
0,
min(
max(
@ -39,14 +41,14 @@ class TopWidget extends ConsumerWidget {
),
5,
),
replyMessage.text.length,
replyMessage.metadata!["body"].length,
),
)
: null;
final replyText =
(smallerText == null ||
smallerText.length == replyMessage.text.length)
? replyMessage.text
smallerText.length == replyMessage.metadata!["body"].length)
? replyMessage.metadata!["body"]
: "$smallerText...";
return Padding(