mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Chore: Port over Home & Fix Suwayomi-Server Detection on Web
This commit is contained in:
@@ -2,52 +2,88 @@ import type { Manga } from '$lib/types'
|
||||
import type { MangaStatus } from '$lib/server-adapters/types'
|
||||
|
||||
export type LibrarySortOption = 'alphabetical' | 'unread' | 'lastRead' | 'dateAdded'
|
||||
export type LibraryTab = 'saved' | 'downloaded'
|
||||
|
||||
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>(),
|
||||
})
|
||||
class LibraryState {
|
||||
items = $state<Manga[]>([])
|
||||
loading = $state(false)
|
||||
error = $state<string | null>(null)
|
||||
refreshing = $state(false)
|
||||
|
||||
export const filteredItems = $derived.by(() => {
|
||||
let result = libraryState.items
|
||||
tab = $state<LibraryTab>('saved')
|
||||
sort = $state<LibrarySortOption>('alphabetical')
|
||||
sortDesc = $state(false)
|
||||
|
||||
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)
|
||||
}
|
||||
filter = $state({
|
||||
status: 'all' as MangaStatus | 'all',
|
||||
unread: false,
|
||||
downloaded: false,
|
||||
bookmarked: false,
|
||||
query: '',
|
||||
})
|
||||
|
||||
return libraryState.sortDesc ? sorted.reverse() : sorted
|
||||
})
|
||||
selected = $state(new Set<number>())
|
||||
selectMode = $state(false)
|
||||
|
||||
filteredItems = $derived.by(() => {
|
||||
let result = this.tab === 'downloaded'
|
||||
? this.items.filter(m => (m.downloadCount ?? 0) > 0)
|
||||
: this.items.filter(m => m.inLibrary)
|
||||
|
||||
if (this.filter.unread) result = result.filter(m => (m.unreadCount ?? 0) > 0)
|
||||
if (this.filter.downloaded) result = result.filter(m => (m.downloadCount ?? 0) > 0)
|
||||
if (this.filter.bookmarked) result = result.filter(m => (m.bookmarkCount ?? 0) > 0)
|
||||
|
||||
if (this.filter.status !== 'all') {
|
||||
result = result.filter(
|
||||
m => m.status?.toUpperCase().replace(/\s+/g, '_') === this.filter.status
|
||||
)
|
||||
}
|
||||
|
||||
if (this.filter.query) {
|
||||
const q = this.filter.query.toLowerCase()
|
||||
result = result.filter(m => m.title.toLowerCase().includes(q))
|
||||
}
|
||||
|
||||
const sorted = [...result].sort((a, b) => {
|
||||
switch (this.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)
|
||||
default: return a.title.localeCompare(b.title)
|
||||
}
|
||||
})
|
||||
|
||||
return this.sortDesc ? sorted.reverse() : sorted
|
||||
})
|
||||
|
||||
get hasActiveFilters() {
|
||||
return this.filter.status !== 'all'
|
||||
|| this.filter.unread
|
||||
|| this.filter.downloaded
|
||||
|| this.filter.bookmarked
|
||||
}
|
||||
|
||||
enterSelect(id?: number) {
|
||||
this.selectMode = true
|
||||
if (id !== undefined) this.selected = new Set([id])
|
||||
}
|
||||
|
||||
exitSelect() {
|
||||
this.selectMode = false
|
||||
this.selected = new Set()
|
||||
}
|
||||
|
||||
toggleSelect(id: number) {
|
||||
const next = new Set(this.selected)
|
||||
if (next.has(id)) next.delete(id); else next.add(id)
|
||||
this.selected = next
|
||||
if (next.size === 0) this.exitSelect()
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
this.selected = new Set(this.filteredItems.map(m => m.id))
|
||||
}
|
||||
}
|
||||
|
||||
export const libraryState = new LibraryState()
|
||||
Reference in New Issue
Block a user