mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-04-18 14:42:54 +00:00
smoother response animation
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
This commit is contained in:
parent
527bf51bff
commit
f419b3709d
4 changed files with 72 additions and 34 deletions
|
|
@ -20,7 +20,7 @@ import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_dat
|
|||
import 'package:twonly/src/views/chats/chat_messages_components/message_input.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
import 'package:twonly/src/views/components/blink.component.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/blink.component.dart';
|
||||
import 'package:twonly/src/views/components/flame.dart';
|
||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_med
|
|||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_text_entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_unkown.entry.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/common.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_actions.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_reply_drag.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_context_menu.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
|
|
@ -74,8 +74,9 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
|
||||
Future<void> initAsync() async {
|
||||
if (widget.message.mediaId != null) {
|
||||
final mediaFileStream =
|
||||
twonlyDB.mediaFilesDao.watchMedia(widget.message.mediaId!);
|
||||
final mediaFileStream = twonlyDB.mediaFilesDao.watchMedia(
|
||||
widget.message.mediaId!,
|
||||
);
|
||||
mediaFileSub = mediaFileStream.listen((mediaFiles) {
|
||||
if (mediaFiles != null) {
|
||||
mediaService = MediaFileService(mediaFiles);
|
||||
|
|
@ -87,8 +88,9 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
}
|
||||
});
|
||||
}
|
||||
final stream =
|
||||
twonlyDB.reactionsDao.watchReactions(widget.message.messageId);
|
||||
final stream = twonlyDB.reactionsDao.watchReactions(
|
||||
widget.message.messageId,
|
||||
);
|
||||
|
||||
reactionsSub = stream.listen((update) {
|
||||
setState(() {
|
||||
|
|
@ -159,8 +161,10 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
);
|
||||
|
||||
final seen = <String>{};
|
||||
var reactionsForWidth =
|
||||
reactions.where((t) => seen.add(t.emoji)).toList().length;
|
||||
var reactionsForWidth = reactions
|
||||
.where((t) => seen.add(t.emoji))
|
||||
.toList()
|
||||
.length;
|
||||
if (reactionsForWidth > 4) reactionsForWidth = 4;
|
||||
|
||||
Widget child = Stack(
|
||||
|
|
@ -205,7 +209,7 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
);
|
||||
|
||||
if (widget.onResponseTriggered != null) {
|
||||
child = MessageActions(
|
||||
child = MessageReplyDrag(
|
||||
message: widget.message,
|
||||
onResponseTriggered: widget.onResponseTriggered!,
|
||||
child: child,
|
||||
|
|
@ -228,8 +232,9 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
child: Padding(
|
||||
padding: padding,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
right ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
mainAxisAlignment: right
|
||||
? MainAxisAlignment.end
|
||||
: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (!right && !widget.group.isDirectChat)
|
||||
hideContactAvatar
|
||||
|
|
@ -306,6 +311,6 @@ class _ChatListEntryState extends State<ChatListEntry> {
|
|||
bottomRight: Radius.circular(bottomRight),
|
||||
bottomLeft: Radius.circular(bottomLeft),
|
||||
),
|
||||
hideContactAvatar
|
||||
hideContactAvatar,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import 'package:flutter/services.dart';
|
|||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
|
||||
class MessageActions extends StatefulWidget {
|
||||
const MessageActions({
|
||||
class MessageReplyDrag extends StatefulWidget {
|
||||
const MessageReplyDrag({
|
||||
required this.child,
|
||||
required this.message,
|
||||
required this.onResponseTriggered,
|
||||
|
|
@ -17,27 +17,49 @@ class MessageActions extends StatefulWidget {
|
|||
final VoidCallback onResponseTriggered;
|
||||
|
||||
@override
|
||||
State<MessageActions> createState() => _SlidingResponseWidgetState();
|
||||
State<MessageReplyDrag> createState() => _SlidingResponseWidgetState();
|
||||
}
|
||||
|
||||
class _SlidingResponseWidgetState extends State<MessageActions> {
|
||||
class _SlidingResponseWidgetState extends State<MessageReplyDrag> {
|
||||
double _offsetX = 0;
|
||||
bool gotFeedback = false;
|
||||
double _dragProgress = 0;
|
||||
double _animatedScale = 1;
|
||||
|
||||
Future<void> _triggerPopAnimation() async {
|
||||
setState(() {
|
||||
_animatedScale = 1.3;
|
||||
});
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 50));
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_animatedScale = 1.0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onHorizontalDragUpdate(DragUpdateDetails details) {
|
||||
setState(() {
|
||||
_offsetX += details.delta.dx;
|
||||
if (_offsetX <= 0) _offsetX = 0;
|
||||
if (_offsetX > 40) {
|
||||
_offsetX = 40;
|
||||
if (!gotFeedback) {
|
||||
unawaited(HapticFeedback.heavyImpact());
|
||||
gotFeedback = true;
|
||||
unawaited(_triggerPopAnimation());
|
||||
}
|
||||
_dragProgress = 1;
|
||||
} else {
|
||||
_dragProgress = _offsetX / 40;
|
||||
}
|
||||
if (_offsetX < 30) {
|
||||
gotFeedback = false;
|
||||
}
|
||||
if (_offsetX <= 0) _offsetX = 0;
|
||||
if (_offsetX > 50) {
|
||||
_offsetX = 50;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +69,7 @@ class _SlidingResponseWidgetState extends State<MessageActions> {
|
|||
}
|
||||
setState(() {
|
||||
_offsetX = 0.0;
|
||||
_dragProgress = 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +77,32 @@ class _SlidingResponseWidgetState extends State<MessageActions> {
|
|||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
if (_dragProgress > 0.2)
|
||||
Positioned(
|
||||
left: _dragProgress * 10,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Transform.scale(
|
||||
scale: 1 * _dragProgress,
|
||||
child: AnimatedScale(
|
||||
duration: const Duration(milliseconds: 50),
|
||||
scale: _animatedScale,
|
||||
curve: Curves.easeInOut,
|
||||
child: Opacity(
|
||||
opacity: 1 * _dragProgress,
|
||||
child: const Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FaIcon(
|
||||
FontAwesomeIcons.reply,
|
||||
size: 14,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Transform.translate(
|
||||
offset: Offset(_offsetX, 0),
|
||||
child: GestureDetector(
|
||||
|
|
@ -62,22 +111,6 @@ class _SlidingResponseWidgetState extends State<MessageActions> {
|
|||
child: widget.child,
|
||||
),
|
||||
),
|
||||
if (_offsetX >= 40)
|
||||
const Positioned(
|
||||
left: 20,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FaIcon(
|
||||
FontAwesomeIcons.reply,
|
||||
size: 14,
|
||||
// color: Colors.green,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue