Refactor catalog
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
/>
|
/>
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover" class="bg-white dark:bg-gray-800 dark:text-white">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import type { Volume } from '$lib/types';
|
|
||||||
import { writable } from 'svelte/store';
|
|
||||||
import { db } from '$lib/catalog/db';
|
import { db } from '$lib/catalog/db';
|
||||||
import { liveQuery } from 'dexie';
|
import { liveQuery } from 'dexie';
|
||||||
export const currentManga = writable<Volume[] | undefined>(undefined);
|
|
||||||
export const currentVolume = writable<Volume | undefined>(undefined);
|
|
||||||
export const catalog = liveQuery(() => db.catalog.toArray());
|
export const catalog = liveQuery(() => db.catalog.toArray());
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { catalog } from '$lib/catalog';
|
import { catalog } from '$lib/catalog';
|
||||||
import { Button } from 'flowbite-svelte';
|
|
||||||
import CatalogItem from './CatalogItem.svelte';
|
import CatalogItem from './CatalogItem.svelte';
|
||||||
import { promptConfirmation } from '$lib/util';
|
|
||||||
import { db } from '$lib/catalog/db';
|
|
||||||
|
|
||||||
function onClear() {
|
|
||||||
promptConfirmation('Are you sure you want to clear your catalog?', () => db.catalog.clear());
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $catalog}
|
{#if $catalog}
|
||||||
{#if $catalog.length > 0}
|
{#if $catalog.length > 0}
|
||||||
<div class="flex flex-col gap-5">
|
<div class="flex flex-col gap-5">
|
||||||
<div class="sm:block flex-col flex">
|
|
||||||
<Button outline color="red" class="float-right" on:click={onClear}>Clear catalog</Button>
|
|
||||||
</div>
|
|
||||||
<div class="flex sm:flex-row flex-col gap-5 flex-wrap justify-center sm:justify-start">
|
<div class="flex sm:flex-row flex-col gap-5 flex-wrap justify-center sm:justify-start">
|
||||||
{#each $catalog as { id, manga } (id)}
|
{#each $catalog as { id } (id)}
|
||||||
<CatalogItem {manga} />
|
<CatalogItem {id} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { currentManga } from '$lib/catalog';
|
import { catalog } from '$lib/catalog';
|
||||||
import type { Volume } from '$lib/types';
|
|
||||||
export let manga: Volume[];
|
|
||||||
const { volumeName, files, mokuroData } = manga[0];
|
|
||||||
|
|
||||||
function onClick() {
|
export let id: string;
|
||||||
currentManga.set(manga);
|
|
||||||
}
|
$: manga = $catalog?.find((item) => item.id === id)?.manga[0];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a href={volumeName} on:click={onClick}>
|
{#if manga}
|
||||||
|
<a href={id}>
|
||||||
<div class="flex flex-col gap-[5px] text-center items-center">
|
<div class="flex flex-col gap-[5px] text-center items-center">
|
||||||
{mokuroData.title}
|
{manga.mokuroData.title}
|
||||||
{#if files}
|
{#if manga.files}
|
||||||
<img
|
<img
|
||||||
src={URL.createObjectURL(Object.values(files)[0])}
|
src={URL.createObjectURL(Object.values(manga.files)[0])}
|
||||||
alt="img"
|
alt="img"
|
||||||
class="object-contain w-[250px] h-[350px] bg-black border-gray-900 border"
|
class="object-contain w-[250px] h-[350px] bg-black border-gray-900 border"
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Page } from '$lib/types';
|
import type { Page } from '$lib/types';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import TextBoxes from './TextBoxes.svelte';
|
import TextBoxes from './TextBoxes.svelte';
|
||||||
|
|
||||||
export let page: Page;
|
export let page: Page;
|
||||||
export let src: File;
|
export let src: File;
|
||||||
|
|
||||||
|
$: url = `url(${URL.createObjectURL(src)})`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
draggable="false"
|
draggable="false"
|
||||||
style:width={`${page.img_width}px`}
|
style:width={`${page.img_width}px`}
|
||||||
style:height={`${page.img_height}px`}
|
style:height={`${page.img_height}px`}
|
||||||
style:background-image={`url(${URL.createObjectURL(src)})`}
|
style:background-image={url}
|
||||||
class="relative"
|
class="relative"
|
||||||
>
|
>
|
||||||
<TextBoxes {page} {src} />
|
<TextBoxes {page} {src} />
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { currentVolume } from '$lib/catalog';
|
import { catalog } from '$lib/catalog';
|
||||||
import { Panzoom, toggleFullScreen, zoomDefault } from '$lib/panzoom';
|
import { Panzoom, toggleFullScreen, zoomDefault } from '$lib/panzoom';
|
||||||
import { progress, settings, updateProgress } from '$lib/settings';
|
import { progress, settings, updateProgress } from '$lib/settings';
|
||||||
import { clamp } from '$lib/util';
|
import { clamp } from '$lib/util';
|
||||||
import { Input, Popover, Range } from 'flowbite-svelte';
|
import { Input, Popover, Range, Spinner } from 'flowbite-svelte';
|
||||||
import MangaPage from './MangaPage.svelte';
|
import MangaPage from './MangaPage.svelte';
|
||||||
import {
|
import {
|
||||||
ChervonDoubleLeftSolid,
|
ChervonDoubleLeftSolid,
|
||||||
@@ -13,9 +13,13 @@
|
|||||||
} from 'flowbite-svelte-icons';
|
} from 'flowbite-svelte-icons';
|
||||||
import { afterUpdate, onMount } from 'svelte';
|
import { afterUpdate, onMount } from 'svelte';
|
||||||
import Cropper from './Cropper.svelte';
|
import Cropper from './Cropper.svelte';
|
||||||
|
import { page as pageStore } from '$app/stores';
|
||||||
|
|
||||||
const volume = $currentVolume;
|
$: volume = $catalog
|
||||||
const pages = volume?.mokuroData.pages;
|
?.find((item) => item.id === $pageStore.params.manga)
|
||||||
|
?.manga.find((item) => item.mokuroData.volume_uuid === $pageStore.params.volume);
|
||||||
|
|
||||||
|
$: pages = volume?.mokuroData.pages;
|
||||||
|
|
||||||
$: page = $progress?.[volume?.mokuroData.volume_uuid || 0] || 1;
|
$: page = $progress?.[volume?.mokuroData.volume_uuid || 0] || 1;
|
||||||
$: index = page - 1;
|
$: index = page - 1;
|
||||||
@@ -102,7 +106,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
getMaxCharCount();
|
|
||||||
updateCharacterCount(page);
|
updateCharacterCount(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -143,7 +146,7 @@
|
|||||||
|
|
||||||
function updateCharacterCount(currentPage: number) {
|
function updateCharacterCount(currentPage: number) {
|
||||||
charCount = 0;
|
charCount = 0;
|
||||||
if (pages && $settings.charCount) {
|
if (pages && pages.length > 0 && $settings.charCount) {
|
||||||
for (let i = 0; i < currentPage; i++) {
|
for (let i = 0; i < currentPage; i++) {
|
||||||
const blocks = pages[i].blocks;
|
const blocks = pages[i].blocks;
|
||||||
blocks.forEach((block) => {
|
blocks.forEach((block) => {
|
||||||
@@ -157,8 +160,10 @@
|
|||||||
|
|
||||||
let maxCharCount = 0;
|
let maxCharCount = 0;
|
||||||
|
|
||||||
|
$: pages?.length, getMaxCharCount();
|
||||||
|
|
||||||
function getMaxCharCount() {
|
function getMaxCharCount() {
|
||||||
if (pages) {
|
if (pages && pages.length > 0) {
|
||||||
for (let i = 0; i < pages.length; i++) {
|
for (let i = 0; i < pages.length; i++) {
|
||||||
const blocks = pages[i].blocks;
|
const blocks = pages[i].blocks;
|
||||||
blocks.forEach((block) => {
|
blocks.forEach((block) => {
|
||||||
@@ -174,6 +179,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:resize={zoomDefault} on:keyup|preventDefault={handleShortcuts} />
|
<svelte:window on:resize={zoomDefault} on:keyup|preventDefault={handleShortcuts} />
|
||||||
|
<svelte:head>
|
||||||
|
<title>{volume?.mokuroData.volume || 'Volume'}</title>
|
||||||
|
</svelte:head>
|
||||||
{#if volume && pages}
|
{#if volume && pages}
|
||||||
<Cropper />
|
<Cropper />
|
||||||
<Popover placement="bottom-end" trigger="click" triggeredBy="#page-num" class="z-20">
|
<Popover placement="bottom-end" trigger="click" triggeredBy="#page-num" class="z-20">
|
||||||
@@ -235,4 +243,8 @@
|
|||||||
on:mouseup={right}
|
on:mouseup={right}
|
||||||
class="right-0 top-0 absolute h-full w-10 hover:bg-slate-400 opacity-[0.01]"
|
class="right-0 top-0 absolute h-full w-10 hover:bg-slate-400 opacity-[0.01]"
|
||||||
/>
|
/>
|
||||||
|
{:else}
|
||||||
|
<div class="fixed z-50 left-1/2 top-1/2">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AccordionItem, Button, Label, Select } from 'flowbite-svelte';
|
import { db } from '$lib/catalog/db';
|
||||||
|
import { promptConfirmation } from '$lib/util';
|
||||||
|
import { AccordionItem, Button, Select } from 'flowbite-svelte';
|
||||||
|
|
||||||
let profiles = [
|
let profiles = [
|
||||||
{ value: 'default', name: 'Default' },
|
{ value: 'default', name: 'Default' },
|
||||||
@@ -8,12 +10,19 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
let profile = 'default';
|
let profile = 'default';
|
||||||
|
|
||||||
|
function onClear() {
|
||||||
|
promptConfirmation('Are you sure you want to clear your catalog?', () => db.catalog.clear());
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
<span slot="header">Profile</span>
|
<span slot="header">Profile</span>
|
||||||
|
<div class="flex flex-col gap-5">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<Select items={profiles} value={profile} />
|
<Select items={profiles} value={profile} />
|
||||||
<Button size="sm" outline color="dark">Manage profiles</Button>
|
<Button size="sm" outline color="dark">Manage profiles</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<Button on:click={onClear} outline color="red">Clear catalog</Button>
|
||||||
|
</div>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { currentVolume } from '$lib/catalog';
|
import { progress } from '$lib/settings';
|
||||||
import type { Volume } from '$lib/types';
|
import type { Volume } from '$lib/types';
|
||||||
|
import { ListgroupItem } from 'flowbite-svelte';
|
||||||
|
import type { ListGroupItemType } from 'flowbite-svelte/dist/types';
|
||||||
|
|
||||||
export let volume: Volume;
|
export let item: string | ListGroupItemType;
|
||||||
const { volumeName, files } = volume;
|
const volume = item as Volume;
|
||||||
|
|
||||||
|
const { volumeName, mokuroData } = volume as Volume;
|
||||||
|
|
||||||
function onClick() {
|
function onClick() {
|
||||||
currentVolume.set(volume);
|
goto(`${$page.params.manga}/${mokuroData.volume_uuid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: currentPage = $progress?.[volume?.mokuroData.volume_uuid || 0] || 1;
|
||||||
|
|
||||||
|
$: progressDisplay = `${currentPage} / ${volume.mokuroData.pages.length}`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a href={`${$page.params.manga}/${volumeName}`} on:click={onClick}>
|
<a href={`${$page.params.manga}/${mokuroData.volume_uuid}`} class="h-full w-full">
|
||||||
<div class="flex flex-col gap-[5px] text-center items-center">
|
<ListgroupItem>
|
||||||
{volumeName}
|
<div>
|
||||||
{#if files}
|
<h3 class="font-semibold">{volumeName}</h3>
|
||||||
<img
|
<p>{progressDisplay}</p>
|
||||||
src={URL.createObjectURL(Object.values(files)[0])}
|
|
||||||
alt="img"
|
|
||||||
class="object-contain sm:w-[250px] h-[350px] bg-black border-gray-900 border"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
</ListgroupItem>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
import Catalog from '$lib/components/Catalog.svelte';
|
import Catalog from '$lib/components/Catalog.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="p-2">
|
<svelte:head>
|
||||||
|
<title>Mokuro</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="p-5">
|
||||||
<Catalog />
|
<Catalog />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { currentManga } from '$lib/catalog';
|
import { catalog } from '$lib/catalog';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import VolumeItem from '$lib/components/VolumeItem.svelte';
|
import VolumeItem from '$lib/components/VolumeItem.svelte';
|
||||||
import { Button } from 'flowbite-svelte';
|
import { Button, Listgroup } from 'flowbite-svelte';
|
||||||
import { db } from '$lib/catalog/db';
|
import { db } from '$lib/catalog/db';
|
||||||
import { promptConfirmation } from '$lib/util';
|
import { promptConfirmation } from '$lib/util';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import type { Volume } from '$lib/types';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
const manga = $currentManga?.sort((a, b) => {
|
function sortManga(a: Volume, b: Volume) {
|
||||||
if (a.volumeName < b.volumeName) {
|
if (a.volumeName < b.volumeName) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -15,13 +17,9 @@
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!manga) {
|
|
||||||
goto('/');
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
$: manga = $catalog?.find((item) => item.id === $page.params.manga)?.manga.sort(sortManga);
|
||||||
|
|
||||||
async function confirmDelete() {
|
async function confirmDelete() {
|
||||||
const title = manga?.[0].mokuroData.title_uuid;
|
const title = manga?.[0].mokuroData.title_uuid;
|
||||||
@@ -34,15 +32,22 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="p-2 flex flex-col gap-5">
|
<svelte:head>
|
||||||
<div class="sm:block flex-col flex">
|
<title>{manga?.[0].mokuroData.title || 'Manga'}</title>
|
||||||
<Button outline color="red" class="float-right" on:click={onDelete}>Delete manga</Button>
|
</svelte:head>
|
||||||
|
{#if manga}
|
||||||
|
<div class="p-5 flex flex-col gap-5">
|
||||||
|
<div class="flex flex-row justify-between">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<h3 class="font-bold">{manga[0].mokuroData.title}</h3>
|
||||||
|
<p>Volumes: {manga.length}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex sm:flex-row flex-col justify-center sm:justify-start gap-5 flex-wrap">
|
<div><Button color="alternative" on:click={onDelete}>Remove manga</Button></div>
|
||||||
{#if manga}
|
|
||||||
{#each manga as volume}
|
|
||||||
<VolumeItem {volume} />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<Listgroup items={manga} let:item active class="flex-1 h-full w-full">
|
||||||
|
<VolumeItem {item} />
|
||||||
|
</Listgroup>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="flex justify-center p-16">Manga not found</div>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -1,17 +1,3 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { currentVolume } from '$lib/catalog';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
|
|
||||||
const volume = $currentVolume;
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!volume) {
|
|
||||||
goto('/');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Reference in New Issue
Block a user