Files
chitai/frontend/src/lib/state/library.svelte.ts
T
patrick d6207b5743 refactor: resolve() internal links instead of plain hrefs
Type-checks every link against the real route tree. Caught a dead one: the
book page linked tags to /tag/{id}, a route that has never existed.
2026-08-12 11:08:09 -04:00

87 lines
2.7 KiB
TypeScript

import { resolve } from '$app/paths';
import { browser } from '$app/environment';
import { goto, invalidate } from '$app/navigation';
import { page } from '$app/state';
import { deleteLibrary } from '$lib/api';
import type { Library } from '$lib/schema';
import { getContext, setContext } from 'svelte';
import { toast } from 'svelte-sonner';
export class LibraryState {
libraries = $state<Library[]>([]);
activeLibrary = $state<Library | undefined>(undefined);
createDialogOpen = $state(false);
constructor(libraries: Library[]) {
this.libraries = libraries;
const activeLibrary = this.libraries.find((lib) => lib.id.toString() === page.params.libraryId);
let previousLibrary: Library | undefined;
if (browser) {
previousLibrary = this.libraries.find(
(lib) => lib.id.toString() === localStorage.getItem('previousLibraryId')
);
}
this.activeLibrary = activeLibrary || previousLibrary || libraries[0];
}
async setActive(libraryId: number) {
this.activeLibrary = this.libraries.find((lib) => lib.id === libraryId) || this.libraries[0];
if (browser) {
localStorage.setItem('previousLibraryId', this.activeLibrary.id.toString());
await goto(resolve('/(root)/(library)/library/[libraryId]/view', { libraryId: String(libraryId) }), {
invalidate: ['app:libraries']
});
}
}
updateLibraries(libraries: Library[]) {
this.libraries = libraries;
// Update the current library as it may have been removed
if (!this.libraries.find((lib) => this.activeLibrary?.id === lib.id))
this.activeLibrary = libraries[0] || undefined;
}
async deleteLibrary(libraryId: number) {
// Store library with its original index
const libIndex = this.libraries.findIndex((lib) => lib.id === libraryId);
const deletionSnapshot = { library: this.libraries[libIndex], index: libIndex };
// Optimistically delete the library
this.libraries = this.libraries.filter((lib) => lib.id !== libraryId);
try {
// Delete library via API
await deleteLibrary(libraryId);
toast.success(`Deleted library '${deletionSnapshot.library}'`);
} catch (err) {
// Delete failed; restore library at its original position
this.libraries.splice(deletionSnapshot.index, 0, deletionSnapshot.library);
toast.error('Failed to delete library');
console.error('Failed to delete library: ', err);
}
}
addLibrary(library: Library) {
this.libraries.push(library);
}
openLibraryCreateDialog() {
this.createDialogOpen = true;
}
}
const LIBRARY_KEY = Symbol('LIBRARY');
export function setLibraryState(libraries: Library[]) {
return setContext(LIBRARY_KEY, new LibraryState(libraries));
}
export function getLibraryState() {
return getContext<ReturnType<typeof setLibraryState>>(LIBRARY_KEY);
}