From b54ea4e2b1fc8496b257fab7d595507b8bce049f Mon Sep 17 00:00:00 2001 From: --global Date: Tue, 16 Jul 2024 16:29:09 +0700 Subject: [PATCH] TW-1774: update the chat list item avatar when have new syncUpdate event --- lib/pages/chat_list/chat_list_body_view.dart | 9 +- lib/pages/chat_list/chat_list_item.dart | 14 +-- .../chat_list/chat_list_item_avatar.dart | 104 ++++++++++++++++++ .../chat_list/chat_list_view_builder.dart | 7 ++ 4 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 lib/pages/chat_list/chat_list_item_avatar.dart diff --git a/lib/pages/chat_list/chat_list_body_view.dart b/lib/pages/chat_list/chat_list_body_view.dart index 86c8767d37..ce7cb1aab8 100644 --- a/lib/pages/chat_list/chat_list_body_view.dart +++ b/lib/pages/chat_list/chat_list_body_view.dart @@ -51,8 +51,11 @@ class ChatListBodyView extends StatelessWidget { ), stream: controller.activeClient.onSync.stream .where((s) => s.hasRoomUpdate) - .rateLimit(const Duration(seconds: 1)), - builder: (context, _) { + .rateLimitWithSyncUpdate(const Duration(seconds: 1)), + builder: (context, syncUpdateSnapshot) { + Logs().v( + 'ChatListBodyView: StreamBuilder: snapshot: ${syncUpdateSnapshot.data?.rooms}', + ); if (controller.activeFilter == ActiveFilter.spaces) { return SpaceView( controller, @@ -163,6 +166,7 @@ class ChatListBodyView extends StatelessWidget { child: ChatListViewBuilder( controller: controller, rooms: controller.filteredRoomsForPin, + syncUpdate: syncUpdateSnapshot.data, ), ), if (!controller.filteredRoomsForAllIsEmpty) @@ -192,6 +196,7 @@ class ChatListBodyView extends StatelessWidget { child: ChatListViewBuilder( controller: controller, rooms: controller.filteredRoomsForAll, + syncUpdate: syncUpdateSnapshot.data, ), ), ], diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index e1c78d5e74..85fd7dc701 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -1,13 +1,12 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pages/chat_list/chat_list_item_avatar.dart'; import 'package:fluffychat/presentation/mixins/chat_list_item_mixin.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item_style.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item_subtitle.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item_title.dart'; import 'package:fluffychat/utils/dialog/twake_dialog.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/twake_snackbar.dart'; -import 'package:fluffychat/widgets/avatar/avatar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -27,6 +26,7 @@ class ChatListItem extends StatelessWidget with ChatListItemMixin { final void Function()? onTapAvatar; final void Function(TapDownDetails)? onSecondaryTapDown; final void Function()? onLongPress; + final JoinedRoomUpdate? joinedRoomUpdate; const ChatListItem( this.room, { @@ -39,6 +39,7 @@ class ChatListItem extends StatelessWidget with ChatListItemMixin { this.onSecondaryTapDown, this.onLongPress, super.key, + this.joinedRoomUpdate, }); void clickAction(BuildContext context) async { @@ -87,9 +88,6 @@ class ChatListItem extends StatelessWidget with ChatListItemMixin { @override Widget build(BuildContext context) { - final displayName = room.getLocalizedDisplayname( - MatrixLocals(L10n.of(context)!), - ); return Padding( padding: ChatListItemStyle.paddingConversation, child: Material( @@ -114,10 +112,10 @@ class ChatListItem extends StatelessWidget with ChatListItemMixin { padding: ChatListItemStyle.paddingAvatar, child: Stack( children: [ - Avatar( - mxContent: room.avatar, - name: displayName, + ChatListItemAvatar( + room: room, onTap: onTapAvatar, + joinedRoomUpdate: joinedRoomUpdate, ), if (_isGroupChat) Positioned( diff --git a/lib/pages/chat_list/chat_list_item_avatar.dart b/lib/pages/chat_list/chat_list_item_avatar.dart new file mode 100644 index 0000000000..7e143eb285 --- /dev/null +++ b/lib/pages/chat_list/chat_list_item_avatar.dart @@ -0,0 +1,104 @@ +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/widgets/avatar/avatar.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class ChatListItemAvatar extends StatefulWidget { + final Room room; + final void Function()? onTap; + final JoinedRoomUpdate? joinedRoomUpdate; + + const ChatListItemAvatar({ + required this.room, + this.onTap, + this.joinedRoomUpdate, + super.key, + }); + + @override + State createState() => _ChatListItemAvatarState(); +} + +class _ChatListItemAvatarState extends State { + final ValueNotifier avatarUrlNotifier = ValueNotifier(Uri()); + + @override + void initState() { + avatarUrlNotifier.value = widget.room.avatar ?? Uri(); + super.initState(); + } + + @override + void dispose() { + avatarUrlNotifier.dispose(); + super.dispose(); + } + + @override + void didUpdateWidget(ChatListItemAvatar oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.joinedRoomUpdate != widget.joinedRoomUpdate) { + updateAvatarUrlFromJoinedRoomUpdate(); + } + } + + @override + Widget build(BuildContext context) { + final displayName = widget.room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)!), + ); + return ValueListenableBuilder( + valueListenable: avatarUrlNotifier, + builder: (context, avatarUrl, child) { + return Avatar( + mxContent: avatarUrl, + name: displayName, + onTap: widget.onTap, + ); + }, + ); + } + + void updateAvatarUrlFromJoinedRoomUpdate() { + if (isChatHaveAvatarUpdated) { + if (isGroupChatAvatarUpdated) { + updateGroupAvatar(); + } else if (isDirectChatAvatarUpdated) { + updateDirectChatAvatar(); + } + } + } + + bool get isChatHaveAvatarUpdated => + widget.joinedRoomUpdate?.timeline?.events?.isNotEmpty == true; + + bool get isDirectChatAvatarUpdated { + return widget.room.isDirectChat && + widget.joinedRoomUpdate?.timeline?.events?.last.type == + EventTypes.RoomMember; + } + + bool get isGroupChatAvatarUpdated => + widget.joinedRoomUpdate?.timeline?.events?.last.type == + EventTypes.RoomAvatar; + + void updateDirectChatAvatar() { + final event = widget.joinedRoomUpdate?.timeline?.events?.last; + final avatarMxc = event?.content['avatar_url']; + if (avatarMxc is String && + avatarMxc.isNotEmpty && + event?.senderId == widget.room.directChatMatrixID) { + avatarUrlNotifier.value = Uri.tryParse(avatarMxc); + } + } + + void updateGroupAvatar() { + final avatarMxc = + widget.joinedRoomUpdate?.timeline?.events?.last.content['url']; + + if (avatarMxc is String && avatarMxc.isNotEmpty) { + avatarUrlNotifier.value = Uri.tryParse(avatarMxc); + } + } +} diff --git a/lib/pages/chat_list/chat_list_view_builder.dart b/lib/pages/chat_list/chat_list_view_builder.dart index 214548e645..0b00548291 100644 --- a/lib/pages/chat_list/chat_list_view_builder.dart +++ b/lib/pages/chat_list/chat_list_view_builder.dart @@ -14,11 +14,13 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; class ChatListViewBuilder extends StatelessWidget { final ChatListController controller; final List rooms; + final SyncUpdate? syncUpdate; const ChatListViewBuilder({ super.key, required this.controller, required this.rooms, + this.syncUpdate, }); @override @@ -41,12 +43,14 @@ class ChatListViewBuilder extends StatelessWidget { chatListItem: _CommonChatListItem( controller: controller, room: rooms[index], + joinedRoomUpdate: syncUpdate?.rooms?.join?[rooms[index].id], ), ); } return _CommonChatListItem( controller: controller, room: rooms[index], + joinedRoomUpdate: syncUpdate?.rooms?.join?[rooms[index].id], ); }, ); @@ -155,10 +159,12 @@ class _CommonChatListItem extends StatelessWidget { const _CommonChatListItem({ required this.controller, required this.room, + this.joinedRoomUpdate, }); final ChatListController controller; final Room room; + final JoinedRoomUpdate? joinedRoomUpdate; @override Widget build(BuildContext context) { @@ -195,6 +201,7 @@ class _CommonChatListItem extends StatelessWidget { }, ), activeChat: activeRoomId == room.id, + joinedRoomUpdate: joinedRoomUpdate, ); }, );