From 6215537f8e8686e6ba57b6f37d72fe3b9da6ac5b Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 26 Dec 2025 15:04:49 -0500 Subject: [PATCH] Fix sending permission thingy --- lib/controllers/room_chat_controller.dart | 25 ++- lib/widgets/chat_page/chat_box.dart | 260 +++++++++++----------- 2 files changed, 148 insertions(+), 137 deletions(-) diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 3dbe0a8..4c7be45 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -7,6 +7,7 @@ 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; class RoomChatController extends AsyncNotifier { final Room room; @@ -93,11 +94,27 @@ class RoomChatController extends AsyncNotifier { Future updateMessage(Message message, Message newMessage) async => (await future).updateMessage(message, newMessage); - Future send(String message, {Message? replyTo}) async => - room.sendTextEvent( - message, - inReplyTo: replyTo == null ? null : await room.getEventById(replyTo.id), + Future send( + String message, { + required Iterable tags, + Message? replyTo, + }) async { + var taggedMessage = message; + + for (final tag in tags) { + final escaped = RegExp.escape(tag.id.substring(1)); + final pattern = RegExp(r"@@(" + escaped + r")#[^#]*#"); + taggedMessage = taggedMessage.replaceAllMapped( + pattern, + (m) => "@${m.group(1)}", ); + } + + await room.sendTextEvent( + taggedMessage, + inReplyTo: replyTo == null ? null : await room.getEventById(replyTo.id), + ); + } Future resolveUser(String id) async { final user = await room.client.getUserProfile(id); diff --git a/lib/widgets/chat_page/chat_box.dart b/lib/widgets/chat_page/chat_box.dart index e38e5f5..bb7c7cf 100644 --- a/lib/widgets/chat_page/chat_box.dart +++ b/lib/widgets/chat_page/chat_box.dart @@ -33,19 +33,25 @@ class ChatBox extends HookConsumerWidget { final triggerCharacter = useState(""); final query = useState(""); - Future send() => ref - .watch(RoomChatController.provider(room).notifier) - .send(controller.value.text, replyTo: replyToMessage); + void send() { + ref + .watch(RoomChatController.provider(room).notifier) + .send( + controller.value.formattedText, + replyTo: replyToMessage, + tags: controller.value.tags, + ); + controller.value.text = ""; + } final node = useFocusNode( onKeyEvent: (_, event) { - if (event is! KeyDownEvent || Platform.isAndroid || Platform.isIOS) { + if (event is KeyDownEvent && !Platform.isAndroid && !Platform.isIOS) { if (event.logicalKey == LogicalKeyboardKey.enter && !HardwareKeyboard.instance.isShiftPressed) { send(); return KeyEventResult.handled; - } - if (event.logicalKey == LogicalKeyboardKey.escape) { + } else if (event.logicalKey == LogicalKeyboardKey.escape) { onDismiss(); return KeyEventResult.handled; } @@ -78,139 +84,127 @@ class ChatBox extends HookConsumerWidget { Container( color: theme.colorScheme.surfaceContainerHighest, padding: EdgeInsets.symmetric(horizontal: 8), - child: room.canSendDefaultMessages - ? Row( - spacing: 8, - children: [ - PopupMenuButton( - itemBuilder: (context) => [], - icon: Icon(Icons.add), - ), - Expanded( - child: FlutterTagger( - triggerStrategy: TriggerStrategy.eager, - overlay: Padding( - padding: EdgeInsets.all(8), - child: ClipRRect( - borderRadius: BorderRadius.all( - Radius.circular(12), - ), - child: Container( - color: - theme.colorScheme.surfaceContainerHigh, - padding: EdgeInsets.all(8), - child: switch (triggerCharacter.value) { - "@" => - ref - .watch( - MembersController.provider(room), - ) - .betterWhen( - data: (members) => ListView( - children: - (query.value.isEmpty - ? members - : members.where( - (member) => - member - .senderId - .contains( - query - .value, - ) || - (member.content["displayname"] - as String?) - ?.contains( - query.value, - ) == - true, - )) - .map( - (member) => ListTile( - leading: AvatarOrHash( - ref - .watch( - AvatarController.provider( - member - .content["avatar_url"] - .toString(), - ), - ) - .whenOrNull( - data: - ( - data, - ) => - data, - ), - member - .content["displayname"] - .toString(), - headers: room - .client - .headers, + child: Row( + spacing: 8, + children: [ + PopupMenuButton( + itemBuilder: (context) => [], + icon: Icon(Icons.add), + enabled: room.canSendDefaultMessages, + ), + Expanded( + child: FlutterTagger( + triggerStrategy: TriggerStrategy.eager, + overlay: Padding( + padding: EdgeInsets.all(8), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(12)), + child: Container( + color: theme.colorScheme.surfaceContainerHigh, + padding: EdgeInsets.all(8), + child: switch (triggerCharacter.value) { + "@" => + ref + .watch(MembersController.provider(room)) + .betterWhen( + data: (members) => ListView( + children: + (query.value.isEmpty + ? members + : members.where( + (member) => + member.senderId + .contains( + query.value, + ) || + (member.content["displayname"] + as String?) + ?.contains( + query + .value, + ) == + true, + )) + .map( + (member) => ListTile( + leading: AvatarOrHash( + ref + .watch( + AvatarController.provider( + member + .content["avatar_url"] + .toString(), + ), + ) + .whenOrNull( + data: (data) => + data, ), - title: Text( - member.content["displayname"] - as String? ?? - member - .senderId, - ), - onTap: () => controller - .value - .addTag( - id: "member", - name: member - .senderId - .substring( - 1, - ) - .split( - ":", - ) - .first, - ), + member + .content["displayname"] + .toString(), + headers: + room.client.headers, + ), + title: Text( + member.content["displayname"] + as String? ?? + member.senderId, + ), + onTap: () => controller + .value + .addTag( + id: member.senderId, + name: member + .senderId + .substring(1) + .split(":") + .first, ), - ) - .toList(), - ), - ), - "#" => Text("Todo"), - _ => Loading(), - }, - ), - ), - ), - controller: controller.value, - onSearch: (newQuery, newTriggerCharacter) { - triggerCharacter.value = newTriggerCharacter; - query.value = newQuery; + ), + ) + .toList(), + ), + ), + "#" => Text("Todo"), + _ => Loading(), }, - triggerCharacterAndStyles: { - "@": style, - "#": style, - ":": style, - }, - builder: (context, key) => TextFormField( - maxLines: 12, - minLines: 1, - decoration: InputDecoration( - hintText: "Your message here...", - border: InputBorder.none, - ), - controller: controller.value, - key: key, - autofocus: true, - focusNode: node, - ), ), ), - IconButton(onPressed: send, icon: Icon(Icons.send)), - ], - ) - : Text( - "You don't have permission to send messages here...", + ), + controller: controller.value, + onSearch: (newQuery, newTriggerCharacter) { + triggerCharacter.value = newTriggerCharacter; + query.value = newQuery; + }, + triggerCharacterAndStyles: { + "@": style, + "#": style, + ":": style, + }, + builder: (context, key) => TextFormField( + enabled: room.canSendDefaultMessages, + maxLines: 12, + minLines: 1, + decoration: InputDecoration( + hintText: room.canSendDefaultMessages + ? "Your message here..." + : "You don't have permission to send messages in this room...", + border: InputBorder.none, + ), + controller: controller.value, + key: key, + autofocus: true, + focusNode: node, + ), ), + ), + IconButton( + onPressed: room.canSendDefaultMessages ? send : null, + icon: Icon(Icons.send), + ), + ], + ), ), ], ),