Mostly get drag and drop working
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
import CatalogItem from './CatalogItem.svelte';
|
import CatalogItem from './CatalogItem.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $catalog && $catalog.length > 0}
|
{#if $catalog}
|
||||||
{#if $catalog.length > 0}
|
{#if $catalog.length > 0}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{#each $catalog as { manga }}
|
{#each $catalog as { manga }}
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
<p>Your catalog is currently empty.</p>
|
<p>Your catalog is currently empty.</p>
|
||||||
<a href="upload">Add manga</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let files: FileList | null | undefined = undefined;
|
import { A, Fileupload, Label } from 'flowbite-svelte';
|
||||||
|
|
||||||
|
export let files: FileList | undefined = undefined;
|
||||||
export let onUpload: ((files: FileList) => void) | undefined = undefined;
|
export let onUpload: ((files: FileList) => void) | undefined = undefined;
|
||||||
|
|
||||||
let input: HTMLInputElement;
|
let input: HTMLInputElement;
|
||||||
@@ -13,38 +15,15 @@
|
|||||||
function onClick() {
|
function onClick() {
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDrop(event: DragEvent) {
|
|
||||||
const items = event.dataTransfer?.items;
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input type="file" bind:files bind:this={input} on:change={handleChange} {...$$restProps} />
|
<input
|
||||||
|
type="file"
|
||||||
|
bind:files
|
||||||
|
bind:this={input}
|
||||||
|
on:change={handleChange}
|
||||||
|
{...$$restProps}
|
||||||
|
class="hidden"
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<A on:click={onClick}><slot>Upload</slot></A>
|
||||||
on:click={onClick}
|
|
||||||
on:dragover|preventDefault
|
|
||||||
on:drop|preventDefault|stopPropagation={onDrop}
|
|
||||||
>
|
|
||||||
<p><slot>Upload</slot></p>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
height: 100px;
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 2px dashed $secondary-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
color: $secondary-color;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,66 +1,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { Navbar, NavBrand } from 'flowbite-svelte';
|
||||||
Navbar,
|
import { UserSettingsSolid, UploadSolid } from 'flowbite-svelte-icons';
|
||||||
NavBrand,
|
|
||||||
Modal,
|
|
||||||
Drawer,
|
|
||||||
Button,
|
|
||||||
CloseButton,
|
|
||||||
Checkbox,
|
|
||||||
Toggle,
|
|
||||||
Select,
|
|
||||||
Input,
|
|
||||||
Label
|
|
||||||
} from 'flowbite-svelte';
|
|
||||||
import {
|
|
||||||
UserSettingsSolid,
|
|
||||||
UploadSolid,
|
|
||||||
InfoCircleSolid,
|
|
||||||
ArrowRightOutline
|
|
||||||
} from 'flowbite-svelte-icons';
|
|
||||||
import FileUpload from './FileUpload.svelte';
|
|
||||||
import { processFiles } from '$lib/upload';
|
|
||||||
import { afterNavigate } from '$app/navigation';
|
import { afterNavigate } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { sineIn } from 'svelte/easing';
|
import Settings from './Settings.svelte';
|
||||||
import { settingsStore, updateSetting } from '$lib/settings';
|
import UploadModal from './UploadModal.svelte';
|
||||||
|
|
||||||
let transitionParams = {
|
let settingsHidden = true;
|
||||||
x: 320,
|
let uploadModalOpen = false;
|
||||||
duration: 200,
|
let isReader = false;
|
||||||
easing: sineIn
|
|
||||||
};
|
|
||||||
|
|
||||||
let promise: Promise<void>;
|
|
||||||
let modal = false;
|
|
||||||
let drawer = false;
|
|
||||||
let isReader = true;
|
|
||||||
|
|
||||||
async function onUpload(files: FileList) {
|
|
||||||
promise = processFiles(files).then(() => {
|
|
||||||
modal = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
afterNavigate(() => {
|
afterNavigate(() => {
|
||||||
isReader = $page.route.id === '/[manga]/[volume]';
|
isReader = $page.route.id === '/[manga]/[volume]';
|
||||||
});
|
});
|
||||||
|
|
||||||
let selected = 'us';
|
|
||||||
|
|
||||||
let countries = [
|
|
||||||
{ value: 'us', name: 'auto' },
|
|
||||||
{ value: 'ca', name: 'Canada' },
|
|
||||||
{ value: 'fr', name: 'France' }
|
|
||||||
];
|
|
||||||
|
|
||||||
$: toggles = [
|
|
||||||
{ key: 'rightToLeft', text: 'Right to left', value: $settingsStore.rightToLeft },
|
|
||||||
{ key: 'singlePageView', text: 'Single page view', value: $settingsStore.singlePageView },
|
|
||||||
{ key: 'textEditable', text: 'Editable text', value: $settingsStore.textEditable },
|
|
||||||
{ key: 'textBoxBorders', text: 'Text box borders', value: $settingsStore.textBoxBorders },
|
|
||||||
{ key: 'displayOCR', text: 'OCR enabled', value: $settingsStore.displayOCR }
|
|
||||||
];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative z-10">
|
<div class="relative z-10">
|
||||||
@@ -69,54 +21,17 @@
|
|||||||
<span class="text-xl font-semibold dark:text-white">Mokuro</span>
|
<span class="text-xl font-semibold dark:text-white">Mokuro</span>
|
||||||
</NavBrand>
|
</NavBrand>
|
||||||
<div class="flex md:order-2 gap-5">
|
<div class="flex md:order-2 gap-5">
|
||||||
<UserSettingsSolid class="hover:text-primary-700" on:click={() => (drawer = false)} />
|
<UserSettingsSolid class="hover:text-primary-700" on:click={() => (settingsHidden = false)} />
|
||||||
<UploadSolid class="hover:text-primary-700" on:click={() => (modal = true)} />
|
<UploadSolid class="hover:text-primary-700" on:click={() => (uploadModalOpen = true)} />
|
||||||
</div>
|
</div>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
{#if isReader}
|
{#if isReader}
|
||||||
<UserSettingsSolid
|
<UserSettingsSolid
|
||||||
class="hover:text-primary-700 absolute right-5 top-5 opacity-10 hover:opacity-100"
|
class="hover:text-primary-700 absolute right-5 top-5 opacity-10 hover:opacity-100"
|
||||||
on:click={() => (drawer = false)}
|
on:click={() => (settingsHidden = false)}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Drawer
|
<Settings bind:hidden={settingsHidden} />
|
||||||
placement="right"
|
<UploadModal bind:open={uploadModalOpen} />
|
||||||
transitionType="fly"
|
|
||||||
width="lg:w-1/4 md:w-1/2 w-full"
|
|
||||||
{transitionParams}
|
|
||||||
bind:hidden={drawer}
|
|
||||||
id="sidebar1"
|
|
||||||
>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<h5 id="drawer-label" class="inline-flex items-center mb-4 text-base font-semibold">
|
|
||||||
<UserSettingsSolid class="w-4 h-4 mr-2.5" />Settings
|
|
||||||
</h5>
|
|
||||||
<CloseButton on:click={() => (drawer = true)} class="mb-4 dark:text-white" />
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-5">
|
|
||||||
{#each toggles as { key, text, value }}
|
|
||||||
<Toggle size="small" checked={value} on:change={() => updateSetting(key, !value)}
|
|
||||||
>{text}</Toggle
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
<div>
|
|
||||||
<Label>Fontsize:</Label>
|
|
||||||
<Select items={countries} bind:value={selected} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Background color:</Label>
|
|
||||||
<Input type="color" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Drawer>
|
|
||||||
|
|
||||||
<Modal title="Upload" bind:open={modal} outsideclose>
|
|
||||||
{#await promise}
|
|
||||||
<h2 class="justify-center flex">Loading...</h2>
|
|
||||||
{:then}
|
|
||||||
<FileUpload {onUpload} webkitdirectory>Upload directory</FileUpload>
|
|
||||||
<FileUpload {onUpload} accept=".mokuro,.zip,.cbz" multiple>Upload files</FileUpload>
|
|
||||||
{/await}
|
|
||||||
</Modal>
|
|
||||||
|
|||||||
61
src/lib/components/Settings.svelte
Normal file
61
src/lib/components/Settings.svelte
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Drawer, CloseButton, Toggle, Select, Input, Label } from 'flowbite-svelte';
|
||||||
|
import { UserSettingsSolid } from 'flowbite-svelte-icons';
|
||||||
|
import { sineIn } from 'svelte/easing';
|
||||||
|
import { settingsStore, updateSetting } from '$lib/settings';
|
||||||
|
|
||||||
|
let transitionParams = {
|
||||||
|
x: 320,
|
||||||
|
duration: 200,
|
||||||
|
easing: sineIn
|
||||||
|
};
|
||||||
|
|
||||||
|
export let hidden = true;
|
||||||
|
|
||||||
|
let selected = 'us';
|
||||||
|
|
||||||
|
let countries = [
|
||||||
|
{ value: 'us', name: 'auto' },
|
||||||
|
{ value: 'ca', name: 'Canada' },
|
||||||
|
{ value: 'fr', name: 'France' }
|
||||||
|
];
|
||||||
|
|
||||||
|
$: toggles = [
|
||||||
|
{ key: 'rightToLeft', text: 'Right to left', value: $settingsStore.rightToLeft },
|
||||||
|
{ key: 'singlePageView', text: 'Single page view', value: $settingsStore.singlePageView },
|
||||||
|
{ key: 'textEditable', text: 'Editable text', value: $settingsStore.textEditable },
|
||||||
|
{ key: 'textBoxBorders', text: 'Text box borders', value: $settingsStore.textBoxBorders },
|
||||||
|
{ key: 'displayOCR', text: 'OCR enabled', value: $settingsStore.displayOCR }
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Drawer
|
||||||
|
placement="right"
|
||||||
|
transitionType="fly"
|
||||||
|
width="lg:w-1/4 md:w-1/2 w-full"
|
||||||
|
{transitionParams}
|
||||||
|
bind:hidden
|
||||||
|
id="sidebar1"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<h5 id="drawer-label" class="inline-flex items-center mb-4 text-base font-semibold">
|
||||||
|
<UserSettingsSolid class="w-4 h-4 mr-2.5" />Settings
|
||||||
|
</h5>
|
||||||
|
<CloseButton on:click={() => (hidden = true)} class="mb-4 dark:text-white" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-5">
|
||||||
|
{#each toggles as { key, text, value }}
|
||||||
|
<Toggle size="small" checked={value} on:change={() => updateSetting(key, !value)}
|
||||||
|
>{text}</Toggle
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
<div>
|
||||||
|
<Label>Fontsize:</Label>
|
||||||
|
<Select items={countries} bind:value={selected} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>Background color:</Label>
|
||||||
|
<Input type="color" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
144
src/lib/components/UploadModal.svelte
Normal file
144
src/lib/components/UploadModal.svelte
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button, Dropzone, Modal, Spinner } from 'flowbite-svelte';
|
||||||
|
import FileUpload from './FileUpload.svelte';
|
||||||
|
import { processFiles } from '$lib/upload';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { formatBytes, scanFiles } from '$lib/util/upload';
|
||||||
|
|
||||||
|
export let open = false;
|
||||||
|
|
||||||
|
let promise: Promise<void>;
|
||||||
|
let files: FileList | undefined = undefined;
|
||||||
|
|
||||||
|
async function onUpload() {
|
||||||
|
if (files) {
|
||||||
|
promise = processFiles([...files]).then(() => {
|
||||||
|
open = false;
|
||||||
|
});
|
||||||
|
} else if (draggedFiles) {
|
||||||
|
promise = processFiles(draggedFiles).then(() => {
|
||||||
|
open = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
files = undefined;
|
||||||
|
draggedFiles = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let storageSpace = 'Loading...';
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
navigator.storage.estimate().then(({ usage, quota }) => {
|
||||||
|
if (usage && quota) {
|
||||||
|
storageSpace = `Storage: ${formatBytes(usage)} / ${formatBytes(quota)}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let filePromises: Promise<File>[];
|
||||||
|
let draggedFiles: File[] | undefined;
|
||||||
|
|
||||||
|
const dropHandle = async (event: DragEvent) => {
|
||||||
|
draggedFiles = [];
|
||||||
|
filePromises = [];
|
||||||
|
event.preventDefault();
|
||||||
|
activeStyle = defaultStyle;
|
||||||
|
|
||||||
|
if (event?.dataTransfer?.items) {
|
||||||
|
for (const item of [...event.dataTransfer.items]) {
|
||||||
|
const entry = item.webkitGetAsEntry();
|
||||||
|
if (entry && entry.isDirectory) {
|
||||||
|
await scanFiles(entry, filePromises);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.kind === 'file') {
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
draggedFiles.push(file);
|
||||||
|
draggedFiles = draggedFiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filePromises && filePromises.length > 0) {
|
||||||
|
const files = await Promise.all(filePromises);
|
||||||
|
if (files) {
|
||||||
|
draggedFiles = [...draggedFiles, ...files];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let defaultStyle =
|
||||||
|
'flex flex-col justify-center items-center w-full h-64 bg-gray-50 rounded-lg border-2 border-gray-300 border-dashed cursor-pointer dark:hover:bg-bray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600';
|
||||||
|
let highlightStyle =
|
||||||
|
'flex flex-col justify-center items-center w-full h-64 bg-gray-50 rounded-lg border-2 border-gray-300 border-dashed cursor-pointer dark:bg-bray-800 dark:bg-gray-700 bg-gray-100 dark:border-gray-600 dark:border-gray-500 dark:bg-gray-600';
|
||||||
|
|
||||||
|
let activeStyle = defaultStyle;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal title="Upload" bind:open outsideclose on:close={reset}>
|
||||||
|
{#await promise}
|
||||||
|
<h2 class="justify-center flex">Loading...</h2>
|
||||||
|
<div class="text-center"><Spinner /></div>
|
||||||
|
{:then}
|
||||||
|
<Dropzone
|
||||||
|
id="dropzone"
|
||||||
|
on:drop={dropHandle}
|
||||||
|
on:dragover={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
activeStyle = highlightStyle;
|
||||||
|
}}
|
||||||
|
on:dragleave={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
activeStyle = defaultStyle;
|
||||||
|
}}
|
||||||
|
on:click={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
defaultClass={activeStyle}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mb-3 w-10 h-10 text-gray-400"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
><path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||||
|
/></svg
|
||||||
|
>
|
||||||
|
{#if files}
|
||||||
|
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Upload {files.length}
|
||||||
|
{files.length > 1 ? 'files' : 'file'}?
|
||||||
|
</p>
|
||||||
|
{:else if draggedFiles && draggedFiles.length > 0}
|
||||||
|
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Upload {draggedFiles.length} hih
|
||||||
|
{draggedFiles.length > 1 ? 'files' : 'file'}?
|
||||||
|
</p>
|
||||||
|
{:else}
|
||||||
|
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Drag and drop / <FileUpload bind:files accept=".mokuro,.zip,.cbz" multiple
|
||||||
|
>choose files</FileUpload
|
||||||
|
> /
|
||||||
|
<FileUpload bind:files webkitdirectory>choose directory</FileUpload>
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</Dropzone>
|
||||||
|
|
||||||
|
<p class=" text-sm text-gray-500 dark:text-gray-400 text-center">{storageSpace}</p>
|
||||||
|
<div class="flex flex-1 flex-col gap-2">
|
||||||
|
<Button outline on:click={reset} disabled={!files && !draggedFiles} color="dark">Reset</Button
|
||||||
|
>
|
||||||
|
<Button outline on:click={onUpload} disabled={!files && !draggedFiles}>Upload</Button>
|
||||||
|
</div>
|
||||||
|
{/await}
|
||||||
|
</Modal>
|
||||||
@@ -35,17 +35,7 @@ function getDetails(file: File) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function processFiles(files: File[]) {
|
||||||
// export async function processVolumes(volumes: Volume[]) {
|
|
||||||
// for (const { mokuroData, volumeName, archiveFile, files } of volumes) {
|
|
||||||
// const { title_uuid } = mokuroData
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
export async function processFiles(fileList: FileList) {
|
|
||||||
const files = [...fileList]
|
|
||||||
const zipTypes = ['zip', 'cbz']
|
const zipTypes = ['zip', 'cbz']
|
||||||
const volumes: Record<string, Volume> = {};
|
const volumes: Record<string, Volume> = {};
|
||||||
|
|
||||||
@@ -64,7 +54,10 @@ export async function processFiles(fileList: FileList) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'image/jpeg' || type === 'image/png') {
|
const mimeType = type || getMimeType(file.name)
|
||||||
|
|
||||||
|
if (mimeType === 'image/jpeg' || mimeType === 'image/png') {
|
||||||
|
|
||||||
if (webkitRelativePath) {
|
if (webkitRelativePath) {
|
||||||
const imageName = webkitRelativePath.split('/').at(-1)
|
const imageName = webkitRelativePath.split('/').at(-1)
|
||||||
const vol = webkitRelativePath.split('/').at(-2)
|
const vol = webkitRelativePath.split('/').at(-2)
|
||||||
|
|||||||
@@ -4,3 +4,41 @@ export async function requestPersistentStorage() {
|
|||||||
console.log(`Persisted storage granted: ${isPersisted}`);
|
console.log(`Persisted storage granted: ${isPersisted}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatBytes(bytes: number, decimals = 2) {
|
||||||
|
if (bytes === 0) return '0 B';
|
||||||
|
|
||||||
|
const k = 1024;
|
||||||
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
|
||||||
|
return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFile(fileEntry: FileSystemFileEntry) {
|
||||||
|
try {
|
||||||
|
return new Promise<File>((resolve, reject) => fileEntry.file(resolve, reject));
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function scanFiles(item: FileSystemEntry, files: Promise<File | undefined>[]) {
|
||||||
|
if (item.isDirectory) {
|
||||||
|
const directoryReader = (item as FileSystemDirectoryEntry).createReader();
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
directoryReader.readEntries((entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry.isFile) {
|
||||||
|
files.push(getFile(entry as FileSystemFileEntry))
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
scanFiles(entry, files);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,16 @@
|
|||||||
import { ExclamationCircleOutline } from 'flowbite-svelte-icons';
|
import { ExclamationCircleOutline } from 'flowbite-svelte-icons';
|
||||||
import { db } from '$lib/catalog/db';
|
import { db } from '$lib/catalog/db';
|
||||||
|
|
||||||
const manga = $currentManga;
|
const manga = $currentManga?.sort((a, b) => {
|
||||||
|
if (a.volumeName < b.volumeName) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.volumeName > b.volumeName) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
let popupModal = false;
|
let popupModal = false;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
let promise: Promise<void>;
|
let promise: Promise<void>;
|
||||||
|
|
||||||
async function onUpload(files: FileList) {
|
async function onUpload(files: FileList) {
|
||||||
promise = processFiles(files);
|
promise = processFiles([...files]);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user