Chore: Completed Splash-Screen & Iniital Tauri Wire-Up

This commit is contained in:
Youwes09
2026-06-02 08:27:37 -05:00
parent c5243ba30c
commit 18027baee1
45 changed files with 2981 additions and 2013 deletions
+6 -2
View File
@@ -14,14 +14,16 @@ export const boot = $state({
loginPass: '',
sessionExpired: false,
skipped: false,
serverProbeOk: false,
})
let probeGeneration = 0
function handleProbeSuccess(gen: number) {
if (gen !== probeGeneration) return
boot.failed = false
boot.skipped = false
boot.failed = false
boot.skipped = false
boot.serverProbeOk = true
appState.authenticated = true
appState.status = 'ready'
}
@@ -56,6 +58,7 @@ export function startProbe(
boot.failed = false
boot.loginRequired = false
boot.skipped = false
boot.serverProbeOk = false
appState.status = 'booting'
let tries = 0
@@ -121,6 +124,7 @@ export async function submitLogin(): Promise<void> {
boot.skipped = false
boot.loginPass = ''
boot.loginError = null
boot.serverProbeOk = true
appState.authenticated = true
appState.status = 'ready'
} catch (e: unknown) {
+202
View File
@@ -0,0 +1,202 @@
import { saveLibrary } from '$lib/core/persistence/persist'
import type { ReadSession, ReadingStats } from '$lib/types/history'
import { DEFAULT_READING_STATS } from '$lib/types/history'
const MAX_SESSIONS = 1000
const SESSION_GAP_MS = 60 * 60 * 1_000
export interface ActiveSession {
id: string
mangaId: number
mangaTitle: string
thumbnailUrl: string
startChapterId: number
startChapterName: string
endChapterId: number
endChapterName: string
startPage: number
endPage: number
startedAt: number
lastTickAt: number
seenChapterIds: Set<number>
}
function dateKey(ms: number): string {
return new Date(ms).toISOString().slice(0, 10)
}
function computeStats(sessions: ReadSession[]): ReadingStats {
if (!sessions.length) return { ...DEFAULT_READING_STATS }
const chapterIds = new Set<number>()
const mangaIds = new Set<number>()
const days = new Set<string>()
let totalMs = 0
let firstReadAt = Infinity
let lastReadAt = 0
for (const s of sessions) {
chapterIds.add(s.endChapterId)
if (s.chaptersSpanned > 1) chapterIds.add(s.startChapterId)
mangaIds.add(s.mangaId)
totalMs += Math.min(s.durationMs, SESSION_GAP_MS)
firstReadAt = Math.min(firstReadAt, s.startedAt)
lastReadAt = Math.max(lastReadAt, s.endedAt)
days.add(dateKey(s.endedAt))
}
const sortedDays = Array.from(days).sort()
let currentStreak = 0
let longestStreak = 0
let streak = 0
const todayKey = dateKey(Date.now())
const yestKey = dateKey(Date.now() - 86_400_000)
const lastDay = sortedDays[sortedDays.length - 1]
const streakActive = lastDay === todayKey || lastDay === yestKey
for (let i = 0; i < sortedDays.length; i++) {
if (i === 0) {
streak = 1
} else {
const prev = new Date(sortedDays[i - 1]).getTime()
const curr = new Date(sortedDays[i]).getTime()
streak = curr - prev <= 86_400_000 * 1.5 ? streak + 1 : 1
}
longestStreak = Math.max(longestStreak, streak)
}
currentStreak = streakActive ? streak : 0
return {
totalChaptersRead: chapterIds.size,
totalMangaRead: mangaIds.size,
totalMinutesRead: Math.round(totalMs / 60_000),
firstReadAt: firstReadAt === Infinity ? 0 : firstReadAt,
lastReadAt,
currentStreakDays: currentStreak,
longestStreakDays: longestStreak,
lastStreakDate: lastDay ?? '',
}
}
class HistoryStore {
sessions = $state<ReadSession[]>([])
dailyReadCounts = $state<Record<string, number>>({})
stats = $state<ReadingStats>({ ...DEFAULT_READING_STATS })
active = $state<ActiveSession | null>(null)
load(sessions: ReadSession[], dailyReadCounts: Record<string, number>) {
this.sessions = sessions
this.dailyReadCounts = dailyReadCounts
this.stats = computeStats(sessions)
}
openSession(
mangaId: number,
mangaTitle: string,
thumbnailUrl: string,
chapterId: number,
chapterName: string,
page: number,
) {
if (this.active) this._commit(Date.now())
this.active = {
id: crypto.randomUUID(),
mangaId,
mangaTitle,
thumbnailUrl,
startChapterId: chapterId,
startChapterName: chapterName,
endChapterId: chapterId,
endChapterName: chapterName,
startPage: page,
endPage: page,
startedAt: Date.now(),
lastTickAt: Date.now(),
seenChapterIds: new Set([chapterId]),
}
}
tickSession(chapterId: number, chapterName: string, page: number) {
if (!this.active) return
const now = Date.now()
if (now - this.active.lastTickAt > SESSION_GAP_MS) {
this._commit(this.active.lastTickAt)
this.openSession(
this.active.mangaId,
this.active.mangaTitle,
this.active.thumbnailUrl,
chapterId,
chapterName,
page,
)
return
}
this.active.lastTickAt = now
this.active.endPage = page
this.active.endChapterId = chapterId
this.active.endChapterName = chapterName
this.active.seenChapterIds.add(chapterId)
}
closeSession() {
if (!this.active) return
this._commit(Date.now())
this.active = null
}
clearHistory() {
this.sessions = []
this.dailyReadCounts = {}
this.stats = { ...DEFAULT_READING_STATS }
void this._persist()
}
private _commit(endedAt: number) {
const a = this.active
if (!a) return
const durationMs = Math.min(endedAt - a.startedAt, SESSION_GAP_MS)
if (durationMs < 1_000) return
const session: ReadSession = {
id: a.id,
mangaId: a.mangaId,
mangaTitle: a.mangaTitle,
thumbnailUrl: a.thumbnailUrl,
startChapterId: a.startChapterId,
startChapterName: a.startChapterName,
endChapterId: a.endChapterId,
endChapterName: a.endChapterName,
startPage: a.startPage,
endPage: a.endPage,
startedAt: a.startedAt,
endedAt,
durationMs,
chaptersSpanned: a.seenChapterIds.size,
}
const day = dateKey(endedAt)
this.dailyReadCounts[day] = (this.dailyReadCounts[day] ?? 0) + 1
this.sessions = [session, ...this.sessions].slice(0, MAX_SESSIONS)
this.stats = computeStats(this.sessions)
void this._persist()
}
private async _persist() {
const bookmarks = (await import('$lib/state/reader.svelte')).readerState.bookmarks
const markers = (await import('$lib/state/reader.svelte')).readerState.markers
await saveLibrary({
sessions: this.sessions,
bookmarks,
markers,
dailyReadCounts: this.dailyReadCounts,
})
}
}
export const historyState = new HistoryStore()
+8 -37
View File
@@ -1,46 +1,17 @@
export interface HistoryEntry {
mangaId: number;
mangaTitle: string;
thumbnailUrl: string;
chapterId: number;
chapterName: string;
chapterNumber: number;
pageNumber: number;
readAt: number;
}
export interface ReadingStats {
currentStreakDays: number;
totalChaptersRead: number;
totalMinutesRead: number;
totalMangaRead: number;
longestStreakDays: number;
}
import { historyState } from '$lib/state/history.svelte'
export const homeState = $state({
history: [] as HistoryEntry[],
dailyReadCounts: {} as Record<string, number>,
stats: {
currentStreakDays: 0,
totalChaptersRead: 0,
totalMinutesRead: 0,
totalMangaRead: 0,
longestStreakDays: 0,
} as ReadingStats,
heroSlots: [null, null, null, null] as [number | null, number | null, number | null, number | null],
});
})
export function getHistoryStats() { return historyState.stats }
export function getHistorySessions() { return historyState.sessions }
export function getHistoryDailyCounts() { return historyState.dailyReadCounts }
export function setHeroSlot(i: 1 | 2 | 3, mangaId: number | null) {
homeState.heroSlots[i] = mangaId;
}
export function recordRead(entry: HistoryEntry) {
homeState.history = [entry, ...homeState.history.filter(e => e.chapterId !== entry.chapterId)];
const dateStr = new Date(entry.readAt).toISOString().slice(0, 10);
homeState.dailyReadCounts[dateStr] = (homeState.dailyReadCounts[dateStr] ?? 0) + 1;
homeState.stats.totalChaptersRead++;
homeState.heroSlots[i] = mangaId
}
export function clearHistory() {
homeState.history = [];
historyState.clearHistory()
}
+7 -5
View File
@@ -52,6 +52,7 @@ class ReaderState {
zoomOpen = $state(false);
winOpen = $state(false);
presetOpen = $state(false);
actionsOpen = $state(false);
nextN = $state(5);
dlBusy = $state(false);
@@ -116,11 +117,12 @@ class ReaderState {
}
closeAllPopovers(): boolean {
if (this.markerOpen) { this.markerOpen = false; return true; }
if (this.zoomOpen) { this.zoomOpen = false; return true; }
if (this.dlOpen) { this.dlOpen = false; return true; }
if (this.winOpen) { this.winOpen = false; return true; }
if (this.presetOpen) { this.presetOpen = false; return true; }
if (this.markerOpen) { this.markerOpen = false; return true; }
if (this.zoomOpen) { this.zoomOpen = false; return true; }
if (this.dlOpen) { this.dlOpen = false; return true; }
if (this.winOpen) { this.winOpen = false; return true; }
if (this.presetOpen) { this.presetOpen = false; return true; }
if (this.actionsOpen) { this.actionsOpen = false; return true; }
return false;
}
+17 -27
View File
@@ -1,38 +1,28 @@
import type { Settings } from "$lib/types/settings";
import { DEFAULT_SETTINGS } from "$lib/types/settings";
import type { Settings } from '$lib/types/settings'
import { DEFAULT_SETTINGS } from '$lib/types/settings'
import { saveSettings } from '$lib/core/persistence/persist'
const KEY = "moku_settings";
export const settingsState = $state({ settings: { ...DEFAULT_SETTINGS } as Settings })
function load(): Settings {
try {
const raw = localStorage.getItem(KEY);
if (raw) return { ...DEFAULT_SETTINGS, ...JSON.parse(raw) };
} catch {}
return { ...DEFAULT_SETTINGS };
}
function save(s: Settings) {
try { localStorage.setItem(KEY, JSON.stringify(s)); } catch {}
}
export const settingsState = $state({ settings: load() });
if (typeof document !== "undefined") {
document.documentElement.style.zoom = String(settingsState.settings.uiZoom ?? 1.0);
export async function loadSettingsIntoState(raw: unknown) {
if (raw && typeof raw === 'object') {
Object.assign(settingsState.settings, raw)
}
if (typeof document !== 'undefined') {
document.documentElement.style.zoom = String(settingsState.settings.uiZoom ?? 1.0)
}
}
export function updateSettings(patch: Partial<Settings>) {
Object.assign(settingsState.settings, patch);
save(settingsState.settings);
Object.assign(settingsState.settings, patch)
void saveSettings({ storeVersion: 2, settings: settingsState.settings })
if (typeof document !== "undefined") {
if (patch.uiZoom !== undefined) {
document.documentElement.style.zoom = String(patch.uiZoom);
}
if (typeof document !== 'undefined' && patch.uiZoom !== undefined) {
document.documentElement.style.zoom = String(patch.uiZoom)
}
}
export function resetSettings() {
settingsState.settings = { ...DEFAULT_SETTINGS };
save(settingsState.settings);
settingsState.settings = { ...DEFAULT_SETTINGS }
void saveSettings({ storeVersion: 2, settings: settingsState.settings })
}