chore: Nicer message animation

This commit is contained in:
krille-chan 2024-05-20 17:25:09 +02:00
commit 3d7dbf7ddf
No known key found for this signature in database

View file

@ -144,255 +144,270 @@ class Message extends StatelessWidget {
setState(resetAnimateIn); setState(resetAnimateIn);
}); });
} }
return AnimatedSlide( return AnimatedSize(
offset: Offset(0, animateIn ? 1 : 0),
duration: FluffyThemes.animationDuration, duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve, curve: FluffyThemes.animationCurve,
child: Stack( child: animateIn
children: [ ? const SizedBox.shrink()
Positioned( : Stack(
top: 0, children: [
bottom: 0, Positioned(
left: 0, top: 0,
right: 0, bottom: 0,
child: InkWell( left: 0,
onTap: () => onSelect(event), right: 0,
onLongPress: () => onSelect(event), child: InkWell(
borderRadius: onTap: () => onSelect(event),
BorderRadius.circular(AppConfig.borderRadius / 2), onLongPress: () => onSelect(event),
child: Material( borderRadius:
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
BorderRadius.circular(AppConfig.borderRadius / 2), child: Material(
color: selected borderRadius:
? Theme.of(context) BorderRadius.circular(AppConfig.borderRadius / 2),
.colorScheme color: selected
.secondaryContainer ? Theme.of(context)
.withAlpha(100) .colorScheme
: highlightMarker .secondaryContainer
? Theme.of(context) .withAlpha(100)
.colorScheme : highlightMarker
.tertiaryContainer ? Theme.of(context)
.withAlpha(100) .colorScheme
: Colors.transparent, .tertiaryContainer
), .withAlpha(100)
), : Colors.transparent,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: rowMainAxisAlignment,
children: [
if (longPressSelect)
SizedBox(
height: 32,
width: Avatar.defaultSize,
child: Checkbox.adaptive(
value: selected,
shape: const CircleBorder(),
onChanged: (_) => onSelect(event),
),
)
else if (nextEventSameSender || ownMessage)
SizedBox(
width: Avatar.defaultSize,
child: Center(
child: SizedBox(
width: 16,
height: 16,
child: event.status == EventStatus.error
? const Icon(Icons.error, color: Colors.red)
: event.fileSendingStatus != null
? const CircularProgressIndicator.adaptive(
strokeWidth: 1,
)
: null,
), ),
), ),
)
else
FutureBuilder<User?>(
future: event.fetchSenderUser(),
builder: (context, snapshot) {
final user =
snapshot.data ?? event.senderFromMemoryOrFallback;
return Avatar(
mxContent: user.avatarUrl,
name: user.calcDisplayname(),
presenceUserId: user.stateKey,
presenceBackgroundColor:
avatarPresenceBackgroundColor,
onTap: () => onAvatarTab(event),
);
},
), ),
Expanded( Row(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisAlignment: rowMainAxisAlignment,
children: [ children: [
if (!nextEventSameSender) if (longPressSelect)
Padding( SizedBox(
padding: height: 32,
const EdgeInsets.only(left: 8.0, bottom: 4), width: Avatar.defaultSize,
child: ownMessage || event.room.isDirectChat child: Checkbox.adaptive(
? const SizedBox(height: 12) value: selected,
: FutureBuilder<User?>( shape: const CircleBorder(),
future: event.fetchSenderUser(), onChanged: (_) => onSelect(event),
builder: (context, snapshot) { ),
final displayname = )
snapshot.data?.calcDisplayname() ?? else if (nextEventSameSender || ownMessage)
event.senderFromMemoryOrFallback SizedBox(
.calcDisplayname(); width: Avatar.defaultSize,
return Text( child: Center(
displayname, child: SizedBox(
style: TextStyle( width: 16,
fontSize: 12, height: 16,
color: child: event.status == EventStatus.error
(Theme.of(context).brightness == ? const Icon(Icons.error, color: Colors.red)
Brightness.light : event.fileSendingStatus != null
? displayname.color ? const CircularProgressIndicator
: displayname.lightColorText), .adaptive(
), strokeWidth: 1,
); )
}, : null,
), ),
),
)
else
FutureBuilder<User?>(
future: event.fetchSenderUser(),
builder: (context, snapshot) {
final user = snapshot.data ??
event.senderFromMemoryOrFallback;
return Avatar(
mxContent: user.avatarUrl,
name: user.calcDisplayname(),
presenceUserId: user.stateKey,
presenceBackgroundColor:
avatarPresenceBackgroundColor,
onTap: () => onAvatarTab(event),
);
},
), ),
Container( Expanded(
alignment: alignment, child: Column(
padding: const EdgeInsets.only(left: 8), crossAxisAlignment: CrossAxisAlignment.start,
child: GestureDetector( mainAxisSize: MainAxisSize.min,
onLongPress: longPressSelect children: [
? null if (!nextEventSameSender)
: () { Padding(
HapticFeedback.heavyImpact(); padding: const EdgeInsets.only(
onSelect(event); left: 8.0, bottom: 4),
}, child: ownMessage || event.room.isDirectChat
child: AnimatedOpacity( ? const SizedBox(height: 12)
opacity: animateIn : FutureBuilder<User?>(
? 0 future: event.fetchSenderUser(),
: event.redacted || builder: (context, snapshot) {
event.messageType == final displayname = snapshot.data
MessageTypes.BadEncrypted || ?.calcDisplayname() ??
event.status.isSending event.senderFromMemoryOrFallback
? 0.5 .calcDisplayname();
: 1, return Text(
duration: FluffyThemes.animationDuration, displayname,
curve: FluffyThemes.animationCurve, style: TextStyle(
child: Material( fontSize: 12,
color: noBubble ? Colors.transparent : color, color: (Theme.of(context)
clipBehavior: Clip.antiAlias, .brightness ==
shape: RoundedRectangleBorder( Brightness.light
borderRadius: borderRadius, ? displayname.color
), : displayname
child: Container( .lightColorText),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
AppConfig.borderRadius,
),
),
padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
constraints: const BoxConstraints(
maxWidth: FluffyThemes.columnWidth * 1.5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
if (event.relationshipType ==
RelationshipTypes.reply)
FutureBuilder<Event?>(
future: event.getReplyEvent(timeline),
builder:
(BuildContext context, snapshot) {
final replyEvent = snapshot.hasData
? snapshot.data!
: Event(
eventId: event
.relationshipEventId!,
content: {
'msgtype': 'm.text',
'body': '...',
},
senderId: event.senderId,
type: 'm.room.message',
room: event.room,
status: EventStatus.sent,
originServerTs:
DateTime.now(),
);
return Padding(
padding: const EdgeInsets.only(
bottom: 4.0,
),
child: InkWell(
borderRadius:
ReplyContent.borderRadius,
onTap: () => scrollToEventId(
replyEvent.eventId,
),
child: AbsorbPointer(
child: ReplyContent(
replyEvent,
ownMessage: ownMessage,
timeline: timeline,
),
),
), ),
); );
}, },
), ),
MessageContent( ),
displayEvent, Container(
textColor: textColor, alignment: alignment,
onInfoTab: onInfoTab, padding: const EdgeInsets.only(left: 8),
child: GestureDetector(
onLongPress: longPressSelect
? null
: () {
HapticFeedback.heavyImpact();
onSelect(event);
},
child: AnimatedOpacity(
opacity: animateIn
? 0
: event.redacted ||
event.messageType ==
MessageTypes.BadEncrypted ||
event.status.isSending
? 0.5
: 1,
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
child: Material(
color:
noBubble ? Colors.transparent : color,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: borderRadius, borderRadius: borderRadius,
), ),
if (event.hasAggregatedEvents( child: Container(
timeline, decoration: BoxDecoration(
RelationshipTypes.edit, borderRadius: BorderRadius.circular(
)) AppConfig.borderRadius,
Padding(
padding: const EdgeInsets.only(
top: 4.0,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.edit_outlined,
color: textColor.withAlpha(164),
size: 14,
),
Text(
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
style: TextStyle(
color:
textColor.withAlpha(164),
fontSize: 12,
),
),
],
), ),
), ),
], padding: noBubble || noPadding
? EdgeInsets.zero
: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
constraints: const BoxConstraints(
maxWidth:
FluffyThemes.columnWidth * 1.5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
if (event.relationshipType ==
RelationshipTypes.reply)
FutureBuilder<Event?>(
future: event
.getReplyEvent(timeline),
builder: (BuildContext context,
snapshot) {
final replyEvent = snapshot
.hasData
? snapshot.data!
: Event(
eventId: event
.relationshipEventId!,
content: {
'msgtype': 'm.text',
'body': '...',
},
senderId:
event.senderId,
type:
'm.room.message',
room: event.room,
status:
EventStatus.sent,
originServerTs:
DateTime.now(),
);
return Padding(
padding:
const EdgeInsets.only(
bottom: 4.0,
),
child: InkWell(
borderRadius: ReplyContent
.borderRadius,
onTap: () =>
scrollToEventId(
replyEvent.eventId,
),
child: AbsorbPointer(
child: ReplyContent(
replyEvent,
ownMessage:
ownMessage,
timeline: timeline,
),
),
),
);
},
),
MessageContent(
displayEvent,
textColor: textColor,
onInfoTab: onInfoTab,
borderRadius: borderRadius,
),
if (event.hasAggregatedEvents(
timeline,
RelationshipTypes.edit,
))
Padding(
padding: const EdgeInsets.only(
top: 4.0,
),
child: Row(
mainAxisSize:
MainAxisSize.min,
children: [
Icon(
Icons.edit_outlined,
color: textColor
.withAlpha(164),
size: 14,
),
Text(
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
style: TextStyle(
color: textColor
.withAlpha(164),
fontSize: 12,
),
),
],
),
),
],
),
),
),
), ),
), ),
), ),
), ],
), ),
), ),
], ],
), ),
), ],
], ),
),
],
),
); );
}, },
); );