fix newlines, wip edits
This commit is contained in:
parent
4424e27276
commit
9663995114
6 changed files with 79 additions and 33 deletions
|
|
@ -8,6 +8,7 @@ import "package:nexus/controllers/events_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/event_to_message.dart";
|
import "package:nexus/helpers/extensions/event_to_message.dart";
|
||||||
import "package:nexus/helpers/extensions/list_to_messages.dart";
|
import "package:nexus/helpers/extensions/list_to_messages.dart";
|
||||||
import "package:fluttertagger/fluttertagger.dart" as tagger;
|
import "package:fluttertagger/fluttertagger.dart" as tagger;
|
||||||
|
import "package:nexus/models/relation_type.dart";
|
||||||
|
|
||||||
class RoomChatController extends AsyncNotifier<ChatController> {
|
class RoomChatController extends AsyncNotifier<ChatController> {
|
||||||
final Room room;
|
final Room room;
|
||||||
|
|
@ -37,7 +38,10 @@ class RoomChatController extends AsyncNotifier<ChatController> {
|
||||||
(element) => element.id == event.relationshipEventId,
|
(element) => element.id == event.relationshipEventId,
|
||||||
);
|
);
|
||||||
if (oldMessage == null || message == null) return;
|
if (oldMessage == null || message == null) return;
|
||||||
return await updateMessage(oldMessage, message);
|
return await updateMessage(
|
||||||
|
oldMessage,
|
||||||
|
message.copyWith(id: oldMessage.id),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
return await insertMessage(message);
|
return await insertMessage(message);
|
||||||
|
|
@ -97,7 +101,8 @@ class RoomChatController extends AsyncNotifier<ChatController> {
|
||||||
Future<void> send(
|
Future<void> send(
|
||||||
String message, {
|
String message, {
|
||||||
required Iterable<tagger.Tag> tags,
|
required Iterable<tagger.Tag> tags,
|
||||||
Message? replyTo,
|
required RelationType relationType,
|
||||||
|
Message? relation,
|
||||||
}) async {
|
}) async {
|
||||||
var taggedMessage = message;
|
var taggedMessage = message;
|
||||||
|
|
||||||
|
|
@ -113,7 +118,10 @@ class RoomChatController extends AsyncNotifier<ChatController> {
|
||||||
|
|
||||||
await room.sendTextEvent(
|
await room.sendTextEvent(
|
||||||
taggedMessage,
|
taggedMessage,
|
||||||
inReplyTo: replyTo == null ? null : await room.getEventById(replyTo.id),
|
editEventId: relationType == RelationType.edit ? relation?.id : null,
|
||||||
|
inReplyTo: (relationType == RelationType.reply && relation != null)
|
||||||
|
? await room.getEventById(relation.id)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,10 @@ extension EventToMessage on Event {
|
||||||
newContent?["formatted_body"] ??
|
newContent?["formatted_body"] ??
|
||||||
newContent?["body"] ??
|
newContent?["body"] ??
|
||||||
event.content["formatted_body"] ??
|
event.content["formatted_body"] ??
|
||||||
event.content["body"],
|
event.content["body"] ??
|
||||||
|
"",
|
||||||
"reply": await replyEvent?.toMessage(mustBeText: true),
|
"reply": await replyEvent?.toMessage(mustBeText: true),
|
||||||
|
"body": newContent?["body"] ?? event.content["body"],
|
||||||
"eventType": event.type,
|
"eventType": event.type,
|
||||||
"avatarUrl": sender.avatarUrl.toString(),
|
"avatarUrl": sender.avatarUrl.toString(),
|
||||||
"displayName": sender.displayName ?? sender.id,
|
"displayName": sender.displayName ?? sender.id,
|
||||||
|
|
@ -69,7 +71,7 @@ extension EventToMessage on Event {
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
EventTypes.Encrypted => asText.copyWith(
|
EventTypes.Encrypted => asText.copyWith(
|
||||||
text: "Unable to decrypt message.",
|
text: "Unable to decrypt message.",
|
||||||
metadata: {"formatted": "Unable to decrypt message.", ...metadata},
|
metadata: {...metadata, "formatted": "Unable to decrypt message."},
|
||||||
),
|
),
|
||||||
(EventTypes.Sticker || EventTypes.Message) => switch (messageType) {
|
(EventTypes.Sticker || EventTypes.Message) => switch (messageType) {
|
||||||
(MessageTypes.Sticker || MessageTypes.Image) => Message.image(
|
(MessageTypes.Sticker || MessageTypes.Image) => Message.image(
|
||||||
|
|
|
||||||
1
lib/models/relation_type.dart
Normal file
1
lib/models/relation_type.dart
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
enum RelationType { edit, reply }
|
||||||
|
|
@ -7,15 +7,18 @@ import "package:fluttertagger/fluttertagger.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:matrix/matrix.dart";
|
import "package:matrix/matrix.dart";
|
||||||
import "package:nexus/controllers/room_chat_controller.dart";
|
import "package:nexus/controllers/room_chat_controller.dart";
|
||||||
|
import "package:nexus/models/relation_type.dart";
|
||||||
import "package:nexus/widgets/chat_page/mention_overlay.dart";
|
import "package:nexus/widgets/chat_page/mention_overlay.dart";
|
||||||
import "package:nexus/widgets/chat_page/reply_preview.dart";
|
import "package:nexus/widgets/chat_page/relation_preview.dart";
|
||||||
|
|
||||||
class ChatBox extends HookConsumerWidget {
|
class ChatBox extends HookConsumerWidget {
|
||||||
final Message? replyToMessage;
|
final Message? relatedMessage;
|
||||||
|
final RelationType relationType;
|
||||||
final VoidCallback onDismiss;
|
final VoidCallback onDismiss;
|
||||||
final Room room;
|
final Room room;
|
||||||
const ChatBox({
|
const ChatBox({
|
||||||
required this.replyToMessage,
|
required this.relatedMessage,
|
||||||
|
required this.relationType,
|
||||||
required this.onDismiss,
|
required this.onDismiss,
|
||||||
required this.room,
|
required this.room,
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -28,14 +31,25 @@ class ChatBox extends HookConsumerWidget {
|
||||||
final triggerCharacter = useState("");
|
final triggerCharacter = useState("");
|
||||||
final query = useState("");
|
final query = useState("");
|
||||||
|
|
||||||
|
if (relationType == RelationType.edit &&
|
||||||
|
relatedMessage is TextMessage &&
|
||||||
|
controller.value.text.isEmpty) {
|
||||||
|
final text = (relatedMessage as TextMessage).text;
|
||||||
|
controller.value.text = relatedMessage?.replyToMessageId == null
|
||||||
|
? text
|
||||||
|
: text.split("\n\n").sublist(1).join("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
void send() {
|
void send() {
|
||||||
ref
|
ref
|
||||||
.watch(RoomChatController.provider(room).notifier)
|
.watch(RoomChatController.provider(room).notifier)
|
||||||
.send(
|
.send(
|
||||||
controller.value.formattedText,
|
controller.value.formattedText,
|
||||||
replyTo: replyToMessage,
|
relation: relatedMessage,
|
||||||
|
relationType: relationType,
|
||||||
tags: controller.value.tags,
|
tags: controller.value.tags,
|
||||||
);
|
);
|
||||||
|
onDismiss();
|
||||||
controller.value.text = "";
|
controller.value.text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,8 +85,9 @@ class ChatBox extends HookConsumerWidget {
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ReplyPreview(
|
RelationPreview(
|
||||||
replyToMessage: replyToMessage,
|
relatedMessage: relatedMessage,
|
||||||
|
relationType: relationType,
|
||||||
onDismiss: onDismiss,
|
onDismiss: onDismiss,
|
||||||
room: room,
|
room: room,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,17 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:matrix/matrix.dart";
|
import "package:matrix/matrix.dart";
|
||||||
import "package:nexus/controllers/avatar_controller.dart";
|
import "package:nexus/controllers/avatar_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/get_headers.dart";
|
import "package:nexus/helpers/extensions/get_headers.dart";
|
||||||
|
import "package:nexus/models/relation_type.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
|
|
||||||
class ReplyPreview extends ConsumerWidget {
|
class RelationPreview extends ConsumerWidget {
|
||||||
final Message? replyToMessage;
|
final Message? relatedMessage;
|
||||||
|
final RelationType relationType;
|
||||||
final VoidCallback onDismiss;
|
final VoidCallback onDismiss;
|
||||||
final Room room;
|
final Room room;
|
||||||
const ReplyPreview({
|
const RelationPreview({
|
||||||
required this.replyToMessage,
|
required this.relatedMessage,
|
||||||
|
required this.relationType,
|
||||||
required this.onDismiss,
|
required this.onDismiss,
|
||||||
required this.room,
|
required this.room,
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -19,9 +22,9 @@ class ReplyPreview extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
if (relatedMessage == null) return SizedBox.shrink();
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
if (replyToMessage == null) return SizedBox.shrink();
|
|
||||||
return Container(
|
return Container(
|
||||||
color: theme.colorScheme.surfaceContainerHigh,
|
color: theme.colorScheme.surfaceContainerHigh,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
|
@ -29,34 +32,40 @@ class ReplyPreview extends ConsumerWidget {
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
|
if (relationType == RelationType.edit)
|
||||||
|
Text(
|
||||||
|
"Editing message:",
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
AvatarOrHash(
|
AvatarOrHash(
|
||||||
ref
|
ref
|
||||||
.watch(
|
.watch(
|
||||||
AvatarController.provider(
|
AvatarController.provider(
|
||||||
replyToMessage!.metadata!["avatarUrl"],
|
relatedMessage!.metadata!["avatarUrl"],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.whenOrNull(data: (data) => data),
|
.whenOrNull(data: (data) => data),
|
||||||
replyToMessage!.metadata!["displayName"].toString(),
|
relatedMessage!.metadata!["displayName"].toString(),
|
||||||
headers: room.client.headers,
|
headers: room.client.headers,
|
||||||
height: 16,
|
height: 16,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
replyToMessage!.metadata?["displayName"] ??
|
relatedMessage!.metadata?["displayName"] ??
|
||||||
replyToMessage!.authorId,
|
relatedMessage!.authorId,
|
||||||
style: theme.textTheme.labelMedium?.copyWith(
|
style: theme.textTheme.labelMedium?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: (replyToMessage is TextMessage)
|
child: Text(
|
||||||
? Text(
|
(relatedMessage is TextMessage)
|
||||||
(replyToMessage as TextMessage).text,
|
? (relatedMessage as TextMessage).text
|
||||||
|
: relatedMessage?.metadata?["body"] ??
|
||||||
|
relatedMessage?.metadata?["eventType"],
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: theme.textTheme.labelMedium,
|
style: theme.textTheme.labelMedium,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
)
|
),
|
||||||
: SizedBox(),
|
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: onDismiss,
|
onPressed: onDismiss,
|
||||||
|
|
@ -13,6 +13,7 @@ import "package:nexus/controllers/room_chat_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/better_when.dart";
|
import "package:nexus/helpers/extensions/better_when.dart";
|
||||||
import "package:nexus/helpers/extensions/get_headers.dart";
|
import "package:nexus/helpers/extensions/get_headers.dart";
|
||||||
import "package:nexus/helpers/extensions/show_context_menu.dart";
|
import "package:nexus/helpers/extensions/show_context_menu.dart";
|
||||||
|
import "package:nexus/models/relation_type.dart";
|
||||||
import "package:nexus/widgets/chat_page/chat_box.dart";
|
import "package:nexus/widgets/chat_page/chat_box.dart";
|
||||||
import "package:nexus/widgets/chat_page/html/html.dart";
|
import "package:nexus/widgets/chat_page/html/html.dart";
|
||||||
import "package:nexus/widgets/chat_page/member_list.dart";
|
import "package:nexus/widgets/chat_page/member_list.dart";
|
||||||
|
|
@ -34,6 +35,7 @@ class RoomChat extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final replyToMessage = useState<Message?>(null);
|
final replyToMessage = useState<Message?>(null);
|
||||||
final memberListOpened = useState<bool>(showMembersByDefault);
|
final memberListOpened = useState<bool>(showMembersByDefault);
|
||||||
|
final relationType = useState(RelationType.reply);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final danger = theme.colorScheme.error;
|
final danger = theme.colorScheme.error;
|
||||||
|
|
||||||
|
|
@ -56,7 +58,10 @@ class RoomChat extends HookConsumerWidget {
|
||||||
|
|
||||||
List<PopupMenuEntry> getMessageOptions(Message message) => [
|
List<PopupMenuEntry> getMessageOptions(Message message) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => replyToMessage.value = message,
|
onTap: () {
|
||||||
|
replyToMessage.value = message;
|
||||||
|
relationType.value = RelationType.reply;
|
||||||
|
},
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Icon(Icons.reply),
|
leading: Icon(Icons.reply),
|
||||||
title: Text("Reply"),
|
title: Text("Reply"),
|
||||||
|
|
@ -64,7 +69,10 @@ class RoomChat extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
if (message.authorId == room.roomData.client.userID)
|
if (message.authorId == room.roomData.client.userID)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () {},
|
onTap: () {
|
||||||
|
replyToMessage.value = message;
|
||||||
|
relationType.value = RelationType.edit;
|
||||||
|
},
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Icon(Icons.edit),
|
leading: Icon(Icons.edit),
|
||||||
title: Text("Edit"),
|
title: Text("Edit"),
|
||||||
|
|
@ -217,7 +225,8 @@ class RoomChat extends HookConsumerWidget {
|
||||||
bottomPadding: 72,
|
bottomPadding: 72,
|
||||||
),
|
),
|
||||||
composerBuilder: (_) => ChatBox(
|
composerBuilder: (_) => ChatBox(
|
||||||
replyToMessage: replyToMessage.value,
|
relationType: relationType.value,
|
||||||
|
relatedMessage: replyToMessage.value,
|
||||||
onDismiss: () =>
|
onDismiss: () =>
|
||||||
replyToMessage.value = null,
|
replyToMessage.value = null,
|
||||||
room: room.roomData,
|
room: room.roomData,
|
||||||
|
|
@ -231,7 +240,8 @@ class RoomChat extends HookConsumerWidget {
|
||||||
MessageGroupStatus? groupStatus,
|
MessageGroupStatus? groupStatus,
|
||||||
}) => FlyerChatTextMessage(
|
}) => FlyerChatTextMessage(
|
||||||
customWidget: Html(
|
customWidget: Html(
|
||||||
message.metadata?["formatted"]
|
(message.metadata?["formatted"]
|
||||||
|
as String)
|
||||||
.replaceAllMapped(
|
.replaceAllMapped(
|
||||||
RegExp(
|
RegExp(
|
||||||
regexLink,
|
regexLink,
|
||||||
|
|
@ -239,7 +249,8 @@ class RoomChat extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
(m) =>
|
(m) =>
|
||||||
"<a href=\"${m.group(0)!}\">${m.group(0)!}</a>",
|
"<a href=\"${m.group(0)!}\">${m.group(0)!}</a>",
|
||||||
) +
|
)
|
||||||
|
.replaceAll("\n", "<br/>") +
|
||||||
((message.editedAt != null)
|
((message.editedAt != null)
|
||||||
? "<sub edited>(edited)</sub>"
|
? "<sub edited>(edited)</sub>"
|
||||||
: ""),
|
: ""),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue