mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Feat: Longstrip Viewer(s) & Lag Improvements
This commit is contained in:
@@ -2,6 +2,7 @@ import type { Manga, Chapter } from "$lib/types";
|
||||
import type { BookmarkEntry, MarkerEntry, MarkerColor } from "$lib/types/history";
|
||||
import type { MangaPrefs, ReaderSettings, ReaderPreset } from "$lib/types/settings";
|
||||
import { settingsState, updateSettings } from "$lib/state/settings.svelte";
|
||||
import { seriesState } from "$lib/state/series.svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
export const PAGE_STYLES = ["single", "fade", "double", "longstrip"] as const;
|
||||
@@ -30,9 +31,12 @@ export interface StripChapter {
|
||||
}
|
||||
|
||||
class ReaderState {
|
||||
activeManga = $state<Manga | null>(null);
|
||||
activeChapter = $state<Chapter | null>(null);
|
||||
activeChapterList = $state<Chapter[]>([]);
|
||||
get activeManga() { return seriesState.activeManga; }
|
||||
set activeManga(v: Manga | null) { seriesState.activeManga = v; }
|
||||
|
||||
get activeChapter() { return seriesState.activeChapter; }
|
||||
set activeChapter(v: Chapter | null){ seriesState.activeChapter = v; }
|
||||
|
||||
pageUrls = $state<string[]>([]);
|
||||
pageNumber = $state(1);
|
||||
bookmarks = $state<BookmarkEntry[]>([]);
|
||||
@@ -77,19 +81,19 @@ class ReaderState {
|
||||
|
||||
containerWidth = $state(0);
|
||||
|
||||
readonly activeChapterList = $derived(seriesState.readerChapterList);
|
||||
|
||||
get settings() { return settingsState.settings; }
|
||||
|
||||
openReader(chapter: Chapter, chapterList: Chapter[], manga?: Manga | null) {
|
||||
openReader(chapter: Chapter, manga?: Manga | null) {
|
||||
const isChapterNav = this.activeChapter !== null;
|
||||
this.activeChapter = chapter;
|
||||
this.activeChapterList = chapterList;
|
||||
this.activeChapter = chapter;
|
||||
if (manga !== undefined) this.activeManga = manga;
|
||||
goto(`/reader/${this.activeManga!.id}/${chapter.id}`, { replaceState: isChapterNav });
|
||||
}
|
||||
|
||||
closeReader() {
|
||||
this.activeChapter = null;
|
||||
this.activeChapterList = [];
|
||||
this.activeChapter = null;
|
||||
history.back();
|
||||
}
|
||||
|
||||
@@ -224,5 +228,5 @@ export const DEFAULT_MANGA_PREFS: MangaPrefs = {
|
||||
|
||||
export const readerState = new ReaderState();
|
||||
|
||||
export function openReader(ch: Chapter, list: Chapter[], manga?: Manga | null) { readerState.openReader(ch, list, manga); }
|
||||
export function closeReader() { readerState.closeReader(); }
|
||||
export function openReader(ch: Chapter, manga?: Manga | null) { readerState.openReader(ch, manga); }
|
||||
export function closeReader() { readerState.closeReader(); }
|
||||
+220
-117
@@ -1,99 +1,18 @@
|
||||
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";
|
||||
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 { getAdapter } from '$lib/request-manager'
|
||||
import { buildChapterList } from '$lib/components/series/lib/chapterList'
|
||||
import { goto } from '$app/navigation'
|
||||
|
||||
export type { BookmarkEntry, MarkerEntry, MarkerColor } from "$lib/types/history";
|
||||
export type { MangaPrefs } from "$lib/types/settings";
|
||||
|
||||
class SeriesStore {
|
||||
current = $state<Manga | null>(null);
|
||||
loading = $state(false);
|
||||
error = $state<string | null>(null);
|
||||
|
||||
chapters = $state<Chapter[]>([]);
|
||||
chaptersLoading = $state(false);
|
||||
chaptersError = $state<string | null>(null);
|
||||
|
||||
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 type { BookmarkEntry, MarkerEntry, MarkerColor } from '$lib/types/history'
|
||||
export type { MangaPrefs } from '$lib/types/settings'
|
||||
|
||||
export const DEFAULT_MANGA_PREFS: MangaPrefs = {
|
||||
sortMode: "source",
|
||||
sortDir: "asc",
|
||||
preferredScanlator: "",
|
||||
sortMode: 'source',
|
||||
sortDir: 'asc',
|
||||
preferredScanlator: '',
|
||||
scanlatorFilter: [],
|
||||
scanlatorBlacklist: [],
|
||||
scanlatorForce: false,
|
||||
@@ -103,30 +22,214 @@ export const DEFAULT_MANGA_PREFS: MangaPrefs = {
|
||||
deleteOnRead: false,
|
||||
deleteDelayHours: 0,
|
||||
pauseUpdates: false,
|
||||
refreshInterval: "global",
|
||||
coverUrl: "",
|
||||
};
|
||||
refreshInterval: 'global',
|
||||
coverUrl: '',
|
||||
}
|
||||
|
||||
export const seriesState = new SeriesStore();
|
||||
const CHAPTER_TTL_MS = 2 * 60 * 1000
|
||||
|
||||
export const seriesStore = seriesState;
|
||||
class SeriesStore {
|
||||
activeManga = $state<Manga | null>(null)
|
||||
previewManga = $state<Manga | null>(null)
|
||||
activeChapter = $state<Chapter | null>(null)
|
||||
bookmarks = $state<BookmarkEntry[]>([])
|
||||
markers = $state<MarkerEntry[]>([])
|
||||
acknowledgedUpdates = $state<Set<number>>(new Set())
|
||||
|
||||
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); }
|
||||
#rawChapters = $state<Map<number, Chapter[]>>(new Map())
|
||||
#fetchedAt = new Map<number, number>()
|
||||
#abortCtrls = new Map<number, AbortController>()
|
||||
#loading = $state<Set<number>>(new Set())
|
||||
#errors = $state<Map<number, string>>(new Map())
|
||||
|
||||
readonly activeChapterList = $derived.by(() => {
|
||||
const id = this.activeManga?.id
|
||||
if (id == null) return []
|
||||
const raw = this.#rawChapters.get(id) ?? []
|
||||
const prefs = settingsState.settings.mangaPrefs?.[id] ?? {}
|
||||
return buildChapterList(raw, {
|
||||
sortMode: (prefs.sortMode ?? DEFAULT_MANGA_PREFS.sortMode) as MangaPrefs['sortMode'],
|
||||
sortDir: (prefs.sortDir ?? DEFAULT_MANGA_PREFS.sortDir) as MangaPrefs['sortDir'],
|
||||
preferredScanlator: (prefs.preferredScanlator ?? DEFAULT_MANGA_PREFS.preferredScanlator) as string,
|
||||
scanlatorFilter: (prefs.scanlatorFilter ?? DEFAULT_MANGA_PREFS.scanlatorFilter) as string[],
|
||||
scanlatorBlacklist: (prefs.scanlatorBlacklist ?? DEFAULT_MANGA_PREFS.scanlatorBlacklist) as string[],
|
||||
scanlatorForce: (prefs.scanlatorForce ?? DEFAULT_MANGA_PREFS.scanlatorForce) as boolean,
|
||||
})
|
||||
})
|
||||
|
||||
readonly readerChapterList = $derived.by(() => {
|
||||
const id = this.activeManga?.id
|
||||
if (id == null) return []
|
||||
const raw = this.#rawChapters.get(id) ?? []
|
||||
const prefs = settingsState.settings.mangaPrefs?.[id] ?? {}
|
||||
return buildChapterList(raw, {
|
||||
sortMode: 'source',
|
||||
sortDir: 'asc',
|
||||
preferredScanlator: (prefs.preferredScanlator ?? DEFAULT_MANGA_PREFS.preferredScanlator) as string,
|
||||
scanlatorFilter: (prefs.scanlatorFilter ?? DEFAULT_MANGA_PREFS.scanlatorFilter) as string[],
|
||||
scanlatorBlacklist: (prefs.scanlatorBlacklist ?? DEFAULT_MANGA_PREFS.scanlatorBlacklist) as string[],
|
||||
scanlatorForce: (prefs.scanlatorForce ?? DEFAULT_MANGA_PREFS.scanlatorForce) as boolean,
|
||||
})
|
||||
})
|
||||
|
||||
chaptersFor(mangaId: number): Chapter[] { return this.#rawChapters.get(mangaId) ?? [] }
|
||||
isLoadingChapters(mangaId: number) { return this.#loading.has(mangaId) }
|
||||
chapterError(mangaId: number) { return this.#errors.get(mangaId) ?? null }
|
||||
|
||||
async loadChapters(mangaId: number, { force = false } = {}): Promise<void> {
|
||||
const now = Date.now()
|
||||
const stalest = this.#fetchedAt.get(mangaId) ?? 0
|
||||
const fresh = !force && this.#rawChapters.has(mangaId) && now - stalest < CHAPTER_TTL_MS
|
||||
|
||||
if (fresh) return
|
||||
|
||||
this.#abortCtrls.get(mangaId)?.abort()
|
||||
const ctrl = new AbortController()
|
||||
this.#abortCtrls.set(mangaId, ctrl)
|
||||
|
||||
this.#loading = new Set([...this.#loading, mangaId])
|
||||
this.#errors = new Map(this.#errors)
|
||||
this.#errors.delete(mangaId)
|
||||
|
||||
try {
|
||||
const adapter = getAdapter()
|
||||
let nodes = await adapter.getChapters(String(mangaId), ctrl.signal)
|
||||
|
||||
if (!ctrl.signal.aborted && nodes.length === 0) {
|
||||
const fetched = await adapter.fetchChapters(String(mangaId), ctrl.signal)
|
||||
if (!ctrl.signal.aborted) nodes = fetched
|
||||
}
|
||||
|
||||
if (ctrl.signal.aborted) return
|
||||
|
||||
this.#rawChapters = new Map(this.#rawChapters).set(mangaId, nodes)
|
||||
this.#fetchedAt.set(mangaId, Date.now())
|
||||
} catch (e: unknown) {
|
||||
if ((e as { name?: string }).name === 'AbortError') return
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
this.#errors = new Map(this.#errors).set(mangaId, msg)
|
||||
} finally {
|
||||
if (!ctrl.signal.aborted) {
|
||||
const next = new Set(this.#loading)
|
||||
next.delete(mangaId)
|
||||
this.#loading = next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
invalidateChapters(mangaId: number) {
|
||||
this.#fetchedAt.delete(mangaId)
|
||||
}
|
||||
|
||||
patchChapters(mangaId: number, updater: (chapters: Chapter[]) => Chapter[]) {
|
||||
const current = this.#rawChapters.get(mangaId)
|
||||
if (!current) return
|
||||
this.#rawChapters = new Map(this.#rawChapters).set(mangaId, updater(current))
|
||||
}
|
||||
|
||||
setActiveManga(manga: Manga | null) {
|
||||
this.activeManga = manga
|
||||
}
|
||||
|
||||
setPreviewManga(manga: Manga | null) {
|
||||
this.previewManga = manga
|
||||
}
|
||||
|
||||
openReaderForChapter(chapter: Chapter, manga?: Manga | null) {
|
||||
if (manga !== undefined) this.activeManga = manga
|
||||
const mangaId = this.activeManga?.id
|
||||
if (!mangaId) return
|
||||
|
||||
const list = this.readerChapterList
|
||||
const prefs = settingsState.settings.mangaPrefs?.[mangaId] ?? {}
|
||||
const ahead = (prefs.downloadAhead ?? DEFAULT_MANGA_PREFS.downloadAhead) as number
|
||||
|
||||
if (ahead > 0) {
|
||||
const idx = list.findIndex(c => c.id === chapter.id)
|
||||
if (idx >= 0) {
|
||||
const toQueue = list
|
||||
.slice(idx + 1, idx + 1 + ahead)
|
||||
.filter(c => !c.downloaded && !c.read)
|
||||
.map(c => String(c.id))
|
||||
if (toQueue.length) getAdapter().enqueueDownloads(toQueue).catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
this.activeChapter = chapter
|
||||
goto(`/reader/${mangaId}/${chapter.id}`)
|
||||
}
|
||||
|
||||
closeReader() {
|
||||
this.activeChapter = null
|
||||
}
|
||||
|
||||
acknowledgeUpdate(mangaId: number) {
|
||||
if (this.acknowledgedUpdates.has(mangaId)) return
|
||||
this.acknowledgedUpdates = new Set([...this.acknowledgedUpdates, 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 },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
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) }
|
||||
|
||||
get settings() { return settingsState.settings }
|
||||
}
|
||||
|
||||
export const seriesState = new SeriesStore()
|
||||
export const seriesStore = seriesState
|
||||
|
||||
export function setActiveManga(next: Manga | null) { seriesState.setActiveManga(next) }
|
||||
export function setPreviewManga(next: Manga | null) { seriesState.setPreviewManga(next) }
|
||||
export function openReaderForChapter(ch: Chapter, manga?: Manga | null) { seriesState.openReaderForChapter(ch, 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, v: MangaPrefs[K]) { seriesState.setPref(mangaId, key, v) }
|
||||
@@ -19,7 +19,6 @@ class TrackingState {
|
||||
loadingFor: Set<number> = $state(new Set())
|
||||
error: string | null = $state(null)
|
||||
|
||||
// Legacy flat fields kept for request-manager/tracking.ts compatibility
|
||||
trackers: Tracker[] = $state([])
|
||||
loading: boolean = $state(false)
|
||||
syncing: boolean = $state(false)
|
||||
@@ -55,7 +54,6 @@ class TrackingState {
|
||||
}))
|
||||
}
|
||||
|
||||
// ── Per-manga load ──────────────────────────────────────────────────────────
|
||||
|
||||
async loadForManga(mangaId: number) {
|
||||
if (this.loadingFor.has(mangaId)) return
|
||||
@@ -78,15 +76,13 @@ class TrackingState {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Global load (tracking page) ─────────────────────────────────────────────
|
||||
|
||||
async loadAll() {
|
||||
this.loadingAll = true
|
||||
this.error = null
|
||||
try {
|
||||
const trackers = await getAdapter().getAllTrackerRecords() as TrackerWithRecords[]
|
||||
this.allTrackers = trackers
|
||||
this.trackers = trackers // keep flat field in sync
|
||||
this.trackers = trackers
|
||||
|
||||
for (const tracker of trackers.filter((t) => t.isLoggedIn)) {
|
||||
for (const record of tracker.trackRecords.nodes) {
|
||||
@@ -104,8 +100,6 @@ class TrackingState {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Field updates ───────────────────────────────────────────────────────────
|
||||
|
||||
async updateStatus(mangaId: number, record: TrackRecord, status: number): Promise<TrackRecord> {
|
||||
const fresh = await getAdapter().updateTrackRecord(String(record.id), { status })
|
||||
this.patchFor(mangaId, fresh)
|
||||
@@ -134,7 +128,6 @@ class TrackingState {
|
||||
}))
|
||||
}
|
||||
|
||||
// ── Remote sync ─────────────────────────────────────────────────────────────
|
||||
|
||||
async syncFromRemote(
|
||||
mangaId: number,
|
||||
@@ -168,7 +161,6 @@ class TrackingState {
|
||||
)
|
||||
}
|
||||
|
||||
// ── Read/unread sync ────────────────────────────────────────────────────────
|
||||
|
||||
async updateFromRead(
|
||||
mangaId: number,
|
||||
@@ -232,7 +224,6 @@ class TrackingState {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Boot sync ───────────────────────────────────────────────────────────────
|
||||
|
||||
async bootSync() {
|
||||
if (!settingsState.settings.trackerSyncBack) return
|
||||
@@ -297,7 +288,6 @@ class TrackingState {
|
||||
this.byManga = next
|
||||
}
|
||||
|
||||
// ── Status helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
private _statusesFor(trackerId: number): { value: number; name: string }[] {
|
||||
return this.allTrackers.find((t) => t.id === trackerId)?.statuses ?? []
|
||||
|
||||
Reference in New Issue
Block a user