mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Feat: Library-Refresh Overhaul & Settings Re-Wiring
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { FolderSimple, Plus, Pencil, Trash, Star, Eye, EyeSlash } from "phosphor-svelte";
|
||||
import { FolderSimple, Plus, Pencil, Trash, Star, Eye, EyeSlash, ArrowsClockwise, DownloadSimple } from "phosphor-svelte";
|
||||
import { gql } from "@api/client";
|
||||
import { GET_CATEGORIES } from "@api/queries/manga";
|
||||
import { CREATE_CATEGORY, UPDATE_CATEGORY, DELETE_CATEGORY, UPDATE_CATEGORY_ORDER } from "@api/mutations/manga";
|
||||
import { CREATE_CATEGORY, UPDATE_CATEGORY, UPDATE_CATEGORIES, DELETE_CATEGORY, UPDATE_CATEGORY_ORDER } from "@api/mutations/manga";
|
||||
import type { Category } from "@types";
|
||||
import { store, updateSettings, toggleHiddenCategory, setCategories } from "@store/state.svelte";
|
||||
|
||||
@@ -57,6 +57,19 @@
|
||||
} catch (e: any) { catsError = e?.message ?? "Failed to delete folder"; }
|
||||
}
|
||||
|
||||
async function toggleCategoryFlag(id: number, flag: "includeInUpdate" | "includeInDownload") {
|
||||
const cat = store.categories.find(c => c.id === id);
|
||||
if (!cat) return;
|
||||
const next = !cat[flag];
|
||||
setCategories(store.categories.map(c => c.id === id ? { ...c, [flag]: next } : c));
|
||||
try {
|
||||
await gql(UPDATE_CATEGORIES, { ids: [id], patch: { [flag]: next } });
|
||||
} catch (e: any) {
|
||||
setCategories(store.categories.map(c => c.id === id ? { ...c, [flag]: !next } : c));
|
||||
catsError = e?.message ?? "Failed to update folder";
|
||||
}
|
||||
}
|
||||
|
||||
async function moveCategory(id: number, direction: -1 | 1) {
|
||||
const zeroCat = store.categories.filter(c => c.id === 0);
|
||||
const sortable = store.categories.filter(c => c.id !== 0).sort((a, b) => a.order - b.order);
|
||||
@@ -144,6 +157,20 @@
|
||||
title={(store.settings.hiddenCategoryIds ?? []).includes(cat.id) ? "Show in Saved tab" : "Hide from Saved tab"}>
|
||||
{#if (store.settings.hiddenCategoryIds ?? []).includes(cat.id)}<EyeSlash size={13} weight="light" />{:else}<Eye size={13} weight="light" />{/if}
|
||||
</button>
|
||||
<button
|
||||
class="s-btn-icon"
|
||||
class:accent={cat.includeInUpdate !== false}
|
||||
onclick={() => toggleCategoryFlag(cat.id, "includeInUpdate")}
|
||||
title={cat.includeInUpdate !== false ? "Exclude from library updates" : "Include in library updates"}>
|
||||
<ArrowsClockwise size={13} weight={cat.includeInUpdate !== false ? "bold" : "light"} />
|
||||
</button>
|
||||
<button
|
||||
class="s-btn-icon"
|
||||
class:accent={cat.includeInDownload !== false}
|
||||
onclick={() => toggleCategoryFlag(cat.id, "includeInDownload")}
|
||||
title={cat.includeInDownload !== false ? "Exclude from auto-downloads" : "Include in auto-downloads"}>
|
||||
<DownloadSimple size={13} weight={cat.includeInDownload !== false ? "bold" : "light"} />
|
||||
</button>
|
||||
<button class="s-btn-icon" onclick={() => moveCategory(cat.id, -1)} disabled={i === 0} title="Move up">↑</button>
|
||||
<button class="s-btn-icon" onclick={() => moveCategory(cat.id, 1)} disabled={i === displayCats.length - 1} title="Move down">↓</button>
|
||||
<button class="s-btn-icon" onclick={() => startEdit(cat.id, cat.name)} title="Rename"><Pencil size={12} weight="light" /></button>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { gql } from "@api/client";
|
||||
import { GET_DOWNLOADS_PATH, GET_RESTORE_STATUS } from "@api/queries/manga";
|
||||
import { CREATE_BACKUP } from "@api/mutations/manga";
|
||||
import { CLEAR_CACHED_IMAGES } from "@api/mutations/extensions";
|
||||
import { SET_DOWNLOADS_PATH, SET_LOCAL_SOURCE_PATH } from "@api/mutations/downloads";
|
||||
import { untrack } from "svelte";
|
||||
import { store, updateSettings, addToast } from "@store/state.svelte";
|
||||
@@ -17,8 +18,9 @@
|
||||
interface ResetItem { key: string; label: string; desc: string; state: ResetState; error: string | null; confirm: boolean; }
|
||||
|
||||
let resetItems = $state<ResetItem[]>([
|
||||
{ key: "moku-cache", label: "Clear Moku cache", desc: "Removes image cache and temporary files stored by Moku.", state: "idle", error: null, confirm: false },
|
||||
{ key: "suwayomi-cache", label: "Clear Suwayomi cache", desc: "Deletes the Suwayomi cache and KCEF directories inside the data folder.", state: "idle", error: null, confirm: false },
|
||||
{ key: "moku-cache", label: "Clear Moku cache", desc: "Removes image cache and temporary files stored by Moku.", state: "idle", error: null, confirm: false },
|
||||
{ key: "suwayomi-cache", label: "Clear Suwayomi cache", desc: "Deletes the Suwayomi cache and KCEF directories inside the data folder.", state: "idle", error: null, confirm: false },
|
||||
{ key: "server-cache", label: "Clear server image cache", desc: "Removes cached chapter pages and thumbnails stored on the Suwayomi server.", state: "idle", error: null, confirm: false },
|
||||
{ key: "reading-history", label: "Clear reading history", desc: "Erases chapter history, read log, reading stats, and daily read counts.", state: "idle", error: null, confirm: true },
|
||||
{ key: "moku-settings", label: "Reset Moku settings", desc: "Restores all app settings to their defaults. Does not affect library data.", state: "idle", error: null, confirm: true },
|
||||
{ key: "suwayomi-data", label: "Reset Suwayomi data", desc: "Deletes the database, extensions, settings, and logs. Downloads and backups are preserved.", state: "idle", error: null, confirm: true },
|
||||
@@ -82,11 +84,15 @@
|
||||
case "suwayomi-cache":
|
||||
await invoke("clear_suwayomi_cache");
|
||||
break;
|
||||
case "server-cache":
|
||||
await gql(CLEAR_CACHED_IMAGES, { cachedPages: true, cachedThumbnails: true, downloadedThumbnails: false });
|
||||
break;
|
||||
case "reading-history":
|
||||
store.clearHistory();
|
||||
await persistLibrary({ history: [], bookmarks: store.bookmarks, markers: store.markers, readLog: [], readingStats: DEFAULT_READING_STATS, dailyReadCounts: {} });
|
||||
break;
|
||||
case "moku-settings":
|
||||
localStorage.clear();
|
||||
store.hydrate({ settings: DEFAULT_SETTINGS } as any);
|
||||
await persistSettings({ settings: DEFAULT_SETTINGS, storeVersion: 1 });
|
||||
patchReset(key, { state: "done" });
|
||||
@@ -94,6 +100,7 @@
|
||||
invoke("exit_app");
|
||||
return;
|
||||
case "suwayomi-data":
|
||||
localStorage.clear();
|
||||
await invoke("reset_suwayomi_data");
|
||||
patchReset(key, { state: "done" });
|
||||
await showExitCountdown();
|
||||
|
||||
@@ -138,13 +138,25 @@
|
||||
<img src={thumbUrl(tracker.icon)} alt={tracker.name} class="s-tracker-logo" />
|
||||
<div class="s-row-info">
|
||||
<span class="s-label">{tracker.name}</span>
|
||||
<span class="s-pill" class:on={tracker.isLoggedIn}>
|
||||
{tracker.isLoggedIn ? "Connected" : "Not connected"}
|
||||
</span>
|
||||
<div class="s-tracker-status-row">
|
||||
<span class="s-pill" class:on={tracker.isLoggedIn && !tracker.isTokenExpired}>
|
||||
{tracker.isLoggedIn ? "Connected" : "Not connected"}
|
||||
</span>
|
||||
{#if tracker.isLoggedIn && tracker.isTokenExpired}
|
||||
<span class="s-pill s-pill-warn">Token expired — reconnect</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="s-tracker-action">
|
||||
{#if tracker.isLoggedIn}
|
||||
{#if tracker.isLoggedIn && tracker.isTokenExpired}
|
||||
<button class="s-btn s-btn-accent" onclick={() => tracker.authUrl ? startOAuth(tracker) : startCredentials(tracker)}>
|
||||
Reconnect
|
||||
</button>
|
||||
<button class="s-btn s-btn-danger" onclick={() => logoutTracker(tracker.id)} disabled={loggingOut === tracker.id}>
|
||||
{loggingOut === tracker.id ? "Disconnecting…" : "Disconnect"}
|
||||
</button>
|
||||
{:else if tracker.isLoggedIn}
|
||||
<button class="s-btn s-btn-danger" onclick={() => logoutTracker(tracker.id)} disabled={loggingOut === tracker.id}>
|
||||
{loggingOut === tracker.id ? "Disconnecting…" : "Disconnect"}
|
||||
</button>
|
||||
@@ -249,4 +261,9 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.s-tracker-status-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
|
||||
.s-pill-warn { background: color-mix(in srgb, var(--color-warn, #c97c2b) 15%, transparent); color: var(--color-warn, #c97c2b); border-color: color-mix(in srgb, var(--color-warn, #c97c2b) 35%, transparent); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user