From d6d6875882e6d0caeec684f3f1a3990788d9dd25 Mon Sep 17 00:00:00 2001 From: Kelrap <99418823+Kelrap@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:27:11 -0500 Subject: [PATCH] Bot animations (#1262) * onPopinvok * Randomly reset bot animation * Use user ID to identify bot * Keeps timer from acting on nonexistent widget * fix: remove setState call in bot face SVG build function --------- Co-authored-by: ggurdin --- lib/pages/chat/chat_app_bar_title.dart | 11 +-- lib/pages/chat/chat_input_row.dart | 6 ++ .../events/room_creation_state_event.dart | 11 +-- lib/pages/chat/seen_by_row.dart | 3 + lib/pages/chat/typing_indicators.dart | 11 ++- lib/pages/chat_details/chat_details_view.dart | 3 + lib/pages/chat_list/chat_list.dart | 6 ++ lib/pages/chat_list/chat_list_item.dart | 3 + lib/pages/chat_list/chat_list_view.dart | 3 + .../chat_list/client_chooser_button.dart | 1 + lib/pages/chat_list/space_view.dart | 13 ++++ lib/pages/chat_list/status_msg_list.dart | 9 ++- .../chat_search/chat_search_message_tab.dart | 13 ++-- lib/pages/settings/settings_view.dart | 3 + .../settings_ignore_list_view.dart | 3 + .../user_bottom_sheet_view.dart | 3 + .../chat_details/pangea_chat_details.dart | 3 + lib/pangea/widgets/common/bot_face_svg.dart | 78 ++++++++++++------- lib/widgets/avatar.dart | 44 ++++++----- lib/widgets/profile_bottom_sheet.dart | 3 + 20 files changed, 160 insertions(+), 70 deletions(-) diff --git a/lib/pages/chat/chat_app_bar_title.dart b/lib/pages/chat/chat_app_bar_title.dart index 09ead63ce..86ed0205d 100644 --- a/lib/pages/chat/chat_app_bar_title.dart +++ b/lib/pages/chat/chat_app_bar_title.dart @@ -1,14 +1,12 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:go_router/go_router.dart'; - import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/presence_builder.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; class ChatAppBarTitle extends StatelessWidget { final ChatController controller; @@ -38,6 +36,9 @@ class ChatAppBarTitle extends StatelessWidget { name: room.getLocalizedDisplayname( MatrixLocals(L10n.of(context)), ), + // #Pangea + presenceUserId: room.directChatMatrixID, + // Pangea# size: 32, ), ), diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index f679b2f9a..f08e15d38 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -458,6 +458,9 @@ class _ChatAccountPicker extends StatelessWidget { mxContent: snapshot.data?.avatarUrl, name: snapshot.data?.displayName ?? client.userID!.localpart, + // #Pangea + presenceUserId: client.userID!, + // Pangea# size: 20, ), title: Text(snapshot.data?.displayName ?? client.userID!), @@ -471,6 +474,9 @@ class _ChatAccountPicker extends StatelessWidget { mxContent: snapshot.data?.avatarUrl, name: snapshot.data?.displayName ?? Matrix.of(context).client.userID!.localpart, + // #Pangea + presenceUserId: Matrix.of(context).client.userID!, + // Pangea# size: 20, ), ), diff --git a/lib/pages/chat/events/room_creation_state_event.dart b/lib/pages/chat/events/room_creation_state_event.dart index 398afc59f..4e8c9471c 100644 --- a/lib/pages/chat/events/room_creation_state_event.dart +++ b/lib/pages/chat/events/room_creation_state_event.dart @@ -1,12 +1,10 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; class RoomCreationStateEvent extends StatelessWidget { final Event event; @@ -32,6 +30,9 @@ class RoomCreationStateEvent extends StatelessWidget { Avatar( mxContent: event.room.avatar, name: roomName, + // #Pangea + presenceUserId: event.room.directChatMatrixID, + // Pangea# size: Avatar.defaultSize * 2, ), Text( diff --git a/lib/pages/chat/seen_by_row.dart b/lib/pages/chat/seen_by_row.dart index 793a90746..7093886ba 100644 --- a/lib/pages/chat/seen_by_row.dart +++ b/lib/pages/chat/seen_by_row.dart @@ -42,6 +42,9 @@ class SeenByRow extends StatelessWidget { (user) => Avatar( mxContent: user.avatarUrl, name: user.calcDisplayname(), + // #Pangea + presenceUserId: user.id, + // Pangea# size: 16, ), ), diff --git a/lib/pages/chat/typing_indicators.dart b/lib/pages/chat/typing_indicators.dart index 7710ee0b6..24fcfb3bc 100644 --- a/lib/pages/chat/typing_indicators.dart +++ b/lib/pages/chat/typing_indicators.dart @@ -1,12 +1,11 @@ import 'dart:async'; -import 'package:flutter/material.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; class TypingIndicators extends StatelessWidget { final ChatController controller; @@ -61,6 +60,9 @@ class TypingIndicators extends StatelessWidget { Avatar( size: avatarSize, mxContent: typingUsers.first.avatarUrl, + // #Pangea + presenceUserId: typingUsers.first.id, + // Pangea# name: typingUsers.first.calcDisplayname(), ), if (typingUsers.length == 2) @@ -71,6 +73,11 @@ class TypingIndicators extends StatelessWidget { mxContent: typingUsers.length == 2 ? typingUsers.last.avatarUrl : null, + // #Pangea + presenceUserId: typingUsers.length == 2 + ? typingUsers.last.id + : null, + // Pangea# name: typingUsers.length == 2 ? typingUsers.last.calcDisplayname() : '+${typingUsers.length - 1}', diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 069284454..92099828a 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -95,6 +95,9 @@ class ChatDetailsView extends StatelessWidget { child: Avatar( mxContent: room.avatar, name: displayname, + // #Pangea + presenceUserId: room.directChatMatrixID, + // Pangea# size: Avatar.defaultSize * 2.5, ), ), diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 905fba451..0b42d9785 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -732,6 +732,9 @@ class ChatListController extends State mxContent: room.avatar, size: Avatar.defaultSize / 2, name: displayname, + // #Pangea + presenceUserId: room.directChatMatrixID, + // Pangea# ), const SizedBox(width: 12), Text( @@ -753,6 +756,9 @@ class ChatListController extends State mxContent: space.avatar, size: Avatar.defaultSize / 2, name: space.getLocalizedDisplayname(), + // #Pangea + presenceUserId: space.directChatMatrixID, + // Pangea# ), const SizedBox(width: 12), Expanded( diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 0fe44e6de..bcd711df5 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -150,6 +150,9 @@ class ChatListItem extends StatelessWidget { mxContent: space.avatar, size: Avatar.defaultSize * 0.75, name: space.getLocalizedDisplayname(), + // #Pangea + presenceUserId: space.directChatMatrixID, + // Pangea# onTap: () => onLongPress?.call(context), ), ), diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index b7dd91956..ccf0f8088 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -112,6 +112,9 @@ class ChatListView extends StatelessWidget { icon: Avatar( mxContent: rootSpaces[i].avatar, name: displayname, + // #Pangea + presenceUserId: space.directChatMatrixID, + // Pangea# size: 32, borderRadius: BorderRadius.circular( AppConfig.borderRadius / 4, diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index b979928f0..2a9023c55 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -231,6 +231,7 @@ class ClientChooserButton extends StatelessWidget { name: snapshot.data?.displayName ?? matrix.client.userID!.localpart, // #Pangea + presenceUserId: matrix.client.userID!, // size: 32, size: 60, // Pangea# diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 64464cf35..bba64df76 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -546,6 +546,9 @@ class _SpaceViewState extends State { leading: Avatar( mxContent: room?.avatar, name: displayname, + // #Pangea + presenceUserId: room?.directChatMatrixID, + // Pangea# borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), ), title: Text( @@ -713,6 +716,10 @@ class _SpaceViewState extends State { Avatar( mxContent: joinedParents[i].avatar, name: displayname, + // #Pangea + presenceUserId: + joinedParents[i].directChatMatrixID, + // Pangea# size: Avatar.defaultSize / 2, borderRadius: BorderRadius.circular( AppConfig.borderRadius / 4, @@ -809,6 +816,12 @@ class _SpaceViewState extends State { leading: Avatar( mxContent: item.avatarUrl, name: displayname, + // #Pangea + presenceUserId: Matrix.of(context) + .client + .getRoomById(item.roomId) + ?.directChatMatrixID, + // Pangea# borderRadius: item.roomType == 'm.space' ? BorderRadius.circular( AppConfig.borderRadius / 2, diff --git a/lib/pages/chat_list/status_msg_list.dart b/lib/pages/chat_list/status_msg_list.dart index 2d31537cc..38bde4d32 100644 --- a/lib/pages/chat_list/status_msg_list.dart +++ b/lib/pages/chat_list/status_msg_list.dart @@ -1,7 +1,3 @@ -import 'package:flutter/material.dart'; - -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; @@ -10,6 +6,8 @@ import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; class StatusMessageList extends StatelessWidget { final void Function() onStatusEdit; @@ -155,6 +153,9 @@ class PresenceAvatar extends StatelessWidget { ), child: Avatar( name: displayName, + // #Pangea + presenceUserId: profile?.userId, + // Pangea# mxContent: profile?.avatarUrl, size: avatarSize - 6, ), diff --git a/lib/pages/chat_search/chat_search_message_tab.dart b/lib/pages/chat_search/chat_search_message_tab.dart index 7c7024e8d..53cb45b8a 100644 --- a/lib/pages/chat_search/chat_search_message_tab.dart +++ b/lib/pages/chat_search/chat_search_message_tab.dart @@ -1,15 +1,13 @@ +import 'package:fluffychat/utils/date_time_extension.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/utils/url_launcher.dart'; +import 'package:fluffychat/widgets/avatar.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/utils/date_time_extension.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; -import 'package:fluffychat/utils/url_launcher.dart'; -import 'package:fluffychat/widgets/avatar.dart'; - class ChatSearchMessageTab extends StatelessWidget { final String searchQuery; final Room room; @@ -137,6 +135,9 @@ class _MessageSearchResultListTile extends StatelessWidget { Avatar( mxContent: sender.avatarUrl, name: displayname, + // #Pangea + presenceUserId: sender.id, + // Pangea# size: 16, ), const SizedBox(width: 8), diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 1575f9ded..515845cc4 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -58,6 +58,9 @@ class SettingsView extends StatelessWidget { Avatar( mxContent: profile?.avatarUrl, name: displayname, + // #Pangea + presenceUserId: profile?.userId, + // Pangea# size: Avatar.defaultSize * 2.5, ), if (profile != null) diff --git a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart index 93117b1d2..f9a5dfe72 100644 --- a/lib/pages/settings_ignore_list/settings_ignore_list_view.dart +++ b/lib/pages/settings_ignore_list/settings_ignore_list_view.dart @@ -81,6 +81,9 @@ class SettingsIgnoreListView extends StatelessWidget { leading: Avatar( mxContent: s.data?.avatarUrl ?? Uri.parse(''), name: s.data?.displayName ?? client.ignoredUsers[i], + // #Pangea + presenceUserId: s.data?.userId, + // Pangea# ), title: Text( s.data?.displayName ?? client.ignoredUsers[i], diff --git a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart index c4d869ac4..1c10b4767 100644 --- a/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart +++ b/lib/pages/user_bottom_sheet/user_bottom_sheet_view.dart @@ -118,6 +118,9 @@ class UserBottomSheetView extends StatelessWidget { Matrix.of(controller.widget.outerContext).client, mxContent: avatarUrl, name: displayname, + // #Pangea + presenceUserId: user?.id, + // Pangea# size: Avatar.defaultSize * 2.5, ), ), diff --git a/lib/pangea/pages/chat_details/pangea_chat_details.dart b/lib/pangea/pages/chat_details/pangea_chat_details.dart index eb1fa258e..94e1ba26f 100644 --- a/lib/pangea/pages/chat_details/pangea_chat_details.dart +++ b/lib/pangea/pages/chat_details/pangea_chat_details.dart @@ -121,6 +121,9 @@ class PangeaChatDetailsView extends StatelessWidget { child: Avatar( mxContent: room.avatar, name: displayname, + // #Pangea + presenceUserId: room.directChatMatrixID, + // Pangea# size: Avatar.defaultSize * 2.5, ), ), diff --git a/lib/pangea/widgets/common/bot_face_svg.dart b/lib/pangea/widgets/common/bot_face_svg.dart index f387b4bc3..13eac0298 100644 --- a/lib/pangea/widgets/common/bot_face_svg.dart +++ b/lib/pangea/widgets/common/bot_face_svg.dart @@ -1,3 +1,6 @@ +import 'dart:async'; +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:rive/rive.dart'; @@ -22,11 +25,41 @@ class BotFace extends StatefulWidget { class BotFaceState extends State { Artboard? _artboard; StateMachineController? _controller; + final Random _random = Random(); @override void initState() { super.initState(); - _loadRiveFile(); + _loadRiveFile().then((_) => _scheduleNextRun()); + } + + @override + void didUpdateWidget(BotFace oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.expression != widget.expression) { + _controller!.setInputValue( + _controller!.stateMachine.inputs[0].id, + mapExpressionToInput(widget.expression), + ); + } + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + + void _scheduleNextRun() { + final int nextInterval = + _random.nextInt(21) + 20; // Random interval between 20-40 seconds + + Future.delayed(Duration(seconds: nextInterval), () { + if (mounted) { + _loadRiveFile(); + _scheduleNextRun(); + } + }); } double mapExpressionToInput(BotExpression expression) { @@ -45,11 +78,12 @@ class BotFaceState extends State { } Future _loadRiveFile() async { - final riveFile = await RiveFile.asset('assets/pangea/bot_faces/pangea_bot.riv'); + final riveFile = + await RiveFile.asset('assets/pangea/bot_faces/pangea_bot.riv'); final artboard = riveFile.mainArtboard; - _controller = StateMachineController - .fromArtboard(artboard, 'BotIconStateMachine'); + _controller = + StateMachineController.fromArtboard(artboard, 'BotIconStateMachine'); if (_controller != null) { artboard.addController(_controller!); @@ -59,40 +93,24 @@ class BotFaceState extends State { ); } - setState(() { - _artboard = artboard; - }); + if (mounted) { + setState(() { + _artboard = artboard; + }); + } } @override Widget build(BuildContext context) { - return SizedBox( width: widget.width, height: widget.width, child: _artboard != null - ? Rive( - artboard: _artboard!, - fit: BoxFit.cover, - ) - : Container(), + ? Rive( + artboard: _artboard!, + fit: BoxFit.cover, + ) + : Container(), ); } - - @override - void didUpdateWidget(BotFace oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.expression != widget.expression) { - _controller!.setInputValue( - _controller!.stateMachine.inputs[0].id, - mapExpressionToInput(widget.expression), - ); - } - } } - -// extension ParseToString on BotExpressions { -// String toShortString() { -// return toString().split('.').last; -// } -// } diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index 560fb5e9c..68dec26ed 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -1,3 +1,5 @@ +import 'package:fluffychat/pangea/utils/bot_name.dart'; +import 'package:fluffychat/pangea/widgets/common/bot_face_svg.dart'; import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; import 'package:fluffychat/widgets/presence_builder.dart'; @@ -76,24 +78,30 @@ class Avatar extends StatelessWidget { side: border ?? BorderSide.none, ), clipBehavior: Clip.hardEdge, - child: noPic - ? textWidget - : MxcImage( - client: client, - key: ValueKey(mxContent.toString()), - cacheKey: '${mxContent}_$size', - uri: mxContent, - fit: BoxFit.cover, - width: size, - height: size, - placeholder: (_) => Center( - child: Icon( - Icons.person_2, - color: theme.colorScheme.tertiary, - size: size / 1.5, - ), - ), - ), + child: + // #Pangea + presenceUserId == BotName.byEnvironment + ? BotFace(width: size, expression: BotExpression.idle) + : + // Pangea# + noPic + ? textWidget + : MxcImage( + client: client, + key: ValueKey(mxContent.toString()), + cacheKey: '${mxContent}_$size', + uri: mxContent, + fit: BoxFit.cover, + width: size, + height: size, + placeholder: (_) => Center( + child: Icon( + Icons.person_2, + color: theme.colorScheme.tertiary, + size: size / 1.5, + ), + ), + ), ), ), if (presenceUserId != null) diff --git a/lib/widgets/profile_bottom_sheet.dart b/lib/widgets/profile_bottom_sheet.dart index afce5f48a..aececcb3c 100644 --- a/lib/widgets/profile_bottom_sheet.dart +++ b/lib/widgets/profile_bottom_sheet.dart @@ -89,6 +89,9 @@ class ProfileBottomSheet extends StatelessWidget { child: Avatar( mxContent: profile?.avatarUrl, name: profile?.displayName ?? userId, + // #Pangea + presenceUserId: userId, + // Pangea# size: Avatar.defaultSize * 3, ), ),