perf(web): свод 2 — состояние вкладки с поколениями запросов
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
900a3c6535
commit
78e06583f4
@ -110,3 +110,51 @@ export function getSvod2RowId(row) {
|
||||
if (isPlaceholderRow(row)) return `ph-${row.index}`;
|
||||
return `${row.data?.header?.source_form}-${row.data?.header?.source_id}-${row.data?.line_id}`;
|
||||
}
|
||||
|
||||
export function createSheetPagingState() {
|
||||
return {
|
||||
generation: 0,
|
||||
dataGeneration: 0,
|
||||
pendingQueryKey: '',
|
||||
loadedQueryKey: '',
|
||||
total: 0,
|
||||
facets: null,
|
||||
pages: new Map(),
|
||||
loading: false,
|
||||
error: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function svod2PagingReducer(state, action) {
|
||||
if (action.generation !== state.generation && action.type !== 'begin') return state;
|
||||
switch (action.type) {
|
||||
case 'begin':
|
||||
return {
|
||||
...state,
|
||||
generation: action.generation,
|
||||
pendingQueryKey: action.queryKey,
|
||||
error: '',
|
||||
loading: action.mode === 'load' ? true : state.loading,
|
||||
...(action.mode === 'load' ? { pages: new Map(), total: 0, facets: null } : {}),
|
||||
};
|
||||
case 'page': {
|
||||
const first = state.dataGeneration !== action.generation;
|
||||
return {
|
||||
...state,
|
||||
pages: first ? new Map([[action.pageIndex, action.rows]]) : touchPage(state.pages, action.pageIndex, action.rows, action.keepPages),
|
||||
dataGeneration: action.generation,
|
||||
loadedQueryKey: first ? state.pendingQueryKey : state.loadedQueryKey,
|
||||
total: action.total,
|
||||
};
|
||||
}
|
||||
case 'facets':
|
||||
return { ...state, facets: action.facets };
|
||||
case 'fail':
|
||||
if (action.mode === 'scroll') return state;
|
||||
return { ...state, error: action.message, pages: new Map(), total: 0 };
|
||||
case 'settled':
|
||||
return { ...state, loading: false };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
buildSvod2PageParams,
|
||||
buildSvod2QueryParams,
|
||||
buildWindowRows,
|
||||
createSheetPagingState,
|
||||
facetsToMaps,
|
||||
getMissingPages,
|
||||
getPageCount,
|
||||
@ -16,6 +17,7 @@ import {
|
||||
getWindowRange,
|
||||
isPlaceholderRow,
|
||||
shouldShowFound,
|
||||
svod2PagingReducer,
|
||||
touchPage,
|
||||
} from './svod2Paging.js';
|
||||
|
||||
@ -152,3 +154,105 @@ test('getSvod2RowId', () => {
|
||||
assert.equal(isPlaceholderRow({ data: {} }), false);
|
||||
assert.equal(isPlaceholderRow(undefined), false);
|
||||
});
|
||||
|
||||
const begin = (s, generation, mode, queryKey = 'k') => svod2PagingReducer(s, { type: 'begin', generation, mode, queryKey });
|
||||
const page = (s, generation, pageIndex, rows, total) =>
|
||||
svod2PagingReducer(s, { type: 'page', generation, pageIndex, rows, total, keepPages: [pageIndex] });
|
||||
|
||||
test('начальное состояние вкладки', () => {
|
||||
const s = createSheetPagingState();
|
||||
assert.equal(s.generation, 0);
|
||||
assert.equal(s.dataGeneration, 0);
|
||||
assert.equal(s.total, 0);
|
||||
assert.equal(s.facets, null);
|
||||
assert.equal(s.loading, false);
|
||||
assert.equal(s.error, '');
|
||||
assert.equal(s.pages.size, 0);
|
||||
});
|
||||
|
||||
test('устаревшие page/facets/fail игнорируются', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load');
|
||||
s = begin(s, 2, 'refine');
|
||||
for (const a of [
|
||||
{ type: 'page', generation: 1, pageIndex: 0, rows: [], total: 9 },
|
||||
{ type: 'facets', generation: 1, facets: {} },
|
||||
{ type: 'fail', generation: 1, mode: 'load', message: 'x' },
|
||||
{ type: 'settled', generation: 1 },
|
||||
])
|
||||
assert.equal(svod2PagingReducer(s, a), s);
|
||||
});
|
||||
|
||||
test('load ставит loading и pendingQueryKey, сбрасывает error', () => {
|
||||
const s = begin(createSheetPagingState(), 1, 'load', 'q1');
|
||||
assert.equal(s.loading, true);
|
||||
assert.equal(s.generation, 1);
|
||||
assert.equal(s.pendingQueryKey, 'q1');
|
||||
assert.equal(s.error, '');
|
||||
});
|
||||
|
||||
test('refine оставляет прежние строки до первого ответа нового поколения', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load');
|
||||
s = page(s, 1, 0, ['a', 'b'], 2);
|
||||
s = svod2PagingReducer(s, { type: 'facets', generation: 1, facets: { total: 2 } });
|
||||
s = svod2PagingReducer(s, { type: 'settled', generation: 1 });
|
||||
s = begin(s, 2, 'refine', 'k2');
|
||||
assert.deepEqual(s.pages.get(0), ['a', 'b']);
|
||||
assert.equal(s.total, 2);
|
||||
assert.deepEqual(s.facets, { total: 2 });
|
||||
assert.equal(s.loading, false);
|
||||
assert.equal(s.pendingQueryKey, 'k2');
|
||||
s = page(s, 2, 0, ['c'], 1);
|
||||
assert.deepEqual([...s.pages.keys()], [0]);
|
||||
assert.deepEqual(s.pages.get(0), ['c']);
|
||||
assert.equal(s.dataGeneration, 2);
|
||||
});
|
||||
|
||||
test('первый ответ поколения ставит loadedQueryKey', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load', 'k');
|
||||
assert.equal(s.loadedQueryKey, '');
|
||||
s = page(s, 1, 0, ['a'], 1);
|
||||
assert.equal(s.loadedQueryKey, 'k');
|
||||
assert.equal(s.dataGeneration, 1);
|
||||
});
|
||||
|
||||
test('ошибка догрузки (scroll) не меняет состояние, страница снова среди недостающих', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load');
|
||||
s = page(s, 1, 0, ['a'], 600);
|
||||
assert.equal(svod2PagingReducer(s, { type: 'fail', generation: s.generation, mode: 'scroll', message: 'x' }), s);
|
||||
assert.deepEqual(getMissingPages([0, 1], s.pages, new Set()), [1]);
|
||||
});
|
||||
|
||||
test('ошибка load очищает строки и ставит error', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load');
|
||||
s = page(s, 1, 0, ['a'], 5);
|
||||
s = svod2PagingReducer(s, { type: 'fail', generation: 1, mode: 'load', message: 'x' });
|
||||
assert.equal(s.error, 'x');
|
||||
assert.equal(s.pages.size, 0);
|
||||
assert.equal(s.total, 0);
|
||||
});
|
||||
|
||||
test('ошибка refine очищает строки и ставит error', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load');
|
||||
s = page(s, 1, 0, ['a'], 5);
|
||||
s = begin(s, 2, 'refine');
|
||||
s = svod2PagingReducer(s, { type: 'fail', generation: 2, mode: 'refine', message: 'y' });
|
||||
assert.equal(s.error, 'y');
|
||||
assert.equal(s.pages.size, 0);
|
||||
assert.equal(s.total, 0);
|
||||
});
|
||||
|
||||
test('последний total поколения побеждает', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load');
|
||||
s = page(s, 1, 0, ['a'], 5563);
|
||||
s = page(s, 1, 1, ['b'], 5560);
|
||||
assert.equal(s.total, 5560);
|
||||
assert.equal(s.pages.size, 2);
|
||||
});
|
||||
|
||||
test('facets и settled текущего поколения', () => {
|
||||
let s = begin(createSheetPagingState(), 1, 'load');
|
||||
s = svod2PagingReducer(s, { type: 'facets', generation: 1, facets: { total: 3 } });
|
||||
assert.deepEqual(s.facets, { total: 3 });
|
||||
s = svod2PagingReducer(s, { type: 'settled', generation: 1 });
|
||||
assert.equal(s.loading, false);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user