clean up approach to avoid polling

This commit is contained in:
Henry Hiles 2026-09-25 21:24:10 -04:00
commit 72321cc413
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -28,8 +28,6 @@ final class ChatScroll({
final anchorId = useState<String?>(null);
final anchorItemKey = useMemoized(GlobalKey.new, [anchorId.value]);
final anchorItemKeyRef = useRef(anchorItemKey);
anchorItemKeyRef.value = anchorItemKey;
final historyListController = useMemoized(ListController.new, [
anchorId.value,
@ -41,6 +39,7 @@ final class ChatScroll({
final centerKey = useMemoized(GlobalKey.new);
final pendingAnchorTarget = useState<String?>(null);
final anchorMountedCompleter = useRef<Completer<BuildContext>?>(null);
useEffect(() {
if (anchorId.value == null) {
@ -70,13 +69,35 @@ final class ChatScroll({
false;
if (found || controllerData is AsyncError) {
if (found) anchorId.value = target;
if (found) {
anchorId.value = target;
} else {
anchorMountedCompleter.value?.completeError(
StateError("Failed to load context for $target"),
);
anchorMountedCompleter.value = null;
}
pendingAnchorTarget.value = null;
}
return null;
}, [controllerData, pendingAnchorTarget.value]);
useEffect(() {
final completer = anchorMountedCompleter.value;
if (completer == null) return null;
WidgetsBinding.instance.addPostFrameCallback((_) {
final context = anchorItemKey.currentContext;
if (context != null && context.mounted) {
anchorMountedCompleter.value?.complete(context);
anchorMountedCompleter.value = null;
}
});
return null;
}, [anchorId.value]);
final ({IList<Event> history, IList<Event> live}) split = useMemoized(() {
final items = controllerData.value?.timeline;
final anchor = anchorId.value;
@ -157,24 +178,20 @@ final class ChatScroll({
return;
}
final completer = Completer<BuildContext>();
anchorMountedCompleter.value = completer;
pendingAnchorTarget.value = itemId;
setContextualEvent(itemId);
for (var i = 0; i < 60; i++) {
if (anchorId.value == itemId) {
final context = anchorItemKeyRef.value.currentContext;
if (context != null && context.mounted) {
final context = await completer.future;
if (context.mounted) {
await Scrollable.ensureVisible(
context,
alignment: 0.5,
duration: const Duration(milliseconds: 700),
curve: Curves.easeInOut,
);
return;
}
}
await Future.delayed(const Duration(milliseconds: 16));
}
}