mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 17:29:55 -05:00
Chore: Port over SeriesDetail (WIP Panels)
This commit is contained in:
+126
-22
@@ -1,28 +1,132 @@
|
||||
import type { Manga, Chapter } from '$lib/types'
|
||||
import type { Manga, Chapter } from "$lib/types";
|
||||
import type { BookmarkEntry, MarkerEntry, MarkerColor } from "$lib/types/history";
|
||||
import type { MangaPrefs } from "$lib/types/settings";
|
||||
import { settingsState, updateSettings } from "$lib/state/settings.svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
class SeriesState {
|
||||
current = $state<Manga | null>(null)
|
||||
loading = $state(false)
|
||||
error = $state<string | null>(null)
|
||||
export type { BookmarkEntry, MarkerEntry, MarkerColor } from "$lib/types/history";
|
||||
export type { MangaPrefs } from "$lib/types/settings";
|
||||
|
||||
chapters = $state<Chapter[]>([])
|
||||
chaptersLoading = $state(false)
|
||||
chaptersError = $state<string | null>(null)
|
||||
class SeriesStore {
|
||||
current = $state<Manga | null>(null);
|
||||
loading = $state(false);
|
||||
error = $state<string | null>(null);
|
||||
|
||||
chapterSortDesc = $state(true)
|
||||
chapterFilter = $state({ unread: false, downloaded: false, query: '' })
|
||||
chapters = $state<Chapter[]>([]);
|
||||
chaptersLoading = $state(false);
|
||||
chaptersError = $state<string | null>(null);
|
||||
|
||||
filteredChapters = $derived.by(() => {
|
||||
let result = this.chapters
|
||||
if (this.chapterFilter.unread) result = result.filter(c => !c.read)
|
||||
if (this.chapterFilter.downloaded) result = result.filter(c => c.downloaded)
|
||||
if (this.chapterFilter.query) {
|
||||
const q = this.chapterFilter.query.toLowerCase()
|
||||
result = result.filter(c => c.name.toLowerCase().includes(q))
|
||||
}
|
||||
const sorted = [...result].sort((a, b) => a.chapterNumber - b.chapterNumber)
|
||||
return this.chapterSortDesc ? sorted.reverse() : sorted
|
||||
})
|
||||
activeMangaId = $state<number | null>(null);
|
||||
activeManga = $state<Manga | null>(null);
|
||||
previewManga = $state<Manga | null>(null);
|
||||
activeChapter = $state<Chapter | null>(null);
|
||||
activeChapterList = $state<Chapter[]>([]);
|
||||
bookmarks = $state<BookmarkEntry[]>([]);
|
||||
markers = $state<MarkerEntry[]>([]);
|
||||
acknowledgedUpdates = $state<Set<number>>(new Set());
|
||||
|
||||
setActiveMangaId(next: number | null) { this.activeMangaId = next; }
|
||||
setActiveManga(next: Manga | null) { this.activeManga = next; }
|
||||
setPreviewManga(next: Manga | null) { this.previewManga = next; }
|
||||
|
||||
openReader(chapter: Chapter, chapterList: Chapter[], manga?: Manga | null) {
|
||||
this.activeChapter = chapter;
|
||||
this.activeChapterList = chapterList;
|
||||
if (manga !== undefined) this.activeManga = manga;
|
||||
goto(`/reader/${this.activeManga!.id}/${chapter.id}`);
|
||||
}
|
||||
|
||||
closeReader() {
|
||||
this.activeChapter = null;
|
||||
this.activeChapterList = [];
|
||||
}
|
||||
|
||||
acknowledgeUpdate(mangaId: number) {
|
||||
if (this.acknowledgedUpdates.has(mangaId)) return;
|
||||
this.acknowledgedUpdates = new Set([...this.acknowledgedUpdates, mangaId]);
|
||||
}
|
||||
|
||||
addBookmark(entry: Omit<BookmarkEntry, "savedAt">, label?: string) {
|
||||
this.bookmarks = [
|
||||
{ ...entry, savedAt: Date.now(), label },
|
||||
...this.bookmarks.filter(b => b.chapterId !== entry.chapterId),
|
||||
].slice(0, 200);
|
||||
}
|
||||
|
||||
removeBookmark(chapterId: number) { this.bookmarks = this.bookmarks.filter(b => b.chapterId !== chapterId); }
|
||||
clearBookmarks() { this.bookmarks = []; }
|
||||
getBookmark(chapterId: number) { return this.bookmarks.find(b => b.chapterId === chapterId); }
|
||||
|
||||
addMarker(entry: Omit<MarkerEntry, "id" | "createdAt">): string {
|
||||
const id = Math.random().toString(36).slice(2);
|
||||
this.markers = [...this.markers, { ...entry, id, createdAt: Date.now() }];
|
||||
return id;
|
||||
}
|
||||
|
||||
updateMarker(id: string, patch: Partial<Pick<MarkerEntry, "note" | "color">>) {
|
||||
this.markers = this.markers.map(m => m.id === id ? { ...m, ...patch, updatedAt: Date.now() } : m);
|
||||
}
|
||||
|
||||
removeMarker(id: string) { this.markers = this.markers.filter(m => m.id !== id); }
|
||||
getMarkersForPage(chapterId: number, page: number) { return this.markers.filter(m => m.chapterId === chapterId && m.pageNumber === page); }
|
||||
getMarkersForChapter(chapterId: number) { return this.markers.filter(m => m.chapterId === chapterId); }
|
||||
getMarkersForManga(mangaId: number) { return this.markers.filter(m => m.mangaId === mangaId); }
|
||||
clearMarkersForManga(mangaId: number) { this.markers = this.markers.filter(m => m.mangaId !== mangaId); }
|
||||
|
||||
getPref<K extends keyof MangaPrefs>(mangaId: number, key: K): MangaPrefs[K] {
|
||||
const prefs = settingsState.settings.mangaPrefs?.[mangaId] ?? {};
|
||||
return (prefs[key] ?? DEFAULT_MANGA_PREFS[key]) as MangaPrefs[K];
|
||||
}
|
||||
|
||||
setPref<K extends keyof MangaPrefs>(mangaId: number, key: K, value: MangaPrefs[K]) {
|
||||
updateSettings({
|
||||
mangaPrefs: {
|
||||
...settingsState.settings.mangaPrefs,
|
||||
[mangaId]: { ...(settingsState.settings.mangaPrefs?.[mangaId] ?? {}), [key]: value },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
get settings() { return settingsState.settings; }
|
||||
}
|
||||
|
||||
export const seriesState = new SeriesState()
|
||||
export const DEFAULT_MANGA_PREFS: MangaPrefs = {
|
||||
sortMode: "source",
|
||||
sortDir: "asc",
|
||||
preferredScanlator: "",
|
||||
scanlatorFilter: [],
|
||||
scanlatorBlacklist: [],
|
||||
scanlatorForce: false,
|
||||
autoDownload: false,
|
||||
downloadAhead: 0,
|
||||
maxKeepChapters: 0,
|
||||
deleteOnRead: false,
|
||||
deleteDelayHours: 0,
|
||||
pauseUpdates: false,
|
||||
refreshInterval: "global",
|
||||
coverUrl: "",
|
||||
};
|
||||
|
||||
export const seriesState = new SeriesStore();
|
||||
|
||||
export const seriesStore = seriesState;
|
||||
|
||||
export function setActiveMangaId(next: number | null) { seriesState.setActiveMangaId(next); }
|
||||
export function setActiveManga(next: Manga | null) { seriesState.setActiveManga(next); }
|
||||
export function setPreviewManga(next: Manga | null) { seriesState.setPreviewManga(next); }
|
||||
export function openReader(ch: Chapter, list: Chapter[], manga?: Manga | null) { seriesState.openReader(ch, list, manga); }
|
||||
export function closeReader() { seriesState.closeReader(); }
|
||||
export function acknowledgeUpdate(mangaId: number) { seriesState.acknowledgeUpdate(mangaId); }
|
||||
export function addBookmark(entry: Omit<BookmarkEntry, "savedAt">, label?: string) { seriesState.addBookmark(entry, label); }
|
||||
export function removeBookmark(chapterId: number) { seriesState.removeBookmark(chapterId); }
|
||||
export function clearBookmarks() { seriesState.clearBookmarks(); }
|
||||
export function getBookmark(chapterId: number) { return seriesState.getBookmark(chapterId); }
|
||||
export function addMarker(entry: Omit<MarkerEntry, "id" | "createdAt">): string { return seriesState.addMarker(entry); }
|
||||
export function updateMarker(id: string, patch: Partial<Pick<MarkerEntry, "note" | "color">>) { seriesState.updateMarker(id, patch); }
|
||||
export function removeMarker(id: string) { seriesState.removeMarker(id); }
|
||||
export function getMarkersForPage(chapterId: number, page: number) { return seriesState.getMarkersForPage(chapterId, page); }
|
||||
export function getMarkersForChapter(chapterId: number) { return seriesState.getMarkersForChapter(chapterId); }
|
||||
export function getMarkersForManga(mangaId: number) { return seriesState.getMarkersForManga(mangaId); }
|
||||
export function clearMarkersForManga(mangaId: number) { seriesState.clearMarkersForManga(mangaId); }
|
||||
export function getPref<K extends keyof MangaPrefs>(mangaId: number, key: K): MangaPrefs[K] { return seriesState.getPref(mangaId, key); }
|
||||
export function setPref<K extends keyof MangaPrefs>(mangaId: number, key: K, value: MangaPrefs[K]) { seriesState.setPref(mangaId, key, value); }
|
||||
Reference in New Issue
Block a user