From 63a9d2d1698d893a5c8c1d4dc8fd4a7e69860b93 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Sun, 7 Dec 2025 16:31:03 -0500 Subject: [PATCH] progress --- lib/controllers/events_controller.dart | 6 +- lib/controllers/message_controller.dart | 8 +- lib/controllers/room_chat_controller.dart | 37 +- .../selected_space_controller.dart | 4 +- lib/controllers/spaces_controller.dart | 4 +- lib/controllers/thumbnail_controller.dart | 4 +- lib/helpers/extensions/event_to_message.dart | 2 +- lib/main.dart | 14 +- lib/models/space.dart | 2 +- lib/pages/login_page.dart | 6 +- lib/widgets/chat_page/chat_box.dart | 206 +++++++---- lib/widgets/chat_page/html/spoiler_text.dart | 27 +- lib/widgets/chat_page/room_chat.dart | 340 +++++++++--------- lib/widgets/chat_page/sidebar.dart | 8 +- lib/widgets/chat_page/top_widget.dart | 19 +- 15 files changed, 388 insertions(+), 299 deletions(-) diff --git a/lib/controllers/events_controller.dart b/lib/controllers/events_controller.dart index e3f0a9c..008ff2a 100644 --- a/lib/controllers/events_controller.dart +++ b/lib/controllers/events_controller.dart @@ -18,10 +18,8 @@ class EventsController extends AsyncNotifier { return response; } - Future prev() async { - final resp = await build(from: ref.read(FromController.provider(room))); - return resp; - } + Future prev() async => + build(from: ref.read(FromController.provider(room))); static final provider = AsyncNotifierProvider.autoDispose .family( diff --git a/lib/controllers/message_controller.dart b/lib/controllers/message_controller.dart index f1c253f..69d3e37 100644 --- a/lib/controllers/message_controller.dart +++ b/lib/controllers/message_controller.dart @@ -9,17 +9,15 @@ class MessageController extends AsyncNotifier { @override Future build() async { - final room = await ref.watch( - SelectedRoomController.provider.selectAsync((a) => a), - ); + final room = await ref.watch(SelectedRoomController.provider.future); if (room == null) return null; final event = await room.roomData.getEventById(id); return (await event?.toMessage(mustBeText: true)) as TextMessage?; } - static final provider = - AsyncNotifierProvider.family( + static final provider = AsyncNotifierProvider.family + .autoDispose( MessageController.new, ); } diff --git a/lib/controllers/room_chat_controller.dart b/lib/controllers/room_chat_controller.dart index 7733619..49c3cfa 100644 --- a/lib/controllers/room_chat_controller.dart +++ b/lib/controllers/room_chat_controller.dart @@ -3,6 +3,7 @@ import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart" as chat; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:matrix/matrix.dart"; +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"; @@ -13,7 +14,9 @@ class RoomChatController extends AsyncNotifier { @override Future build() async { - final response = await ref.watch(EventsController.provider(room).future); + final response = await ref.watch( + EventsController.provider(room).selectAsync((a) => a), + ); ref.onDispose( room.client.onTimelineEvent.stream.listen((event) async { @@ -75,10 +78,10 @@ class RoomChatController extends AsyncNotifier { .watch(EventsController.provider(room).notifier) .prev(); - await controller.insertAllMessages( - await response.chunk.toMessages(room), - index: 0, - ); + final messages = await response.chunk.toMessages(room); + + await controller.insertAllMessages(messages, index: 0); + ref.notifyListeners(); } Future markRead() async { @@ -92,30 +95,22 @@ class RoomChatController extends AsyncNotifier { Future updateMessage(Message message, Message newMessage) async => (await future).updateMessage(message, newMessage); - Future send(Message message, {Message? replyTo}) async { - final controller = await future; - controller.insertMessage(message); - - if (message is TextMessage) { - await room.sendTextEvent( - message.text, + Future send(String message, {Message? replyTo}) async => + room.sendTextEvent( + message, 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); return chat.User( id: id, name: user.displayname, - imageSource: (await user.avatarUrl?.getThumbnailUri( - // TODO: Fix use of account avatar not room avatar - room.client, - width: 24, - height: 24, - ))?.toString(), + imageSource: user.avatarUrl == null + ? null + : (await ref.watch( + AvatarController.provider(user.avatarUrl!.toString()).future, + )).toString(), ); } diff --git a/lib/controllers/selected_space_controller.dart b/lib/controllers/selected_space_controller.dart index cbefeb5..75bf287 100644 --- a/lib/controllers/selected_space_controller.dart +++ b/lib/controllers/selected_space_controller.dart @@ -7,7 +7,9 @@ import "package:nexus/models/space.dart"; class SelectedSpaceController extends AsyncNotifier { @override Future build() async { - final spaces = await ref.watch(SpacesController.provider.future); + final spaces = await ref.watch( + SpacesController.provider.selectAsync((data) => data), + ); final selectedSpaceId = ref.watch( KeyController.provider(KeyController.spaceKey), ); diff --git a/lib/controllers/spaces_controller.dart b/lib/controllers/spaces_controller.dart index 7d2ecf0..3501de6 100644 --- a/lib/controllers/spaces_controller.dart +++ b/lib/controllers/spaces_controller.dart @@ -41,7 +41,7 @@ class SpacesController extends AsyncNotifier> { title: "Home", id: "home", children: topLevelRooms, - icon: Icon(Icons.home), + icon: Icons.home, ), Space( client: client, @@ -54,7 +54,7 @@ class SpacesController extends AsyncNotifier> { .map((room) => room.fullRoom), ), ), - icon: Icon(Icons.person), + icon: Icons.person, ), ...(await Future.wait( topLevelSpaces.map( diff --git a/lib/controllers/thumbnail_controller.dart b/lib/controllers/thumbnail_controller.dart index e1a6468..4500523 100644 --- a/lib/controllers/thumbnail_controller.dart +++ b/lib/controllers/thumbnail_controller.dart @@ -10,9 +10,7 @@ class ThumbnailController extends AsyncNotifier { @override Future build({String? from}) async { final client = await ref.watch(ClientController.provider.future); - final uri = await Uri.tryParse(data.uri)?.getDownloadUri( - client, - ); // TODO: Should use thumb when c10y fixes animated thumbs + final uri = await Uri.tryParse(data.uri)?.getDownloadUri(client); return uri.toString(); } diff --git a/lib/helpers/extensions/event_to_message.dart b/lib/helpers/extensions/event_to_message.dart index d782458..a4c58d6 100644 --- a/lib/helpers/extensions/event_to_message.dart +++ b/lib/helpers/extensions/event_to_message.dart @@ -25,7 +25,7 @@ extension EventToMessage on Event { final editedAt = relationshipType == RelationshipTypes.edit ? originServerTs : null; - final body = newContent?["body"] as String? ?? this.body; + final body = (newContent?["body"] ?? content["body"]).toString(); final eventId = editedAt == null ? this.eventId : relationshipEventId ?? this.eventId; diff --git a/lib/main.dart b/lib/main.dart index 5d9cde8..7ae44ba 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,17 +20,15 @@ final class Logger extends ProviderObserver { ProviderObserverContext context, Object? previousValue, Object? newValue, - ) { - print('''{ - "provider": "${context.provider}", - "changed": ${previousValue != newValue} - "type": ${previousValue.runtimeType} -}'''); - } + ) => debugPrint(""" +Time: ${DateTime.now().toIso8601String()} +Provider: ${context.provider} +Previous Value: ${previousValue is AsyncData ? previousValue.value : previousValue} +New Value: ${newValue is AsyncData ? newValue.value : newValue} +}"""); } 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; diff --git a/lib/models/space.dart b/lib/models/space.dart index e83d8a2..d64dcc9 100644 --- a/lib/models/space.dart +++ b/lib/models/space.dart @@ -15,6 +15,6 @@ abstract class Space with _$Space { required Client client, Room? roomData, Uri? avatar, - Icon? icon, + IconData? icon, }) = _Space; } diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index c31c601..9716989 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -132,7 +132,11 @@ class LoginPage extends HookConsumerWidget { (homeserver) => Card( child: ListTile( title: Text(homeserver.name), - leading: Image.network(homeserver.iconUrl, height: 32), + leading: Image.network( + homeserver.iconUrl, + errorBuilder: (_, _, _) => SizedBox.shrink(), + height: 32, + ), subtitle: Text(homeserver.description), onTap: isLoading.value ? null diff --git a/lib/widgets/chat_page/chat_box.dart b/lib/widgets/chat_page/chat_box.dart index 2351ca9..f2a3a43 100644 --- a/lib/widgets/chat_page/chat_box.dart +++ b/lib/widgets/chat_page/chat_box.dart @@ -1,13 +1,14 @@ +import "dart:io"; import "package:flutter/material.dart"; +import "package:flutter/services.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:hooks_riverpod/hooks_riverpod.dart"; import "package:matrix/matrix.dart"; -import "package:nexus/helpers/extensions/get_headers.dart"; -import "package:nexus/widgets/form_text_input.dart"; +import "package:nexus/controllers/room_chat_controller.dart"; -class ChatBox extends HookWidget { +class ChatBox extends HookConsumerWidget { final Message? replyToMessage; final VoidCallback onDismiss; final Room room; @@ -19,83 +20,142 @@ class ChatBox extends HookWidget { }); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final controller = useRef(FlutterTaggerController()); - final trigger = useState(null); + + Future send() => ref + .watch(RoomChatController.provider(room).notifier) + .send(controller.value.text); + + final node = useFocusNode( + onKeyEvent: (_, event) { + if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.enter && + !(Platform.isAndroid || Platform.isIOS) ^ + HardwareKeyboard.instance.isShiftPressed) { + send(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + }, + ); + 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, - // ), + return Positioned( + bottom: 0, + left: 0, + right: 0, + child: Padding( + padding: EdgeInsetsGeometry.all(12), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(12)), + child: Container( + color: theme.colorScheme.surfaceContainerHighest, + padding: EdgeInsets.symmetric(horizontal: 8), + child: // TODO: This doesn't work? + room.canSendDefaultMessages + ? Row( + spacing: 8, + children: [ + PopupMenuButton( + itemBuilder: (context) => [], + icon: Icon(Icons.add), + ), + Expanded( + child: FlutterTagger( + overlay: SizedBox(), + controller: controller.value, + onSearch: (query, triggerCharacter) { + triggerCharacter == "#"; + if (controller.value.tags.isEmpty) { + controller.value.addTag( + id: "id", + name: "name", + ); // TODO: RM + } + }, + 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..."), + // 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/spoiler_text.dart b/lib/widgets/chat_page/html/spoiler_text.dart index 9a42bff..87e1439 100644 --- a/lib/widgets/chat_page/html/spoiler_text.dart +++ b/lib/widgets/chat_page/html/spoiler_text.dart @@ -1,5 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; +import "package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart"; class SpoilerText extends HookWidget { final String text; @@ -10,18 +11,20 @@ class SpoilerText extends HookWidget { Widget build(BuildContext context) { final revealed = useState(false); - return InkWell( - onTap: () => revealed.value = !revealed.value, - child: AnimatedContainer( - duration: const Duration(milliseconds: 100), - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - decoration: BoxDecoration( - color: revealed.value ? Colors.transparent : Colors.blueGrey, - borderRadius: BorderRadius.circular(4), - ), - child: Text( - text, - style: TextStyle(color: revealed.value ? null : Colors.transparent), + return InlineCustomWidget( + child: InkWell( + onTap: () => revealed.value = !revealed.value, + child: AnimatedContainer( + duration: const Duration(milliseconds: 100), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + decoration: BoxDecoration( + color: revealed.value ? Colors.transparent : Colors.blueGrey, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + text, + style: TextStyle(color: revealed.value ? null : Colors.transparent), + ), ), ), ); diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index 31455ad..2d7fdc5 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -135,177 +135,197 @@ class RoomChat extends HookConsumerWidget { body: Row( children: [ Expanded( - child: ref - .watch(controllerProvider) - .betterWhen( - data: (controller) => Chat( - currentUserId: room.roomData.client.userID!, - theme: ChatTheme.fromThemeData(theme).copyWith( - colors: ChatColors.fromThemeData(theme).copyWith( - primary: theme.colorScheme.primaryContainer, - onPrimary: theme.colorScheme.onPrimaryContainer, - ), - ), - onMessageSecondaryTap: - ( - context, - message, { - required details, - required index, - }) => context.showContextMenu( - globalPosition: details.globalPosition, - children: getMessageOptions(message), - ), - onMessageLongPress: - ( - context, - message, { - required details, - required index, - }) => context.showContextMenu( - globalPosition: details.globalPosition, - children: getMessageOptions(message), - ), - builders: Builders( - loadMoreBuilder: (_) => Loading(), - chatAnimatedListBuilder: (_, itemBuilder) => - ChatAnimatedList( - itemBuilder: itemBuilder, - onEndReached: notifier.loadOlder, - onStartReached: notifier.markRead, - ), - composerBuilder: (_) => ChatBox( - replyToMessage: replyToMessage.value, - onDismiss: () => replyToMessage.value = null, - room: room.roomData, - ), - textMessageBuilder: - ( - context, - message, - index, { - required bool isSentByMe, - MessageGroupStatus? groupStatus, - }) => FlyerChatTextMessage( - customWidget: Html( - message.metadata?["formatted"] - .replaceAllMapped( - RegExp( - regexLink, - caseSensitive: false, - ), - (m) => - "${m.group(0)!}", - ) + - ((message.editedAt != null) - ? "(edited)" - : ""), - client: room.roomData.client, - ), - topWidget: TopWidget( - message, - headers: room.roomData.client.headers, - groupStatus: groupStatus, - ), - message: message, - showTime: true, - index: index, - ), - linkPreviewBuilder: (_, message, isSentByMe) => - LinkPreview( - text: message.text, - backgroundColor: isSentByMe - ? theme.colorScheme.inversePrimary - : theme.colorScheme.surfaceContainerLow, - insidePadding: EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - linkPreviewData: message.linkPreviewData, - onLinkPreviewDataFetched: - (linkPreviewData) => - notifier.updateMessage( - message, - message.copyWith( - linkPreviewData: - linkPreviewData, - ), + child: Column( + children: [ + Expanded( + child: ref + .watch(controllerProvider) + .betterWhen( + data: (controller) => Chat( + currentUserId: room.roomData.client.userID!, + theme: ChatTheme.fromThemeData(theme) + .copyWith( + colors: ChatColors.fromThemeData(theme) + .copyWith( + primary: theme + .colorScheme + .primaryContainer, + onPrimary: theme + .colorScheme + .onPrimaryContainer, ), - ), - imageMessageBuilder: - ( - _, - message, - index, { - required bool isSentByMe, - MessageGroupStatus? groupStatus, - }) => FlyerChatImageMessage( - topWidget: TopWidget( - message, - headers: room.roomData.client.headers, - groupStatus: groupStatus, - alwaysShow: true, - ), - message: message, - index: index, - headers: room.roomData.client.headers, - ), - fileMessageBuilder: - ( - _, - message, - index, { - required bool isSentByMe, - MessageGroupStatus? groupStatus, - }) => InkWell( - onTap: () => showAboutDialog( - context: context, - ), // TODO: Download - child: FlyerChatFileMessage( - topWidget: TopWidget( - message, - headers: room.roomData.client.headers, - groupStatus: groupStatus, ), - message: message, - index: index, + onMessageSecondaryTap: + ( + context, + message, { + required details, + required index, + }) => context.showContextMenu( + globalPosition: details.globalPosition, + children: getMessageOptions(message), + ), + onMessageLongPress: + ( + context, + message, { + required details, + required index, + }) => context.showContextMenu( + globalPosition: details.globalPosition, + children: getMessageOptions(message), + ), + builders: Builders( + loadMoreBuilder: (_) => Loading(), + chatAnimatedListBuilder: (_, itemBuilder) => + ChatAnimatedList( + itemBuilder: itemBuilder, + onEndReached: notifier.loadOlder, + onStartReached: notifier.markRead, + bottomPadding: 72, + ), + composerBuilder: (_) => ChatBox( + replyToMessage: replyToMessage.value, + onDismiss: () => + replyToMessage.value = null, + room: room.roomData, ), + textMessageBuilder: + ( + context, + message, + index, { + required bool isSentByMe, + MessageGroupStatus? groupStatus, + }) => FlyerChatTextMessage( + customWidget: Html( + message.metadata?["formatted"] + .replaceAllMapped( + RegExp( + regexLink, + caseSensitive: false, + ), + (m) => + "${m.group(0)!}", + ) + + ((message.editedAt != null) + ? "(edited)" + : ""), + client: room.roomData.client, + ), + topWidget: TopWidget( + message, + headers: + room.roomData.client.headers, + groupStatus: groupStatus, + ), + message: message, + showTime: true, + index: index, + ), + linkPreviewBuilder: + (_, message, isSentByMe) => LinkPreview( + text: message.text, + backgroundColor: isSentByMe + ? theme.colorScheme.inversePrimary + : theme + .colorScheme + .surfaceContainerLow, + insidePadding: EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + linkPreviewData: + message.linkPreviewData, + onLinkPreviewDataFetched: + (linkPreviewData) => + notifier.updateMessage( + message, + message.copyWith( + linkPreviewData: + linkPreviewData, + ), + ), + ), + imageMessageBuilder: + ( + _, + message, + index, { + required bool isSentByMe, + MessageGroupStatus? groupStatus, + }) => FlyerChatImageMessage( + topWidget: TopWidget( + message, + headers: + room.roomData.client.headers, + groupStatus: groupStatus, + alwaysShow: true, + ), + message: message, + index: index, + headers: room.roomData.client.headers, + ), + fileMessageBuilder: + ( + _, + message, + index, { + required bool isSentByMe, + MessageGroupStatus? groupStatus, + }) => InkWell( + onTap: () => showAboutDialog( + context: context, + ), // TODO: Download + child: FlyerChatFileMessage( + topWidget: TopWidget( + message, + headers: + room.roomData.client.headers, + groupStatus: groupStatus, + ), + message: message, + index: index, + ), + ), + systemMessageBuilder: + ( + _, + message, + index, { + required bool isSentByMe, + MessageGroupStatus? groupStatus, + }) => FlyerChatSystemMessage( + message: message, + index: index, + ), + unsupportedMessageBuilder: + ( + _, + message, + index, { + required bool isSentByMe, + MessageGroupStatus? groupStatus, + }) => Text( + "${message.authorId} sent ${message.metadata?["eventType"]}", + style: theme.textTheme.labelSmall + ?.copyWith(color: Colors.grey), + ), ), - systemMessageBuilder: - ( - _, - message, - index, { - required bool isSentByMe, - MessageGroupStatus? groupStatus, - }) => FlyerChatSystemMessage( - message: message, - index: index, - ), - unsupportedMessageBuilder: - ( - _, - message, - index, { - required bool isSentByMe, - MessageGroupStatus? groupStatus, - }) => Text( - "${message.authorId} sent ${message.metadata?["eventType"]}", - style: theme.textTheme.labelSmall?.copyWith( - color: Colors.grey, - ), - ), - ), - resolveUser: notifier.resolveUser, - chatController: controller, - ), + resolveUser: notifier.resolveUser, + chatController: controller, + ), + ), ), + ], + ), ), if (memberListOpened.value == true && showMembersByDefault) MemberList(room.roomData), ], ), + endDrawer: showMembersByDefault ? null : MemberList(room.roomData), diff --git a/lib/widgets/chat_page/sidebar.dart b/lib/widgets/chat_page/sidebar.dart index 29ef4bc..d4bd89d 100644 --- a/lib/widgets/chat_page/sidebar.dart +++ b/lib/widgets/chat_page/sidebar.dart @@ -60,7 +60,9 @@ class Sidebar extends HookConsumerWidget { (space) => NavigationRailDestination( icon: AvatarOrHash( space.avatar, - fallback: space.icon, + fallback: space.icon == null + ? null + : Icon(space.icon), space.title, headers: space.client.headers, hasBadge: @@ -126,7 +128,9 @@ class Sidebar extends HookConsumerWidget { appBar: AppBar( leading: AvatarOrHash( space.avatar, - fallback: space.icon, + fallback: space.icon == null + ? null + : Icon(space.icon), space.title, headers: space.client.headers, ), diff --git a/lib/widgets/chat_page/top_widget.dart b/lib/widgets/chat_page/top_widget.dart index 95f613c..e208c1e 100644 --- a/lib/widgets/chat_page/top_widget.dart +++ b/lib/widgets/chat_page/top_widget.dart @@ -3,8 +3,10 @@ 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_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/avatar_controller.dart"; import "package:nexus/controllers/message_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; +import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/chat_page/quoted.dart"; class TopWidget extends ConsumerWidget { @@ -62,11 +64,18 @@ class TopWidget extends ConsumerWidget { mainAxisSize: MainAxisSize.min, spacing: 8, children: [ - Avatar( - userId: replyMessage.authorId, - headers: headers, - size: 16, - ), + ref + .watch( + AvatarController.provider(replyMessage.authorId), + ) + .betterWhen( + data: (avatar) => AvatarOrHash( + avatar, + replyMessage.metadata?["displayName"] ?? + replyMessage.authorId, + headers: headers, + ), + ), Flexible( child: Text( replyMessage.metadata?["displayName"] ??