feat: Animate in new events in timeline
This commit is contained in:
parent
8f3f5cdb8b
commit
f07aba448d
3 changed files with 119 additions and 77 deletions
|
|
@ -304,6 +304,13 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
|
|
||||||
Future<void>? loadTimelineFuture;
|
Future<void>? loadTimelineFuture;
|
||||||
|
|
||||||
|
int? animateInEventIndex;
|
||||||
|
|
||||||
|
void onInsert(int i) {
|
||||||
|
// setState will be called by updateView() anyway
|
||||||
|
animateInEventIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _getTimeline({
|
Future<void> _getTimeline({
|
||||||
String? eventContextId,
|
String? eventContextId,
|
||||||
}) async {
|
}) async {
|
||||||
|
|
@ -317,11 +324,15 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
timeline = await room.getTimeline(
|
timeline = await room.getTimeline(
|
||||||
onUpdate: updateView,
|
onUpdate: updateView,
|
||||||
eventContextId: eventContextId,
|
eventContextId: eventContextId,
|
||||||
|
onInsert: onInsert,
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().w('Unable to load timeline on event ID $eventContextId', e, s);
|
Logs().w('Unable to load timeline on event ID $eventContextId', e, s);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
timeline = await room.getTimeline(onUpdate: updateView);
|
timeline = await room.getTimeline(
|
||||||
|
onUpdate: updateView,
|
||||||
|
onInsert: onInsert,
|
||||||
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (e is TimeoutException || e is IOException) {
|
if (e is TimeoutException || e is IOException) {
|
||||||
_showScrollUpMaterialBanner(eventContextId!);
|
_showScrollUpMaterialBanner(eventContextId!);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ class ChatEventList extends StatelessWidget {
|
||||||
final events = controller.timeline!.events
|
final events = controller.timeline!.events
|
||||||
.where((event) => event.isVisibleInGui)
|
.where((event) => event.isVisibleInGui)
|
||||||
.toList();
|
.toList();
|
||||||
|
final animateInEventIndex = controller.animateInEventIndex;
|
||||||
|
|
||||||
// create a map of eventId --> index to greatly improve performance of
|
// create a map of eventId --> index to greatly improve performance of
|
||||||
// ListView's findChildIndexCallback
|
// ListView's findChildIndexCallback
|
||||||
|
|
@ -101,6 +102,8 @@ class ChatEventList extends StatelessWidget {
|
||||||
|
|
||||||
// The message at this index:
|
// The message at this index:
|
||||||
final event = events[i];
|
final event = events[i];
|
||||||
|
final animateIn = animateInEventIndex != null &&
|
||||||
|
event == controller.timeline!.events[animateInEventIndex];
|
||||||
|
|
||||||
return AutoScrollTag(
|
return AutoScrollTag(
|
||||||
key: ValueKey(event.eventId),
|
key: ValueKey(event.eventId),
|
||||||
|
|
@ -108,6 +111,10 @@ class ChatEventList extends StatelessWidget {
|
||||||
controller: controller.scrollController,
|
controller: controller.scrollController,
|
||||||
child: Message(
|
child: Message(
|
||||||
event,
|
event,
|
||||||
|
animateIn: animateIn,
|
||||||
|
resetAnimateIn: () {
|
||||||
|
controller.animateInEventIndex = null;
|
||||||
|
},
|
||||||
onSwipe: () => controller.replyAction(replyTo: event),
|
onSwipe: () => controller.replyAction(replyTo: event),
|
||||||
onInfoTab: controller.showEventInfo,
|
onInfoTab: controller.showEventInfo,
|
||||||
onAvatarTab: (Event event) => showAdaptiveBottomSheet(
|
onAvatarTab: (Event event) => showAdaptiveBottomSheet(
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ class Message extends StatelessWidget {
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final Timeline timeline;
|
final Timeline timeline;
|
||||||
final bool highlightMarker;
|
final bool highlightMarker;
|
||||||
|
final bool animateIn;
|
||||||
|
final void Function()? resetAnimateIn;
|
||||||
|
|
||||||
const Message(
|
const Message(
|
||||||
this.event, {
|
this.event, {
|
||||||
|
|
@ -46,6 +48,8 @@ class Message extends StatelessWidget {
|
||||||
this.selected = false,
|
this.selected = false,
|
||||||
required this.timeline,
|
required this.timeline,
|
||||||
this.highlightMarker = false,
|
this.highlightMarker = false,
|
||||||
|
this.animateIn = false,
|
||||||
|
this.resetAnimateIn,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -382,8 +386,22 @@ class Message extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
TapDownDetails? lastTapDownDetails;
|
TapDownDetails? lastTapDownDetails;
|
||||||
|
final resetAnimateIn = this.resetAnimateIn;
|
||||||
|
var animateIn = this.animateIn;
|
||||||
|
|
||||||
return Center(
|
return StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
|
if (animateIn && resetAnimateIn != null) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
|
animateIn = false;
|
||||||
|
setState(resetAnimateIn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return AnimatedSlide(
|
||||||
|
offset: Offset(0, animateIn ? 1 : 0),
|
||||||
|
duration: FluffyThemes.animationDuration,
|
||||||
|
curve: FluffyThemes.animationCurve,
|
||||||
|
child: Center(
|
||||||
child: Swipeable(
|
child: Swipeable(
|
||||||
key: ValueKey(event.eventId),
|
key: ValueKey(event.eventId),
|
||||||
background: const Padding(
|
background: const Padding(
|
||||||
|
|
@ -400,7 +418,9 @@ class Message extends StatelessWidget {
|
||||||
lastTapDownDetails = details;
|
lastTapDownDetails = details;
|
||||||
},
|
},
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (lastTapDownDetails?.kind == PointerDeviceKind.mouse) return;
|
if (lastTapDownDetails?.kind == PointerDeviceKind.mouse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
onSelect(event);
|
onSelect(event);
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
|
|
@ -431,7 +451,8 @@ class Message extends StatelessWidget {
|
||||||
4,
|
4,
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.circular(AppConfig.borderRadius),
|
BorderRadius.circular(AppConfig.borderRadius),
|
||||||
shadowColor: Theme.of(context).appBarTheme.shadowColor,
|
shadowColor:
|
||||||
|
Theme.of(context).appBarTheme.shadowColor,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -466,6 +487,9 @@ class Message extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue