Skip to content

Commit

Permalink
- Move reader preferences to client-side
Browse files Browse the repository at this point in the history
- Add pages to the library list
  • Loading branch information
LetrixZ committed Jul 13, 2024
1 parent 205e17c commit 54d4e7c
Show file tree
Hide file tree
Showing 19 changed files with 107 additions and 52 deletions.
2 changes: 1 addition & 1 deletion server/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "server"
version = "1.1.3"
version = "1.1.4"
edition = "2021"

[dependencies]
Expand Down
1 change: 1 addition & 0 deletions server/src/api/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ pub struct ArchiveListItem {
pub slug: String,
pub hash: String,
pub title: String,
pub pages: i16,
#[serde(skip_serializing_if = "Option::is_none")]
pub cover: Option<ImageDimensions>,
#[serde(skip_serializing_if = "<[_]>::is_empty")]
Expand Down
3 changes: 3 additions & 0 deletions server/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,8 @@ pub async fn search(
}
}

qb.push(", pages");

qb.push(", ARRAY_POSITION(")
.push_bind(&ids)
.push(",id) AS ord");
Expand All @@ -741,6 +743,7 @@ pub async fn search(
slug: row.get(1),
hash: row.get(2),
title: row.get(3),
pages: row.get(12),
cover,
artists: row.get::<Json<_>, _>(5).0,
circles: row.get::<Json<_>, _>(6).0,
Expand Down
2 changes: 1 addition & 1 deletion server/src/metadata/koharu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub fn add_metadata(info: Metadata, archive: &mut db::UpsertArchiveData) -> anyh
name: utils::parse_source_name(&url),
url: Some(format!(
"https://koharu.to{}",
url.split(":").last().unwrap()
url.split(':').last().unwrap()
)),
});
}
Expand Down
Binary file modified web/bun.lockb
Binary file not shown.
4 changes: 3 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "web-kit",
"version": "1.1.3",
"version": "1.1.4",
"private": true,
"scripts": {
"dev": "vite dev",
Expand Down Expand Up @@ -42,8 +42,10 @@
"type": "module",
"dependencies": {
"@tailwindcss/container-queries": "^0.1.1",
"@types/cookie": "^0.6.0",
"bits-ui": "^0.21.10",
"clsx": "^2.1.1",
"cookie": "^0.6.0",
"dayjs": "^1.11.11",
"dotenv": "^16.4.5",
"fflate": "^0.8.2",
Expand Down
7 changes: 6 additions & 1 deletion web/src/lib/components/list-item.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@
</script>

<div class="group h-auto w-auto space-y-2">
<a href={`/g/${archive.id}${$page.url.search}`} tabindex="-1">
<a href={`/g/${archive.id}${$page.url.search}`} tabindex="-1" class="relative">
<div
class="absolute bottom-1 end-1 w-fit rounded-md bg-neutral-900 p-1 text-xs font-bold text-white opacity-70"
>
{archive.pages}P
</div>
<div class="overflow-clip rounded-md shadow">
<img
class="bg-neutral-300 dark:bg-neutral-600"
Expand Down
29 changes: 27 additions & 2 deletions web/src/lib/components/reader-bar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
showBar,
} from '$lib/reader-store';
import { cn, remToPx, type BarPlacement } from '$lib/utils';
import cookie from 'cookie';
import dayjs from 'dayjs';
import { ArrowLeft, MenuIcon } from 'lucide-svelte';
import ChevronLeft from 'lucide-svelte/icons/chevron-left';
import ChevronRight from 'lucide-svelte/icons/chevron-right';
import { onMount } from 'svelte';
import { fly } from 'svelte/transition';
$: currentPage = $page.state.page || parseInt($page.params.page!);
Expand Down Expand Up @@ -58,9 +60,18 @@
}
}
let isMounted = true;
onMount(() => {
isMounted = true;
});
$: {
if (browser) {
document.cookie = `reader=${JSON.stringify($prefs)}; Path=/; Max-Age=${dayjs(dayjs().add(1, 'year')).diff(dayjs(), 'seconds')}`;
if (browser && isMounted) {
document.cookie = cookie.serialize('reader', JSON.stringify($prefs), {
path: '/',
maxAge: dayjs().add(1, 'year').diff(dayjs(), 'seconds'),
});
}
}
</script>
Expand Down Expand Up @@ -219,12 +230,26 @@
type="single"
value={$prefs.fitMode}
onValueChange={onModeChange}
class="flex flex-wrap"
>
<ToggleGroup.Item value={ImageFitMode.FitHeight}>Fit Height</ToggleGroup.Item>
<ToggleGroup.Item value={ImageFitMode.FillHeight}>Fill Height</ToggleGroup.Item>
<ToggleGroup.Item value={ImageFitMode.MinWidth}>Min Width</ToggleGroup.Item>
<ToggleGroup.Item value={ImageFitMode.MaxWidth}>Max Width</ToggleGroup.Item>
<ToggleGroup.Item value={ImageFitMode.ImageWidth}>Image Width</ToggleGroup.Item>
</ToggleGroup.Root>

<div class="flex items-center">
<Label for="min-width" class="w-full">Min width</Label>
<Input
id="min-width"
type="number"
value={$prefs.minWidth}
class="w-24"
on:change={(event) => ($prefs.minWidth = parseInt(event.currentTarget.value) || undefined)}
/>
</div>

<div class="flex items-center">
<Label for="max-width" class="w-full">Max width</Label>
<Input
Expand Down
37 changes: 22 additions & 15 deletions web/src/lib/components/reader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
nextPage,
preferencesOpen,
prefs,
previewLayout,
prevPage,
readerPage,
showBar,
Expand All @@ -24,8 +25,6 @@
let imageEl: HTMLImageElement;
let container: HTMLDivElement;
let previewLayout = false;
let pageState: (Image & { state: ImageState })[] = archive.images.map((image) => ({
...image,
state: 'idle',
Expand Down Expand Up @@ -106,28 +105,34 @@
return;
}
const base = `aspect-ratio: ${image.width && image.height && image.width / image.height};`;
switch ($prefs.fitMode) {
case ImageFitMode.MinWidth:
if (image.width && image.height) {
return `min-height: min(${image.height}px, 100%); aspect-ratio: ${image.width / image.height};`;
}
return;
case ImageFitMode.MaxWidth:
if ($prefs.maxWidth && image.width && image.height) {
return base + `max-height: ${($prefs.maxWidth * image.height) / image.width}px;`;
if (image.width && image.height) {
return `min-height: min(${image.height}px, 100%);`;
}
return base;
return;
case ImageFitMode.ImageWidth:
return base + `max-height: ${image.height}px;`;
return `max-height: ${image.height}px;`;
case ImageFitMode.FitHeight:
return base + 'max-height: 100%';
default:
return base;
return 'max-height: 100%;';
case ImageFitMode.FillHeight:
return 'min-height: 100%;';
}
};
const getImageStyle = ($prefs: ReaderPreferences) => {
switch ($prefs.fitMode) {
case ImageFitMode.ImageWidth:
return ``;
case ImageFitMode.MinWidth:
return `width: ${$prefs.minWidth}px;`;
case ImageFitMode.MaxWidth:
if ($prefs.maxWidth) {
return `max-width: clamp(0px, ${$prefs.maxWidth}px, 100%);`;
Expand All @@ -136,6 +141,8 @@
}
case ImageFitMode.FitHeight:
return `width: auto; max-height: 100%;`;
case ImageFitMode.FillHeight:
return `width: auto; min-height: 100%; object-fit: contain;`;
}
};
Expand Down Expand Up @@ -193,7 +200,7 @@
style={getContainerStyle($prefs, image)}
>
<a
class={cn('relative h-full flex-grow outline-none', previewLayout && 'bg-blue-500/50 ')}
class={cn('relative h-full flex-grow outline-none', $previewLayout && 'bg-blue-500/50 ')}
href={prevPageUrl}
draggable="false"
on:click|preventDefault={() => changePage($prevPage)}
Expand All @@ -205,7 +212,7 @@
on:click={() => ($showBar = !$showBar)}
/>
<a
class={cn('relative h-full flex-grow outline-none', previewLayout && 'bg-red-500/50')}
class={cn('relative h-full flex-grow outline-none', $previewLayout && 'bg-red-500/50')}
href={nextPageUrl}
draggable="false"
on:click|preventDefault={() => changePage($nextPage)}
Expand All @@ -214,7 +221,7 @@
</a>
</div>

{#if previewLayout}
{#if $previewLayout}
<div class="pointer-events-none fixed inset-0 flex h-full min-w-full max-w-full opacity-100">
<div class="relative flex h-full flex-grow items-center justify-center">
<span class="stroke rounded-md text-2xl font-semibold uppercase tracking-wider">
Expand All @@ -238,7 +245,7 @@
src={`${env.PUBLIC_CDN_URL}/image/${archive.hash}/${image?.filename}`}
loading="eager"
style={getImageStyle($prefs)}
class="m-auto bg-neutral-500"
class="m-auto"
on:error={() => toast.error('Failed to load the page')}
/>
</div>
Expand Down
3 changes: 1 addition & 2 deletions web/src/lib/components/ui/dialog/dialog-content.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script lang="ts">
import { cn, flyAndScale } from '$lib/utils.js';
import { Dialog as DialogPrimitive } from 'bits-ui';
import X from 'lucide-svelte/icons/x';
import * as Dialog from './index.js';
import { cn, flyAndScale } from '$lib/utils.js';
type $$Props = DialogPrimitive.ContentProps;
Expand Down
3 changes: 3 additions & 0 deletions web/src/lib/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface ArchiveListItem {
id: number;
hash: string;
title: string;
pages: number;
cover?: ImageDimensions;
artists?: Taxonomy[];
circles?: Taxonomy[];
Expand Down Expand Up @@ -110,6 +111,8 @@ export interface Task {

export enum ImageFitMode {
ImageWidth = 'image-width',
MinWidth = 'min-width',
MaxWidth = 'max-width',
FitHeight = 'fit-height',
FillHeight = 'fill-height',
}
2 changes: 1 addition & 1 deletion web/src/lib/reader-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const nextPage = writable<number | undefined>();
export const prefs = writable<ReaderPreferences>(preferencesSchema.parse({}));

export const readerTimeout = (() => {
let timeout = 0;
let timeout: NodeJS.Timeout;

const clear = () => clearTimeout(timeout);
const reset = () => {
Expand Down
3 changes: 2 additions & 1 deletion web/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ export function getMetadata(archive: Archive) {
}

export const preferencesSchema = z.object({
fitMode: z.nativeEnum(ImageFitMode).catch(ImageFitMode.FitHeight),
fitMode: z.nativeEnum(ImageFitMode).catch(ImageFitMode.FillHeight),
minWidth: z.number().optional().catch(1000),
maxWidth: z.number().optional().catch(1000),
barPlacement: z.enum(['top', 'bottom']).catch('bottom'),
});
Expand Down
20 changes: 9 additions & 11 deletions web/src/routes/(app)/g/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
});
const { createWriteStream } = await import('streamsaver');
const chunks: any[] = [];
const chunks: Uint8Array[] = [];
const promise = new Promise<void>(async (resolve, reject) => {
const promise = new Promise<void>((resolve, reject) => {
const fileStream = createWriteStream(`${generateFilename(archive)}.cbz`);
const writer = fileStream.getWriter();
Expand Down Expand Up @@ -70,7 +70,7 @@
metadataFile.push(strToU8(JSON.stringify(getMetadata(archive), null, 2)), true);
await pMap(
pMap(
archive.images,
async (image) => {
const url = `${env.PUBLIC_CDN_URL}/image/${archive.hash}/${image.filename}`;
Expand All @@ -91,18 +91,16 @@
task.update((task) => ({ ...task, progress: task.progress + 1 }));
},
{ concurrency: 3 }
);
).then(() => {
zip.end();
zip.end();
task.update((task) => ({ ...task, complete: true }));
task.update((task) => ({ ...task, complete: true }));
resolve();
resolve();
});
} catch (e) {
console.error(e);
await writer.abort();
reject(e);
writer.abort().then(() => reject(e));
}
});
Expand Down
8 changes: 0 additions & 8 deletions web/src/routes/(app)/g/[id]/read/[page]/+layout.server.ts

This file was deleted.

31 changes: 24 additions & 7 deletions web/src/routes/(app)/g/[id]/read/[page]/+layout@.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
<script lang="ts">
import ReaderBar from '$lib/components/reader-bar.svelte';
import { Toaster } from '$lib/components/ui/sonner';
import { prefs } from '$lib/reader-store';
import '../../../../../../app.pcss';
import cookie from 'cookie';
import { onMount } from 'svelte';
import '~/app.pcss';
import { prefs } from '~/lib/reader-store';
import type { ReaderPreferences } from '~/lib/utils';
export let data;
let isMounted = false;
$prefs = data.prefs;
</script>
onMount(() => {
const cookiePerfs = cookie.parse(document.cookie);
<slot></slot>
if (Object.entries(cookiePerfs).length) {
try {
$prefs = JSON.parse(cookiePerfs.reader) as ReaderPreferences;
} finally {
isMounted = true;
}
} else {
isMounted = true;
}
});
</script>

<Toaster richColors position="top-right" />

<ReaderBar />
{#if isMounted}
<ReaderBar />

<slot></slot>
{/if}
Loading

0 comments on commit 54d4e7c

Please sign in to comment.