This commit is contained in:
seb
2026-10-13 02:13:31 +02:00
parent 8f68f7d224
commit 94fcac0795
2 changed files with 55 additions and 39 deletions

View File

@@ -45,7 +45,7 @@ import authAutomat from './authAutomat';
*/ */
export const CHUNK_SIZE = 50; export const CHUNK_SIZE = 50;
const PREFETCH_CHUNKS = 2; const PREFETCH_CHUNKS = 1;
const CHUNK_DEBOUNCE_MS = 130; const CHUNK_DEBOUNCE_MS = 130;
const initialState = { const initialState = {
@@ -87,7 +87,7 @@ function clearChunkDebounce() {
} }
// Executes chunk loading for the standstill target and prefetches lookahead chunks // Executes chunk loading for the standstill target and prefetches lookahead chunks
function _executeRequiredChunks(chunks, targetEpoch) { function _executeRequiredChunks(chunks, targetEpoch, prefetch = true) {
if (targetEpoch !== queryEpoch) { if (targetEpoch !== queryEpoch) {
return Promise.resolve(automat.state.items); return Promise.resolve(automat.state.items);
} }
@@ -102,17 +102,21 @@ function _executeRequiredChunks(chunks, targetEpoch) {
promises.push(actions.loadChunk(c, targetEpoch)); promises.push(actions.loadChunk(c, targetEpoch));
} }
// Prefetch lookahead chunks after the highest requested chunk // Once active chunks finish loading, prefetch the next lookahead chunk in the background
const maxChunk = chunkList[chunkList.length - 1]; return Promise.all(promises).then(() => {
const total = automat.state.total || 0; if (targetEpoch !== queryEpoch) return automat.state.items;
for (let c = maxChunk + 1; c <= maxChunk + PREFETCH_CHUNKS; c++) { if (prefetch) {
if (total > 0 && c * CHUNK_SIZE >= total) { const maxChunk = chunkList[chunkList.length - 1];
break; const total = automat.state.total || 0;
for (let c = maxChunk + 1; c <= maxChunk + PREFETCH_CHUNKS; c++) {
if (total > 0 && c * CHUNK_SIZE >= total) {
break;
}
void actions.loadChunk(c, targetEpoch);
}
} }
void actions.loadChunk(c, targetEpoch); return automat.state.items;
} });
return Promise.all(promises).then(() => automat.state.items);
} }
// MARK: pure order helpers // MARK: pure order helpers
@@ -214,10 +218,10 @@ function applyFilterReload() {
} }
const actions = { const actions = {
// MARK: returns current items and triggers chunk 0 if not loaded // MARK: returns current items and triggers chunk 0 if not loaded (no prefetch on entry)
getItems() { getItems() {
if (automat.state.items === null) { if (automat.state.items === null) {
void actions.requireChunk(0, 0, { immediate: true }); void actions.requireChunk(0, 0, { prefetch: false, immediate: true });
return null; return null;
} }
return automat.state.items; return automat.state.items;
@@ -250,13 +254,14 @@ const actions = {
start = Math.max(0, Math.floor(start)); start = Math.max(0, Math.floor(start));
end = Math.max(start, Math.floor(end)); end = Math.max(start, Math.floor(end));
const prefetch = options.prefetch !== false;
// If initial items are null or immediate requested, bypass debounce and load immediately // If initial items are null or immediate requested, bypass debounce and load immediately
if (options.immediate || automat.state.items === null) { if (options.immediate || automat.state.items === null) {
clearChunkDebounce(); clearChunkDebounce();
const chunks = new Set(); const chunks = new Set();
for (let c = start; c <= end; c++) chunks.add(c); for (let c = start; c <= end; c++) chunks.add(c);
return _executeRequiredChunks(chunks, queryEpoch); return _executeRequiredChunks(chunks, queryEpoch, prefetch);
} }
// New event tick (drag mousemove / pagination click) replaces previous target position // New event tick (drag mousemove / pagination click) replaces previous target position
@@ -302,7 +307,7 @@ const actions = {
pendingChunkResolve = null; pendingChunkResolve = null;
pendingChunkReject = null; pendingChunkReject = null;
_executeRequiredChunks(chunksToLoad, captureEpoch) _executeRequiredChunks(chunksToLoad, captureEpoch, true)
.then((res) => resolve?.(res)) .then((res) => resolve?.(res))
.catch((err) => reject?.(err)); .catch((err) => reject?.(err));
}, CHUNK_DEBOUNCE_MS); }, CHUNK_DEBOUNCE_MS);
@@ -363,15 +368,6 @@ const actions = {
error: null, error: null,
}); });
// Trigger lookahead prefetching if first chunk just arrived
if (index === 0) {
for (let c = 1; c <= PREFETCH_CHUNKS; c++) {
if (c * CHUNK_SIZE < currentTotal) {
void actions.loadChunk(c, targetEpoch);
}
}
}
return items; return items;
}) })
.catch((err) => { .catch((err) => {

View File

@@ -548,17 +548,6 @@ export class OrderList extends PureComponent {
render() { render() {
const { items, partiesById, busyId } = this.state; const { items, partiesById, busyId } = this.state;
if (items === null) {
if (orderAutomat.state.status === 'failed') {
return <BackendError error={backendErrorFrom(orderAutomat.state.error)} onRetry={this.handleRetry} />;
}
return (
<Box sx={{ display: 'flex', justifyContent: 'center', py: 6 }}>
<CircularProgress />
</Box>
);
}
rowContext.partiesById = partiesById; rowContext.partiesById = partiesById;
rowContext.busyId = busyId; rowContext.busyId = busyId;
rowContext.onOpen = this.handleOpen; rowContext.onOpen = this.handleOpen;
@@ -566,18 +555,31 @@ export class OrderList extends PureComponent {
rowContext.onTogglePaid = this.handleTogglePaid; rowContext.onTogglePaid = this.handleTogglePaid;
rowContext.onToggleDelivered = this.handleToggleDelivered; rowContext.onToggleDelivered = this.handleToggleDelivered;
if (items === null && orderAutomat.state.status === 'failed') {
return (
<>
<OrderSearchField inputRef={this.searchRef} onCreate={this.handleCreate} />
<PanelTileGrid>
<PanelTile wide={3} minHeight={0} color={cfg.listColor} sx={{ p: 2 }}>
<BackendError error={backendErrorFrom(orderAutomat.state.error)} onRetry={this.handleRetry} />
</PanelTile>
</PanelTileGrid>
</>
);
}
return ( return (
<> <>
<OrderSearchField inputRef={this.searchRef} onCreate={this.handleCreate} /> <OrderSearchField inputRef={this.searchRef} onCreate={this.handleCreate} />
<PanelTileGrid> <PanelTileGrid>
<PanelTile wide={3} minHeight={0} color={cfg.listColor} sx={{ pb: 0 }}> <PanelTile wide={3} minHeight={0} color={cfg.listColor} sx={{ pb: 0, position: 'relative' }}>
<FillSpace <FillSpace
aboveRef={[this.searchRef]} aboveRef={[this.searchRef]}
reserveBottom={32} reserveBottom={32}
minPageSize={8} minPageSize={8}
minHeight={0} minHeight={0}
items={items} items={items || []}
totalCount={items.length} totalCount={items ? items.length : 0}
renderItem={OrderRow} renderItem={OrderRow}
virtualScrollbar virtualScrollbar
header={ header={
@@ -611,6 +613,24 @@ export class OrderList extends PureComponent {
} }
onPageChange={this.handlePageChange} onPageChange={this.handlePageChange}
/> />
{items === null && (
<Box
sx={{
position: 'absolute',
top: 52,
left: 0,
right: 0,
bottom: 32,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
pointerEvents: 'none',
zIndex: 2,
}}
>
<CircularProgress size={32} />
</Box>
)}
</PanelTile> </PanelTile>
</PanelTileGrid> </PanelTileGrid>
</> </>