queue sheet update

This commit is contained in:
Boof2015
2026-07-28 17:17:43 -04:00
parent 0df3dfcf6b
commit 79fb381e67
3 changed files with 86 additions and 22 deletions
+76 -22
View File
@@ -7,6 +7,7 @@ import {
useState
} from 'react';
import {
InteractionManager,
Pressable,
StyleSheet,
View,
@@ -77,6 +78,7 @@ import {
type QueueIndexByKey,
} from './queueActions';
import {
QUEUE_INITIAL_RENDER_DISTANCE,
QUEUE_ROW_HEIGHT,
queuePreviewRowCount,
queueRenderDistance,
@@ -204,9 +206,16 @@ export const QueueTray = memo(function QueueTray({ onClose, embedded = false }:
// the tray's first frame and unmounts once the real list has painted (onLoad
// fires a frame after first layout completes, so rows are already underneath).
const [listPainted, setListPainted] = useState(false);
const onListLoad = useCallback(() => setListPainted(true), []);
const listPaintedRef = useRef(false);
const onListLoad = useCallback(() => {
listPaintedRef.current = true;
setListPainted(true);
}, []);
const [renderCoverageReady, setRenderCoverageReady] = useState(embedded);
const previewCount = queuePreviewRowCount(windowHeight);
const renderDistance = queueRenderDistance(windowHeight);
const renderDistance = renderCoverageReady
? queueRenderDistance(windowHeight)
: QUEUE_INITIAL_RENDER_DISTANCE;
// Bottom padding clears the gesture-nav inset so the last row is fully
// scrollable into view at the 100% snap.
const listContentStyle = useMemo(
@@ -244,27 +253,43 @@ export const QueueTray = memo(function QueueTray({ onClose, embedded = false }:
const virtualLoadQueued = useRef(false);
const virtualRetryTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const virtualPageLoaderRef = useRef<(reset: boolean) => Promise<void>>(async () => {});
const deferredMountTask = useRef<
ReturnType<typeof InteractionManager.runAfterInteractions> | null
>(null);
const resetVirtualTracks = useCallback(() => {
virtualLoadGeneration.current += 1;
virtualLoadQueued.current = false;
if (virtualRetryTimer.current) {
clearTimeout(virtualRetryTimer.current);
virtualRetryTimer.current = null;
}
const state = getVirtualQueueState();
const seed = state
? seedVirtualQueueTracks(
rollingUpcomingTracksRef.current,
state.activePosition,
state.totalCount,
)
: [];
const previous = virtualTracksRef.current;
const unchanged =
seed.length === previous.length &&
seed.every((track, index) => track === previous[index]);
virtualTracksRef.current = unchanged ? previous : seed;
if (!unchanged) setVirtualTracks(seed);
return state;
}, []);
const loadVirtualPage = useCallback((reset: boolean): Promise<void> => {
if (reset) {
virtualLoadGeneration.current += 1;
virtualLoadQueued.current = false;
if (virtualRetryTimer.current) {
clearTimeout(virtualRetryTimer.current);
virtualRetryTimer.current = null;
if (!embedded && listPaintedRef.current && deferredMountTask.current) {
deferredMountTask.current.cancel();
deferredMountTask.current = null;
setRenderCoverageReady(true);
}
const state = getVirtualQueueState();
const seed = state
? seedVirtualQueueTracks(
rollingUpcomingTracksRef.current,
state.activePosition,
state.totalCount,
)
: [];
virtualTracksRef.current = seed;
setVirtualTracks(seed);
const state = resetVirtualTracks();
if (!state) return Promise.resolve();
if (virtualLoadPromise.current) {
virtualLoadQueued.current = true;
@@ -358,15 +383,44 @@ export const QueueTray = memo(function QueueTray({ onClose, embedded = false }:
}
});
return request;
}, []);
}, [embedded, resetVirtualTracks]);
virtualPageLoaderRef.current = loadVirtualPage;
useEffect(() => {
void loadVirtualPage(true);
}, [loadVirtualPage, virtualActivePosition, virtualMode, virtualState?.sessionId]);
if (embedded || listPaintedRef.current) {
void loadVirtualPage(true);
} else {
resetVirtualTracks();
}
}, [
embedded,
loadVirtualPage,
resetVirtualTracks,
virtualActivePosition,
virtualMode,
virtualState?.sessionId,
]);
useEffect(() => {
if (embedded || !listPainted) return;
const task = InteractionManager.runAfterInteractions(() => {
if (deferredMountTask.current !== task) return;
deferredMountTask.current = null;
setRenderCoverageReady(true);
void virtualPageLoaderRef.current(false);
});
deferredMountTask.current = task;
return () => {
task.cancel();
if (deferredMountTask.current === task) deferredMountTask.current = null;
};
}, [embedded, listPainted]);
useEffect(() => () => {
virtualLoadGeneration.current += 1;
deferredMountTask.current?.cancel();
if (virtualRetryTimer.current) clearTimeout(virtualRetryTimer.current);
}, []);
@@ -1,6 +1,8 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
QUEUE_INITIAL_RENDER_AHEAD_ROWS,
QUEUE_INITIAL_RENDER_DISTANCE,
QUEUE_RENDER_AHEAD_MIN_ROWS,
QUEUE_RENDER_WINDOW_MULTIPLIER,
QUEUE_ROW_HEIGHT,
@@ -8,6 +10,11 @@ import {
queueRenderDistance,
} from './queuePerformance.ts';
test('queue opens with the former four-row render-ahead distance', () => {
assert.equal(QUEUE_INITIAL_RENDER_AHEAD_ROWS, 4);
assert.equal(QUEUE_INITIAL_RENDER_DISTANCE, QUEUE_ROW_HEIGHT * 4);
});
test('queue render-ahead covers two windows with a twelve-row minimum', () => {
assert.equal(QUEUE_RENDER_AHEAD_MIN_ROWS, 12);
assert.equal(QUEUE_RENDER_WINDOW_MULTIPLIER, 2);
+3
View File
@@ -1,4 +1,7 @@
export const QUEUE_ROW_HEIGHT = 64;
export const QUEUE_INITIAL_RENDER_AHEAD_ROWS = 4;
export const QUEUE_INITIAL_RENDER_DISTANCE =
QUEUE_ROW_HEIGHT * QUEUE_INITIAL_RENDER_AHEAD_ROWS;
export const QUEUE_RENDER_AHEAD_MIN_ROWS = 12;
export const QUEUE_RENDER_WINDOW_MULTIPLIER = 2;