fixup! refactor chatview to have two sliver lists

This commit is contained in:
Henry Hiles 2026-09-24 20:22:30 -04:00
commit 856072e0b0
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
2 changed files with 31 additions and 45 deletions

View file

@ -11,9 +11,6 @@ final class ChatScroll<T>({
required final ListController historyListController, required final ListController historyListController,
required final ListController liveListController, required final ListController liveListController,
required final ScrollController scrollController, required final ScrollController scrollController,
required final bool hasMore,
required final bool isLoadingOlder,
required final Future<void> Function() loadOlder,
required final Future<void> Function(String id) jumpToId, required final Future<void> Function(String id) jumpToId,
}) { }) {
static ChatScroll<T> use<T>({ static ChatScroll<T> use<T>({
@ -28,8 +25,6 @@ final class ChatScroll<T>({
final centerKey = useMemoized(GlobalKey.new); final centerKey = useMemoized(GlobalKey.new);
final anchorId = useState<String?>(null); final anchorId = useState<String?>(null);
final hasMore = useState(true);
final isLoadingOlder = useState(false);
final anchorIdValue = anchorId.value; final anchorIdValue = anchorId.value;
@ -65,17 +60,40 @@ final class ChatScroll<T>({
}, [controllerData, anchorIdValue]); }, [controllerData, anchorIdValue]);
Future<void> loadOlderItems() async { Future<void> loadOlderItems() async {
if (!hasMore.value || isLoadingOlder.value) return; if (controllerData.isLoading) return;
isLoadingOlder.value = true; await loadOlder();
}
try { useEffect(() {
hasMore.value = await loadOlder(); const loadThreshold = 500.0;
} finally { const bottomThreshold = 50.0;
isLoadingOlder.value = false;
void checkPosition() {
if (!scrollController.hasClients) return;
final position = scrollController.position;
if (position.extentAfter <= loadThreshold) {
loadOlderItems();
}
if (position.extentBefore <= bottomThreshold) {
onReachedBottom();
} }
} }
scrollController.addListener(checkPosition);
WidgetsBinding.instance.addPostFrameCallback((_) {
checkPosition();
});
return () {
scrollController.removeListener(checkPosition);
};
}, [scrollController, controllerData, loadOlder, onReachedBottom]);
Future<void> jumpToId(String itemId) async { Future<void> jumpToId(String itemId) async {
if (!scrollController.hasClients) return; if (!scrollController.hasClients) return;
@ -108,35 +126,6 @@ final class ChatScroll<T>({
} }
} }
useEffect(() {
const loadThreshold = 500.0;
const bottomThreshold = 50.0;
void checkPosition() {
if (!scrollController.hasClients) {
return;
}
final position = scrollController.position;
if (position.extentAfter <= loadThreshold) {
if (hasMore.value && !isLoadingOlder.value) {
loadOlderItems();
}
}
if (position.extentBefore <= bottomThreshold) {
onReachedBottom();
}
}
scrollController.addListener(checkPosition);
WidgetsBinding.instance.addPostFrameCallback((_) => checkPosition());
return () => scrollController.removeListener(checkPosition);
}, [scrollController, onReachedBottom]);
return .new( return .new(
historyItems: split.history, historyItems: split.history,
liveItems: split.live, liveItems: split.live,
@ -144,9 +133,6 @@ final class ChatScroll<T>({
historyListController: historyListController.value, historyListController: historyListController.value,
liveListController: liveListController.value, liveListController: liveListController.value,
scrollController: scrollController, scrollController: scrollController,
hasMore: hasMore.value,
isLoadingOlder: isLoadingOlder.value,
loadOlder: loadOlderItems,
jumpToId: jumpToId, jumpToId: jumpToId,
); );
} }

View file

@ -68,7 +68,7 @@ class const ChatTimeline({
itemCount: scroll.historyItems.length, itemCount: scroll.historyItems.length,
itemBuilder: (_, index) => eventRow( itemBuilder: (_, index) => eventRow(
scroll.historyItems[index], scroll.historyItems[index],
scroll.historyItems.getOrNull(index), scroll.historyItems.getOrNull(index + 1),
jumpToId: jumpToId, jumpToId: jumpToId,
getEventOptions: getEventOptions, getEventOptions: getEventOptions,
highlightedEvent: highlightedEvent, highlightedEvent: highlightedEvent,