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 <ggurdin@gmail.com>
pull/1544/head
Kelrap 11 months ago committed by GitHub
parent 6e7dc594f2
commit d6d6875882
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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,
),
),

@ -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,
),
),

@ -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(

@ -42,6 +42,9 @@ class SeenByRow extends StatelessWidget {
(user) => Avatar(
mxContent: user.avatarUrl,
name: user.calcDisplayname(),
// #Pangea
presenceUserId: user.id,
// Pangea#
size: 16,
),
),

@ -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}',

@ -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,
),
),

@ -732,6 +732,9 @@ class ChatListController extends State<ChatList>
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<ChatList>
mxContent: space.avatar,
size: Avatar.defaultSize / 2,
name: space.getLocalizedDisplayname(),
// #Pangea
presenceUserId: space.directChatMatrixID,
// Pangea#
),
const SizedBox(width: 12),
Expanded(

@ -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),
),
),

@ -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,

@ -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#

@ -546,6 +546,9 @@ class _SpaceViewState extends State<SpaceView> {
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<SpaceView> {
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<SpaceView> {
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,

@ -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,
),

@ -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),

@ -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)

@ -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],

@ -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,
),
),

@ -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,
),
),

@ -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<BotFace> {
Artboard? _artboard;
StateMachineController? _controller;
final Random _random = Random();
@override
void initState() {
super.initState();
_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<BotFace> {
}
Future<void> _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,14 +93,15 @@ class BotFaceState extends State<BotFace> {
);
}
if (mounted) {
setState(() {
_artboard = artboard;
});
}
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
height: widget.width,
@ -78,21 +113,4 @@ class BotFaceState extends State<BotFace> {
: 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;
// }
// }

@ -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,7 +78,13 @@ class Avatar extends StatelessWidget {
side: border ?? BorderSide.none,
),
clipBehavior: Clip.hardEdge,
child: noPic
child:
// #Pangea
presenceUserId == BotName.byEnvironment
? BotFace(width: size, expression: BotExpression.idle)
:
// Pangea#
noPic
? textWidget
: MxcImage(
client: client,

@ -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,
),
),

Loading…
Cancel
Save