mirror of
https://github.com/Donkie/Spoolman.git
synced 2026-10-19 00:29:46 -05:00
Remember the Library page size
This commit is contained in:
1 parent
b7a282d2eb
commit
5fc8e6cf42
4 files changed
+70
-16
No files matched your search
@@ -236,3 +236,21 @@ describe('lot number visibility', () => {
|
||||
expect(await hrefFor(stored, '?lots=0')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('page size', () => {
|
||||
const DEFAULT_VIEW = '{"group":"filament","sort":"last_used","asc":false';
|
||||
|
||||
it('restores a stored page size on a bare URL', async () => {
|
||||
expect(await hrefFor(`${DEFAULT_VIEW},"size":100}`, '')).toBe('/?size=100');
|
||||
});
|
||||
|
||||
it('lets an explicit URL page size win', async () => {
|
||||
expect(await hrefFor(`${DEFAULT_VIEW},"size":100}`, '?size=50')).toBeNull();
|
||||
});
|
||||
|
||||
it('falls back to the default for an old stored view or an invalid size', async () => {
|
||||
expect(await hrefFor(`${DEFAULT_VIEW}}`, '')).toBeNull();
|
||||
expect(await hrefFor(`${DEFAULT_VIEW},"size":"100"}`, '')).toBeNull();
|
||||
expect(await hrefFor(`${DEFAULT_VIEW},"size":-5}`, '')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -64,7 +64,7 @@ const VIEWS: LibraryView[] = ['spools', 'filaments', 'manufacturers'];
|
||||
* holds. A URL naming none of them is the one that defers to the remembered
|
||||
* view; naming any of them describes a view of its own, which is taken whole so
|
||||
* a stored grouping never gets spliced onto a link's sort. */
|
||||
const VIEW_PARAMS = ['view', 'group', 'sort', 'dir', 'empty', 'lots'];
|
||||
const VIEW_PARAMS = ['view', 'group', 'sort', 'dir', 'empty', 'lots', 'size'];
|
||||
|
||||
const DEFAULTS = {
|
||||
group: 'filament' as GroupMode,
|
||||
@@ -229,7 +229,7 @@ function currentState(): LibraryState {
|
||||
|
||||
/**
|
||||
* Where entering the Library on `url` should actually land: the same view with
|
||||
* the remembered grouping and sort spelled out in the query string, or null when
|
||||
* the remembered grouping, sort and page size spelled out in the query string, or null when
|
||||
* the URL already describes a view (or there's nothing worth restoring). The
|
||||
* Library page navigates there on entry, replacing the history entry — see
|
||||
* routes/+page.svelte.
|
||||
@@ -258,7 +258,8 @@ export function rememberedViewHref(url: URL): string | null {
|
||||
stored.sortKey === DEFAULTS.sortKey &&
|
||||
stored.sortAsc === DEFAULTS.sortAsc &&
|
||||
stored.showEmpty === DEFAULTS.showEmpty &&
|
||||
stored.showLotNumbers === DEFAULTS.showLotNumbers
|
||||
stored.showLotNumbers === DEFAULTS.showLotNumbers &&
|
||||
stored.pageSize === DEFAULTS.pageSize
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -272,7 +273,8 @@ export function rememberedViewHref(url: URL): string | null {
|
||||
sortKey: stored.sortKey,
|
||||
sortAsc: stored.sortAsc,
|
||||
showEmpty: stored.showEmpty,
|
||||
showLotNumbers: stored.showLotNumbers
|
||||
showLotNumbers: stored.showLotNumbers,
|
||||
pageSize: stored.pageSize
|
||||
});
|
||||
return `${url.pathname}?${qs}`;
|
||||
}
|
||||
@@ -297,7 +299,8 @@ function navigate(next: LibraryState, replace = false): void {
|
||||
sortKey: next.sortKey,
|
||||
sortAsc: next.sortAsc,
|
||||
showEmpty: next.showEmpty,
|
||||
showLotNumbers: next.showLotNumbers
|
||||
showLotNumbers: next.showLotNumbers,
|
||||
pageSize: next.pageSize
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ describe('parseStoredView', () => {
|
||||
sortKey: 'name',
|
||||
sortAsc: true,
|
||||
showEmpty: false,
|
||||
showLotNumbers: false
|
||||
showLotNumbers: false,
|
||||
pageSize: 20
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +25,8 @@ describe('parseStoredView', () => {
|
||||
sortKey: 'name',
|
||||
sortAsc: true,
|
||||
showEmpty: true,
|
||||
showLotNumbers: false
|
||||
showLotNumbers: false,
|
||||
pageSize: 20
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,7 +59,8 @@ describe('parseStoredView', () => {
|
||||
sortKey: 'name',
|
||||
sortAsc: false,
|
||||
showEmpty: false,
|
||||
showLotNumbers: false
|
||||
showLotNumbers: false,
|
||||
pageSize: 20
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -72,7 +75,14 @@ it('round-trips the lot number preference through storage', () => {
|
||||
stored = value;
|
||||
}
|
||||
});
|
||||
const view = { group: 'filament', sortKey: 'name', sortAsc: true, showEmpty: false, showLotNumbers: true };
|
||||
const view = {
|
||||
group: 'filament',
|
||||
sortKey: 'name',
|
||||
sortAsc: true,
|
||||
showEmpty: false,
|
||||
showLotNumbers: true,
|
||||
pageSize: 20
|
||||
};
|
||||
rememberView(view);
|
||||
expect(parseStoredView(stored)).toEqual(view);
|
||||
rememberView({ ...view, showLotNumbers: false });
|
||||
@@ -84,3 +94,12 @@ it('defaults lot numbers off for an old stored view with empty enabled', () => {
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('round-trips the page size and defaults it for old or invalid entries', () => {
|
||||
const old = '{"group":"filament","sort":"name","asc":true';
|
||||
expect(parseStoredView(`${old},"size":100}`)?.pageSize).toBe(100);
|
||||
expect(parseStoredView(`${old}}`)?.pageSize).toBe(20);
|
||||
expect(parseStoredView(`${old},"size":"100"}`)?.pageSize).toBe(20);
|
||||
expect(parseStoredView(`${old},"size":0}`)?.pageSize).toBe(20);
|
||||
expect(parseStoredView(`${old},"size":2.5}`)?.pageSize).toBe(20);
|
||||
});
|
||||
@@ -16,13 +16,16 @@
|
||||
// cache, one URL per distinct view — depends on the address bar being the whole
|
||||
// truth about what's on screen.
|
||||
//
|
||||
// Only grouping and sort are remembered, plus toggles that ADD information
|
||||
// (showEmpty adds rows, showLotNumbers adds text). Filters, search and archived-
|
||||
// Only grouping and sort are remembered, the page size (a layout choice that
|
||||
// hides nothing, it just sets how many rows load at once), plus toggles that ADD
|
||||
// information (showEmpty adds rows, showLotNumbers adds text). Filters, search and archived-
|
||||
// visibility hide spools, and a hidden filter silently restored days later reads
|
||||
// as missing data rather than as a preference. Showing empty filaments or lot
|
||||
// numbers cannot produce that confusion -- restoring them can only put more on
|
||||
// screen, never less -- so they are remembered while the hiding toggles are not.
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
const KEY = 'spoolman-v2-library-view';
|
||||
|
||||
/**
|
||||
@@ -36,6 +39,7 @@ export interface StoredView {
|
||||
sortAsc: boolean;
|
||||
showEmpty: boolean;
|
||||
showLotNumbers: boolean;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
/** Rebuild a stored view from its JSON, or null if there isn't a usable one. */
|
||||
@@ -44,15 +48,23 @@ export function parseStoredView(raw: string | null): StoredView | null {
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(raw);
|
||||
if (typeof parsed !== 'object' || parsed === null) return null;
|
||||
const { group, sort, asc, empty, lots } = parsed as Record<string, unknown>;
|
||||
const { group, sort, asc, empty, lots, size } = parsed as Record<string, unknown>;
|
||||
if (typeof group !== 'string' || typeof sort !== 'string' || typeof asc !== 'boolean') {
|
||||
return null;
|
||||
}
|
||||
// `empty` and `lots` arrived after this key shipped, so an entry written
|
||||
// `empty`, `lots` and `size` arrived after this key shipped, so an entry written
|
||||
// by an older build simply has no such fields. Treating that as "off" keeps those
|
||||
// entries valid; rejecting them would throw away a grouping the user
|
||||
// picked long ago the first time they loaded a new version.
|
||||
return { group, sortKey: sort, sortAsc: asc, showEmpty: empty === true, showLotNumbers: lots === true };
|
||||
return {
|
||||
group,
|
||||
sortKey: sort,
|
||||
sortAsc: asc,
|
||||
showEmpty: empty === true,
|
||||
showLotNumbers: lots === true,
|
||||
// An absent or unusable size is the default, not a reason to drop the entry.
|
||||
pageSize: typeof size === 'number' && Number.isInteger(size) && size > 0 ? size : DEFAULT_PAGE_SIZE
|
||||
};
|
||||
} catch {
|
||||
// Corrupt entry: the shipped view is no worse than what a first-time
|
||||
// visitor gets.
|
||||
@@ -82,7 +94,8 @@ export function rememberView(view: StoredView): void {
|
||||
prev.sortKey === view.sortKey &&
|
||||
prev.sortAsc === view.sortAsc &&
|
||||
prev.showEmpty === view.showEmpty &&
|
||||
prev.showLotNumbers === view.showLotNumbers
|
||||
prev.showLotNumbers === view.showLotNumbers &&
|
||||
prev.pageSize === view.pageSize
|
||||
) {
|
||||
// Selecting a spool or turning a page navigates too; only the changes that
|
||||
// actually move the view are worth a synchronous localStorage write.
|
||||
@@ -112,7 +125,8 @@ function write(view: StoredView): void {
|
||||
sort: view.sortKey,
|
||||
asc: view.sortAsc,
|
||||
empty: view.showEmpty,
|
||||
lots: view.showLotNumbers
|
||||
lots: view.showLotNumbers,
|
||||
size: view.pageSize
|
||||
})
|
||||
);
|
||||
} catch {
|
||||
|
||||
Reference in new issue
Block a user