From 36c6d6958ad4d17fbc9fdba18444bb8ed7301346 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 30 Jan 2026 09:21:41 +0100 Subject: [PATCH] wip mention overlay --- lib/widgets/chat_page/chat_box.dart | 20 +- lib/widgets/chat_page/mention_overlay.dart | 207 ++++++++++----------- 2 files changed, 112 insertions(+), 115 deletions(-) diff --git a/lib/widgets/chat_page/chat_box.dart b/lib/widgets/chat_page/chat_box.dart index 876f065..885ddd9 100644 --- a/lib/widgets/chat_page/chat_box.dart +++ b/lib/widgets/chat_page/chat_box.dart @@ -8,6 +8,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/models/relation_type.dart"; import "package:nexus/models/room.dart"; +import "package:nexus/widgets/chat_page/mention_overlay.dart"; import "package:nexus/widgets/chat_page/relation_preview.dart"; class ChatBox extends HookConsumerWidget { @@ -108,16 +109,15 @@ class ChatBox extends HookConsumerWidget { Expanded( child: FlutterTagger( triggerStrategy: TriggerStrategy.eager, - overlay: SizedBox.shrink(), - // MentionOverlay( TODO: Fix - // room, - // query: query.value, - // triggerCharacter: triggerCharacter.value, - // addTag: ({required id, required name}) { - // controller.value.addTag(id: id, name: name); - // node.requestFocus(); - // }, - // ), + overlay: MentionOverlay( + room, + query: query.value, + triggerCharacter: triggerCharacter.value, + addTag: ({required id, required name}) { + controller.value.addTag(id: id, name: name); + node.requestFocus(); + }, + ), controller: controller.value, onSearch: (newQuery, newTriggerCharacter) { triggerCharacter.value = newTriggerCharacter; diff --git a/lib/widgets/chat_page/mention_overlay.dart b/lib/widgets/chat_page/mention_overlay.dart index f6c7fe3..116bc54 100644 --- a/lib/widgets/chat_page/mention_overlay.dart +++ b/lib/widgets/chat_page/mention_overlay.dart @@ -1,12 +1,9 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:matrix/matrix.dart"; -import "package:nexus/controllers/avatar_controller.dart"; import "package:nexus/controllers/members_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/helpers/extensions/get_headers.dart"; -import "package:nexus/widgets/avatar_or_hash.dart"; +import "package:nexus/models/room.dart"; import "package:nexus/widgets/loading.dart"; class MentionOverlay extends ConsumerWidget { @@ -23,108 +20,108 @@ class MentionOverlay extends ConsumerWidget { }); @override - Widget build(BuildContext context, WidgetRef ref) => Padding( - padding: EdgeInsets.all(8), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(12)), - child: Container( - color: Theme.of(context).colorScheme.surfaceContainerHigh, - padding: EdgeInsets.all(8), - child: switch (triggerCharacter) { - "@" => - ref - .watch(MembersController.provider(room)) - .betterWhen( - data: (members) => ListView( - children: - (query.isEmpty - ? members - : members.where( - (member) => - member.senderId.toLowerCase().contains( - query.toLowerCase(), - ) || - (member.content["displayname"] - as String?) - ?.toLowerCase() - .contains( - query.toLowerCase(), - ) == - 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, + Widget build(BuildContext context, WidgetRef ref) { + final rooms = ref.watch(RoomsController.provider); + + return Padding( + padding: EdgeInsets.all(8), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(12)), + child: Container( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + padding: EdgeInsets.all(8), + child: switch (triggerCharacter) { + "@" => + ref + .watch(MembersController.provider(room)) + .betterWhen( + data: (members) => ListView( + children: + (query.isEmpty + ? members + : members.where( + (member) => + member.authorId + .toLowerCase() + .contains(query.toLowerCase()) || + (member.content["displayname"] + as String?) + ?.toLowerCase() + .contains( + query.toLowerCase(), + ) == + true, + )) + .map( + (member) => ListTile( + // leading: AvatarOrHash( TODO: Images + // ref + // .watch( + // AvatarController.provider( + // member.content["avatar_url"] + // .toString(), + // ), + // ) + // .whenOrNull(data: (data) => data), + // member.content["displayname"].toString(), + // headers: room.client.headers, + // ), + title: Text( + member.content["displayname"] as String? ?? + member.authorId, + ), + onTap: () => addTag( + id: member.authorId, + name: member.authorId + .substring(1) + .split(":") + .first, + ), ), - title: Text( - member.content["displayname"] as String? ?? - member.senderId, - ), - onTap: () => addTag( - id: member.senderId, - name: member.senderId - .substring(1) - .split(":") - .first, - ), - ), - ) - .toList(), + ) + .toList(), + ), ), - ), - "#" => - ref - .watch(RoomsController.provider) - .betterWhen( - data: (rooms) => ListView( - children: - (query.isEmpty - ? rooms - : rooms.where( - (room) => room.title.toLowerCase().contains( - query.toLowerCase(), - ), - )) - .map( - (room) => ListTile( - leading: AvatarOrHash( - room.avatar, - room.title, - fallback: Icon(Icons.numbers), - headers: room.roomData.client.headers, - ), - title: Text(room.title), - subtitle: room.roomData.topic.isEmpty - ? null - : Text(room.roomData.topic, maxLines: 1), - onTap: () => addTag( - id: "[#${room.roomData.getLocalizedDisplayname()}](https://matrix.to/#/${room.roomData.id})", - name: - (room.roomData.canonicalAlias.isEmpty - ? room.roomData.id - : room.roomData.canonicalAlias) - .substring(1) - .split(":") - .first, - ), - ), - ) - .toList(), - ), - ), - _ => Loading(), - }, + "#" => ListView( + children: + (query.isEmpty + ? rooms.values + : rooms.values.where( + (room) => (room.metadata?.name ?? "Unnamed Room") + .toLowerCase() + .contains(query.toLowerCase()), + )) + .map( + (room) => ListTile( + // leading: AvatarOrHash( TODO: Images + // room.avatar, + // room.title, + // fallback: Icon(Icons.numbers), + // headers: room.roomData.client.headers, + // ), + title: Text(room.metadata?.name ?? "Unnamed Room"), + subtitle: room.metadata?.topic == null + ? null + : Text(room.metadata!.topic!, maxLines: 1), + onTap: () => addTag( + id: "[#${room.metadata?.name ?? "Unnamed Room"}](https://matrix.to/#/${room.metadata?.id})", + name: + (room.metadata?.canonicalAlias ?? + room.metadata?.id) + ?.substring(1) + .split(":") + .first ?? + "", + ), + ), + ) + .toList(), + ), + + _ => Loading(), + }, + ), ), - ), - ); + ); + } }