Initial commit
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user