Initial commit

This commit is contained in:
hiperman
2025-12-04 00:33:37 -05:00
commit 7ca0a21283
798 changed files with 190424 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
import { goto, invalidate } from '$app/navigation';
import { addBooksToShelf, createBookshelf, listBookshelves, removeBooksFromShelf } from '$lib/api';
import type { Book, Bookshelf } from '$lib/schema';
import { getContext, setContext } from 'svelte';
import { toast } from 'svelte-sonner';
import { SvelteMap } from 'svelte/reactivity';
export class BookshelfState {
readonly libraryBookshelves = new SvelteMap<string, Bookshelf[]>();
getBookshelves(libraryId: string | number): Bookshelf[] | undefined {
const id = libraryId.toString();
if (!this.libraryBookshelves.has(id)) {
this.fetchBookshelves(id).then(shelves => {
this.libraryBookshelves.set(id, shelves);
});
}
return this.libraryBookshelves.get(id);
}
async fetchBookshelves(libraryId: string) {
try {
let paginatedBookshelves = await listBookshelves({
libraries: [libraryId]
});
return paginatedBookshelves.items;
} catch (error) {
console.error('Failed to retrieve bookshelves: ', error);
}
}
async addBookshelf(name: string, libraryId?: string | number, booksToAdd?: string[] | number[]) {
try {
let bookshelf = await createBookshelf({
title: name,
library_id: libraryId,
book_ids: booksToAdd
})
if (bookshelf.library_id) {
const currentShelves = this.libraryBookshelves.get(bookshelf.library_id.toString()) || [];
this.libraryBookshelves.set(bookshelf.library_id.toString(), [...currentShelves, bookshelf]);
}
invalidate('app:books')
if (booksToAdd?.length)
toast.success(`Added ${booksToAdd.length} books to '${name}'`)
else
toast.success(`Created shelf '${name}'`)
return bookshelf
} catch (error) {
toast.error(`Failed to create bookshelf '${name}'`)
console.error(`Failed to create bookshelf: `, error)
}
}
async addBooksToShelf(shelfId: number | string, bookIds: number[]) {
try {
const shelf = await addBooksToShelf({
shelf_id: shelfId,
book_ids: bookIds
});
this.updateShelf(shelf)
if (bookIds.length === 1) toast.success(`Added book to shelf!`);
else toast.success(`Added ${bookIds.length} books to shelf!`);
} catch (error) {
console.error('Failed to add book(s) to shelf: ', error);
toast.error('Failed to add book(s) to shelf.');
}
}
async removeBooksFromShelf(shelfId: number | string, bookIds: number[]) {
try {
const shelf = await removeBooksFromShelf({
shelf_id: shelfId,
book_ids: bookIds
});
this.updateShelf(shelf)
if (bookIds.length === 1) toast.success(`Removed book from shelf.`);
else toast.success(`Removed ${bookIds.length} books from shelf!`);
} catch (error) {
console.error('Failed to remove book(s) from shelf: ', error);
toast.error('Failed to remove book(s) from shelf.');
}
}
async updateShelf(shelf: Bookshelf) {
if (!shelf.library_id) return;
const bookshelves = this.libraryBookshelves.get(shelf.library_id.toString())
if (!bookshelves) return;
const index = bookshelves.findIndex(bookshelf => bookshelf.id === shelf.id)
if (index !== -1) {
this.libraryBookshelves.set(
shelf.library_id.toString(),
[...bookshelves.slice(0, index), shelf, ...bookshelves.slice(index + 1)]
);
}
}
deletedBooks(books: Book[]) {
// Assume all books are in the same library
const libraryId = books[0].library_id.toString()
const bookshelves = this.libraryBookshelves.get(libraryId);
if (!bookshelves) return;
// Create a Set of shelf IDs that need updating for efficient lookup
const shelfIdsToUpdate = new Set<number>();
books.forEach(book => {
book.lists.forEach(shelf => {
shelfIdsToUpdate.add(shelf.id);
});
});
const updatedBookshelves = bookshelves.map(shelf => {
if (shelfIdsToUpdate.has(shelf.id)) {
// Count how many times this shelf appears across all deleted books
let decrementBy = 0;
books.forEach(book => {
if (book.lists.some(s => s.id === shelf.id)) {
decrementBy++;
}
});
return { ...shelf, total: shelf.total - decrementBy };
}
return shelf;
});
this.libraryBookshelves.set(libraryId, updatedBookshelves);
}
}
const BOOKSHELF_KEY = Symbol('BOOKSHELF');
export function setBookshelfState() {
return setContext(BOOKSHELF_KEY, new BookshelfState());
}
export function getBookshelfState() {
return getContext<ReturnType<typeof setBookshelfState>>(BOOKSHELF_KEY);
}