mirror of
https://github.com/block/buzz.git
synced 2026-11-12 12:22:06 +00:00
Share thread calendar cache with sticky date index
Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
@@ -313,7 +313,9 @@ class ThreadDetailPage extends HookConsumerWidget {
|
||||
const headIndex = 0;
|
||||
int indexForReply(int chronologicalIndex) => chronologicalIndex + 1;
|
||||
final tailAnchorIndex = replies.length + 1;
|
||||
final calendarDays = ref.watch(timelineCalendarDaysFactoryProvider)();
|
||||
final stickyDateIndex = _ThreadStickyDateIndex(
|
||||
calendarDays: calendarDays,
|
||||
head: liveHead,
|
||||
replies: replies,
|
||||
);
|
||||
@@ -923,6 +925,7 @@ class ThreadDetailPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _ThreadMessageList(
|
||||
calendarDays: calendarDays,
|
||||
headHeight: headHeight.value ?? 0,
|
||||
onHeadHeightChanged: (height) {
|
||||
if (context.mounted) headHeight.value = height;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
part of '../thread_detail_page.dart';
|
||||
|
||||
class _ThreadMessageList extends HookConsumerWidget {
|
||||
class _ThreadMessageList extends HookWidget {
|
||||
final TimelineCalendarDays calendarDays;
|
||||
final double headHeight;
|
||||
final ValueChanged<double> onHeadHeightChanged;
|
||||
final LaidOutViewport viewport;
|
||||
@@ -30,6 +31,7 @@ class _ThreadMessageList extends HookConsumerWidget {
|
||||
final Map<String, List<TimelineMessage>> childrenByParent;
|
||||
|
||||
const _ThreadMessageList({
|
||||
required this.calendarDays,
|
||||
required this.headHeight,
|
||||
required this.onHeadHeightChanged,
|
||||
required this.viewport,
|
||||
@@ -144,8 +146,7 @@ class _ThreadMessageList extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final calendarDays = ref.watch(timelineCalendarDaysFactoryProvider)();
|
||||
Widget build(BuildContext context) {
|
||||
final headLink = useMemoized(LayerLink.new);
|
||||
final loadingHeadLink = useMemoized(LayerLink.new);
|
||||
final headScrollPosition = useRef<ScrollPosition?>(null);
|
||||
|
||||
@@ -22,6 +22,7 @@ class _ThreadStickyDateIndex {
|
||||
final int messageCount;
|
||||
|
||||
_ThreadStickyDateIndex({
|
||||
required TimelineCalendarDays calendarDays,
|
||||
required TimelineMessage head,
|
||||
required List<TimelineMessage> replies,
|
||||
}) : messageCount = replies.length + 1 {
|
||||
@@ -32,7 +33,10 @@ class _ThreadStickyDateIndex {
|
||||
final message = messages[index];
|
||||
final startsDay =
|
||||
index == 0 ||
|
||||
!isSameDay(messages[index - 1].createdAt, message.createdAt);
|
||||
!calendarDays.isSameDay(
|
||||
messages[index - 1].createdAt,
|
||||
message.createdAt,
|
||||
);
|
||||
if (startsDay) {
|
||||
activeDayTimestamp = message.createdAt;
|
||||
activeDayStartIndex = index;
|
||||
|
||||
@@ -100,6 +100,75 @@ void timelineCalendarBuilderTests() {
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('thread sticky index and visible rows share all conversions', (
|
||||
tester,
|
||||
) async {
|
||||
final conversions = <Map<int, int>>[];
|
||||
final root = _textMsg(
|
||||
id: threadIdThreadDashRoot,
|
||||
pubkey: 'alice',
|
||||
content: 'Root',
|
||||
createdAt: 1000,
|
||||
);
|
||||
final replies = [
|
||||
for (var i = 1; i <= 60; i++)
|
||||
_textMsg(
|
||||
id: 'calendar-long-$i',
|
||||
pubkey: 'bob',
|
||||
content: 'Reply $i',
|
||||
createdAt: 1000 + i * 60,
|
||||
extraTags: const [
|
||||
['e', threadIdThreadDashRoot, '', 'reply'],
|
||||
],
|
||||
),
|
||||
];
|
||||
await tester.pumpWidget(
|
||||
_buildTestable(
|
||||
messages: [root],
|
||||
threadReplies: {threadIdThreadDashRoot: replies},
|
||||
calendarDaysFactory: () {
|
||||
final counts = <int, int>{};
|
||||
conversions.add(counts);
|
||||
return TimelineCalendarDays.withConverter((timestamp) {
|
||||
counts.update(timestamp, (count) => count + 1, ifAbsent: () => 1);
|
||||
return DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
// Discard channel caches before mounting the thread.
|
||||
conversions.clear();
|
||||
final head = formatTimeline([root]).single;
|
||||
Navigator.of(tester.element(find.byType(ChannelDetailPage))).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => ThreadDetailPage(
|
||||
threadHead: head,
|
||||
allMessages: [head],
|
||||
channelId: _channelId,
|
||||
currentPubkey: 'self',
|
||||
isMember: true,
|
||||
isArchived: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
final threadCaches = conversions
|
||||
.where((counts) => counts.containsKey(replies.last.createdAt))
|
||||
.toList();
|
||||
expect(threadCaches, isNotEmpty);
|
||||
for (final counts in threadCaches) {
|
||||
expect(
|
||||
counts.keys,
|
||||
containsAll([
|
||||
root.createdAt,
|
||||
...replies.map((reply) => reply.createdAt),
|
||||
]),
|
||||
);
|
||||
expect(counts.values, everyElement(1));
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('thread shows day dividers when replies cross days', (
|
||||
tester,
|
||||
) async {
|
||||
|
||||
Reference in New Issue
Block a user