From 541933a9396b2c953a5d6db56cf6f0908141d063 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 7 Dec 2025 10:53:49 -0500 Subject: [PATCH] wip mentions --- lib/controllers/client_controller.dart | 7 +- lib/controllers/room_chat_controller.dart | 11 +- lib/helpers/extensions/event_to_message.dart | 2 +- lib/main.dart | 21 ++- lib/models/full_room.dart | 10 -- lib/models/space.dart | 12 -- lib/widgets/chat_page/chat_box.dart | 137 ++++++++++++------- lib/widgets/chat_page/html/html.dart | 4 +- lib/widgets/chat_page/room_chat.dart | 9 +- pubspec.lock | 16 +++ pubspec.yaml | 2 + 11 files changed, 145 insertions(+), 86 deletions(-) diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index f3db979..476565b 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -10,6 +10,11 @@ import "package:nexus/controllers/secure_storage_controller.dart"; import "package:nexus/models/session_backup.dart"; class ClientController extends AsyncNotifier { + @override + bool updateShouldNotify( + AsyncValue previous, + AsyncValue next, + ) => previous.hasValue != next.hasValue; static const sessionBackupKey = "sessionBackup"; @override @@ -81,7 +86,7 @@ class ClientController extends AsyncNotifier { ).toJson(), ), ); - ref.invalidateSelf(); + ref.invalidateSelf(asReload: true); return true; } catch (_) { return false; diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 37a0800..7733619 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -92,11 +92,18 @@ class RoomChatController extends AsyncNotifier { Future updateMessage(Message message, Message newMessage) async => (await future).updateMessage(message, newMessage); - Future send(String message, {Message? replyTo}) async => + Future send(Message message, {Message? replyTo}) async { + final controller = await future; + controller.insertMessage(message); + + if (message is TextMessage) { await room.sendTextEvent( - message, + message.text, inReplyTo: replyTo == null ? null : await room.getEventById(replyTo.id), ); + } + // TODO: Handle other types of message + } Future resolveUser(String id) async { final user = await room.client.getUserProfile(id); diff --git a/lib/helpers/extensions/event_to_message.dart b/lib/helpers/extensions/event_to_message.dart index 1dc1f92..d782458 100644 --- a/lib/helpers/extensions/event_to_message.dart +++ b/lib/helpers/extensions/event_to_message.dart @@ -97,7 +97,7 @@ extension EventToMessage on Event { id: eventId, authorId: senderId, text: - "${content["displayname"]} ${switch (Membership.values.firstWhereOrNull((membership) => membership.name == content["membership"])) { + "${sender.displayName} ${switch (Membership.values.firstWhereOrNull((membership) => membership.name == content["membership"])) { Membership.invite => "was invited to", Membership.join => "joined", Membership.leave => "left", diff --git a/lib/main.dart b/lib/main.dart index e755359..5d9cde8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import "package:flutter/foundation.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/shared_prefs_controller.dart"; @@ -13,7 +14,23 @@ import "package:window_size/window_size.dart"; final GlobalKey navigatorKey = GlobalKey(); +final class Logger extends ProviderObserver { + @override + void didUpdateProvider( + ProviderObserverContext context, + Object? previousValue, + Object? newValue, + ) { + print('''{ + "provider": "${context.provider}", + "changed": ${previousValue != newValue} + "type": ${previousValue.runtimeType} +}'''); + } +} + void showError(Object error, [StackTrace? stackTrace]) { + if (error.toString().contains("ParentDataWidget")) return; if (error.toString().contains("DioException")) return; if (error.toString().contains("UTF-16")) return; @@ -43,7 +60,9 @@ void main() async { setWindowMinSize(const Size.square(500)); - runApp(ProviderScope(child: const App())); + runApp( + ProviderScope(observers: [if (kDebugMode) Logger()], child: const App()), + ); } class App extends ConsumerWidget { diff --git a/lib/models/full_room.dart b/lib/models/full_room.dart index dd81619..ee61da6 100644 --- a/lib/models/full_room.dart +++ b/lib/models/full_room.dart @@ -10,14 +10,4 @@ abstract class FullRoom with _$FullRoom { required String title, required Uri? avatar, }) = _FullRoom; - - @override - bool operator ==(Object other) => - other.runtimeType == runtimeType && - other is FullRoom && - other.avatar == avatar && - other.title == title; - - @override - int get hashCode => Object.hash(runtimeType, title, avatar); } diff --git a/lib/models/space.dart b/lib/models/space.dart index 0ff21f5..e83d8a2 100644 --- a/lib/models/space.dart +++ b/lib/models/space.dart @@ -17,16 +17,4 @@ abstract class Space with _$Space { Uri? avatar, Icon? icon, }) = _Space; - - @override - bool operator ==(Object other) => - other.runtimeType == runtimeType && - other is Space && - other.title == title && - other.id == id && - other.icon == icon && - other.avatar == avatar; - - @override - int get hashCode => Object.hash(runtimeType, title, id, icon, avatar); } diff --git a/lib/widgets/chat_page/chat_box.dart b/lib/widgets/chat_page/chat_box.dart index 350ce16..2351ca9 100644 --- a/lib/widgets/chat_page/chat_box.dart +++ b/lib/widgets/chat_page/chat_box.dart @@ -1,64 +1,101 @@ import "package:flutter/material.dart"; import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_ui/flutter_chat_ui.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; +import "package:fluttertagger/fluttertagger.dart"; +import "package:matrix/matrix.dart"; +import "package:nexus/helpers/extensions/get_headers.dart"; +import "package:nexus/widgets/form_text_input.dart"; -class ChatBox extends StatelessWidget { +class ChatBox extends HookWidget { final Message? replyToMessage; final VoidCallback onDismiss; - final Map headers; + final Room room; const ChatBox({ required this.replyToMessage, required this.onDismiss, - required this.headers, + required this.room, super.key, }); @override - Widget build(BuildContext context) => Composer( - sigmaX: 0, - sigmaY: 0, - sendIconColor: Theme.of(context).colorScheme.primary, - sendOnEnter: true, - topWidget: replyToMessage == null - ? null - : ColoredBox( - color: Theme.of(context).colorScheme.surfaceContainer, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), - child: Row( - spacing: 8, - children: [ - Avatar( - userId: replyToMessage!.authorId, - headers: headers, - size: 16, - ), - Text( - replyToMessage!.metadata?["displayName"] ?? - replyToMessage!.authorId, - style: Theme.of(context).textTheme.labelMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - Expanded( - child: (replyToMessage is TextMessage) - ? Text( - (replyToMessage as TextMessage).text, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.labelMedium, - maxLines: 1, - ) - : SizedBox(), - ), - IconButton( - onPressed: onDismiss, - icon: Icon(Icons.close), - iconSize: 20, - ), - ], - ), - ), - ), - autofocus: true, - ); + Widget build(BuildContext context) { + final theme = Theme.of(context); + final controller = useRef(FlutterTaggerController()); + final trigger = useState(null); + final style = TextStyle( + color: theme.colorScheme.primary, + fontWeight: FontWeight.bold, + ); + + return Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FlutterTagger( + overlay: SizedBox(), + controller: controller.value, + onSearch: (query, triggerCharacter) { + triggerCharacter == "#"; + if (controller.value.tags.isEmpty) + controller.value.addTag(id: "id", name: "name"); + }, + triggerCharacterAndStyles: {"@": style, "#": style}, + builder: (context, key) => TextFormField(controller: controller.value, key: key,autofocus: true,onFieldSubmitted: (_) { + + },) + // Composer( + // textEditingController: controller.value, + // key: key, + // sigmaY: 0, + // sendIconColor: theme.colorScheme.primary, + // sendOnEnter: true, + // topWidget: replyToMessage == null + // ? null + // : ColoredBox( + // color: theme.colorScheme.surfaceContainer, + // child: Padding( + // padding: EdgeInsets.symmetric( + // horizontal: 16, + // vertical: 4, + // ), + // child: Row( + // spacing: 8, + // children: [ + // Avatar( + // userId: replyToMessage!.authorId, + // headers: room.client.headers, + // size: 16, + // ), + // Text( + // replyToMessage!.metadata?["displayName"] ?? + // replyToMessage!.authorId, + // style: theme.textTheme.labelMedium?.copyWith( + // fontWeight: FontWeight.bold, + // ), + // ), + // Expanded( + // child: (replyToMessage is TextMessage) + // ? Text( + // (replyToMessage as TextMessage).text, + // overflow: TextOverflow.ellipsis, + // style: theme.textTheme.labelMedium, + // maxLines: 1, + // ) + // : SizedBox(), + // ), + // IconButton( + // onPressed: onDismiss, + // icon: Icon(Icons.close), + // iconSize: 20, + // ), + // ], + // ), + // ), + // ), + // autofocus: true, + // ), + ), + ], + ); + } } diff --git a/lib/widgets/chat_page/html/html.dart b/lib/widgets/chat_page/html/html.dart index 03f39a1..0046ac3 100644 --- a/lib/widgets/chat_page/html/html.dart +++ b/lib/widgets/chat_page/html/html.dart @@ -85,7 +85,9 @@ class Html extends ConsumerWidget { headers: client.headers, errorBuilder: (_, error, _) => Text( "Image Failed to Load", - style: TextStyle(color: Colors.red), + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), ), height: height.toDouble(), width: width?.toDouble(), diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index 3a544ab..31455ad 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -177,7 +177,7 @@ class RoomChat extends HookConsumerWidget { composerBuilder: (_) => ChatBox( replyToMessage: replyToMessage.value, onDismiss: () => replyToMessage.value = null, - headers: room.roomData.client.headers, + room: room.roomData, ), textMessageBuilder: ( @@ -296,13 +296,6 @@ class RoomChat extends HookConsumerWidget { ), ), ), - onMessageSend: (message) { - notifier.send( - message, - replyTo: replyToMessage.value, - ); - replyToMessage.value = null; - }, resolveUser: notifier.resolveUser, chatController: controller, ), diff --git a/pubspec.lock b/pubspec.lock index 54d15f6..a0446f0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -575,6 +575,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.17.0" + fluttertagger: + dependency: "direct main" + description: + name: fluttertagger + sha256: "3df0132bdd431a7279da78ea70500ea1e767fa093f43f32785b757c10c6a0fcc" + url: "https://pub.dev" + source: hosted + version: "2.3.1" flyer_chat_file_message: dependency: "direct main" description: @@ -912,6 +920,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + mention_tag_text_field: + dependency: "direct main" + description: + name: mention_tag_text_field + sha256: ba7b9d8003e0f340a65c6dcdb7770f4340f653ae1612a9e31e11d12f7f1dd80f + url: "https://pub.dev" + source: hosted + version: "0.0.9" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0796224..de27de9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,6 +65,8 @@ dependencies: vodozemac: ^0.4.0 clipboard: ^2.0.2 shared_preferences: ^2.5.3 + mention_tag_text_field: ^0.0.9 + fluttertagger: ^2.3.1 dev_dependencies: build_runner: ^2.4.11