mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Chore: Restructure Repository for SvelteKit
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
export type AppStatus = 'booting' | 'auth' | 'ready' | 'error'
|
||||
|
||||
export const appState = $state({
|
||||
status: 'booting' as AppStatus,
|
||||
error: null as string | null,
|
||||
serverUrl: '',
|
||||
authenticated: false,
|
||||
platform: 'web' as 'web' | 'tauri' | 'capacitor',
|
||||
version: '',
|
||||
})
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { DownloadItem } from '$lib/server-adapters/types'
|
||||
|
||||
export const downloadsState = $state({
|
||||
items: [] as DownloadItem[],
|
||||
error: null as string | null,
|
||||
})
|
||||
|
||||
export const activeDownloads = $derived(
|
||||
downloadsState.items.filter(d => d.state === 'downloading')
|
||||
)
|
||||
|
||||
export const queuedDownloads = $derived(
|
||||
downloadsState.items.filter(d => d.state === 'queued')
|
||||
)
|
||||
|
||||
export const downloadCount = $derived(downloadsState.items.length)
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { Extension, Source, Manga } from '$lib/types'
|
||||
|
||||
export const extensionsState = $state({
|
||||
items: [] as Extension[],
|
||||
sources: [] as Source[],
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
|
||||
filter: {
|
||||
query: '',
|
||||
installed: false,
|
||||
language: 'all',
|
||||
},
|
||||
|
||||
browseResults: [] as Manga[],
|
||||
browseLoading: false,
|
||||
browseError: null as string | null,
|
||||
browseHasMore: false,
|
||||
})
|
||||
|
||||
export const filteredExtensions = $derived.by(() => {
|
||||
let result = extensionsState.items
|
||||
|
||||
if (extensionsState.filter.installed) {
|
||||
result = result.filter(e => e.installed)
|
||||
}
|
||||
if (extensionsState.filter.language !== 'all') {
|
||||
result = result.filter(e => e.lang === extensionsState.filter.language)
|
||||
}
|
||||
if (extensionsState.filter.query) {
|
||||
const q = extensionsState.filter.query.toLowerCase()
|
||||
result = result.filter(e => e.name.toLowerCase().includes(q))
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { Manga } from '$lib/types'
|
||||
import type { MangaStatus } from '$lib/server-adapters/types'
|
||||
|
||||
export type LibrarySortOption = 'alphabetical' | 'unread' | 'lastRead' | 'dateAdded'
|
||||
|
||||
export const libraryState = $state({
|
||||
items: [] as Manga[],
|
||||
searchResults: [] as Manga[],
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
filter: {
|
||||
status: 'all' as MangaStatus | 'all',
|
||||
tags: [] as string[],
|
||||
unread: false,
|
||||
query: '',
|
||||
},
|
||||
sort: 'alphabetical' as LibrarySortOption,
|
||||
sortDesc: false,
|
||||
view: 'grid' as 'grid' | 'list',
|
||||
selected: new Set<string>(),
|
||||
})
|
||||
|
||||
export const filteredItems = $derived.by(() => {
|
||||
let result = libraryState.items
|
||||
|
||||
if (libraryState.filter.unread) {
|
||||
result = result.filter(m => m.unreadCount > 0)
|
||||
}
|
||||
if (libraryState.filter.status !== 'all') {
|
||||
result = result.filter(m => m.status === libraryState.filter.status)
|
||||
}
|
||||
if (libraryState.filter.tags.length > 0) {
|
||||
result = result.filter(m =>
|
||||
libraryState.filter.tags.every(tag => m.tags?.includes(tag))
|
||||
)
|
||||
}
|
||||
if (libraryState.filter.query) {
|
||||
const q = libraryState.filter.query.toLowerCase()
|
||||
result = result.filter(m => m.title.toLowerCase().includes(q))
|
||||
}
|
||||
|
||||
const sorted = [...result].sort((a, b) => {
|
||||
switch (libraryState.sort) {
|
||||
case 'unread': return (b.unreadCount ?? 0) - (a.unreadCount ?? 0)
|
||||
case 'lastRead': return (b.lastReadAt ?? 0) - (a.lastReadAt ?? 0)
|
||||
case 'dateAdded': return (b.addedAt ?? 0) - (a.addedAt ?? 0)
|
||||
case 'alphabetical':
|
||||
default: return a.title.localeCompare(b.title)
|
||||
}
|
||||
})
|
||||
|
||||
return libraryState.sortDesc ? sorted.reverse() : sorted
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
export type ToastKind = 'info' | 'success' | 'error' | 'download'
|
||||
|
||||
export interface Toast {
|
||||
id: string
|
||||
kind: ToastKind
|
||||
message: string
|
||||
detail?: string
|
||||
duration?: number
|
||||
}
|
||||
|
||||
export const notificationsState = $state({
|
||||
toasts: [] as Toast[],
|
||||
})
|
||||
|
||||
export function toast(kind: ToastKind, message: string, detail?: string, duration = 4000) {
|
||||
const id = crypto.randomUUID()
|
||||
notificationsState.toasts.push({ id, kind, message, detail, duration })
|
||||
if (duration > 0) {
|
||||
setTimeout(() => dismissToast(id), duration)
|
||||
}
|
||||
}
|
||||
|
||||
export function dismissToast(id: string) {
|
||||
notificationsState.toasts = notificationsState.toasts.filter(t => t.id !== id)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { Manga, Chapter } from '$lib/types'
|
||||
import type { Page } from '$lib/server-adapters/types'
|
||||
|
||||
export type ReadMode = 'single' | 'strip'
|
||||
export type FitMode = 'width' | 'height' | 'original'
|
||||
export type ReadDirection = 'ltr' | 'rtl'
|
||||
|
||||
export const readerState = $state({
|
||||
manga: null as Manga | null,
|
||||
chapter: null as Chapter | null,
|
||||
chapters: [] as Chapter[],
|
||||
|
||||
pages: [] as Page[],
|
||||
pagesLoading: false,
|
||||
pagesError: null as string | null,
|
||||
|
||||
currentPage: 0,
|
||||
mode: 'single' as ReadMode,
|
||||
fit: 'width' as FitMode,
|
||||
direction: 'ltr' as ReadDirection,
|
||||
zoom: 1,
|
||||
|
||||
showControls: false,
|
||||
showSettings: false,
|
||||
fullscreen: false,
|
||||
})
|
||||
|
||||
export const currentPageData = $derived(
|
||||
readerState.pages[readerState.currentPage] ?? null
|
||||
)
|
||||
|
||||
export const progress = $derived(
|
||||
readerState.pages.length > 0
|
||||
? (readerState.currentPage + 1) / readerState.pages.length
|
||||
: 0
|
||||
)
|
||||
|
||||
export const hasPrev = $derived(readerState.currentPage > 0)
|
||||
export const hasNext = $derived(
|
||||
readerState.currentPage < readerState.pages.length - 1
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { Manga, Chapter } from '$lib/types'
|
||||
|
||||
export const seriesState = $state({
|
||||
current: null as Manga | null,
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
|
||||
chapters: [] as Chapter[],
|
||||
chaptersLoading: false,
|
||||
chaptersError: null as string | null,
|
||||
|
||||
chapterFilter: {
|
||||
unread: false,
|
||||
downloaded: false,
|
||||
query: '',
|
||||
},
|
||||
chapterSortDesc: true,
|
||||
})
|
||||
|
||||
export const filteredChapters = $derived.by(() => {
|
||||
let result = seriesState.chapters
|
||||
|
||||
if (seriesState.chapterFilter.unread) {
|
||||
result = result.filter(c => !c.read)
|
||||
}
|
||||
if (seriesState.chapterFilter.downloaded) {
|
||||
result = result.filter(c => c.downloaded)
|
||||
}
|
||||
if (seriesState.chapterFilter.query) {
|
||||
const q = seriesState.chapterFilter.query.toLowerCase()
|
||||
result = result.filter(c => c.name.toLowerCase().includes(q))
|
||||
}
|
||||
|
||||
const sorted = [...result].sort((a, b) => a.chapterNumber - b.chapterNumber)
|
||||
return seriesState.chapterSortDesc ? sorted.reverse() : sorted
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { Tracker } from '$lib/types'
|
||||
|
||||
export const trackingState = $state({
|
||||
trackers: [] as Tracker[],
|
||||
loading: false,
|
||||
error: null as string | null,
|
||||
syncing: false,
|
||||
})
|
||||
Reference in New Issue
Block a user