some refactor
This commit is contained in:
parent
88e81853e6
commit
37d49069e5
@ -21,6 +21,7 @@ import Layout from "./components/layout/layout";
|
|||||||
import NotFoundPage from "./page/404";
|
import NotFoundPage from "./page/404";
|
||||||
import LoginPage from "./page/loginPage";
|
import LoginPage from "./page/loginPage";
|
||||||
import ProfilePage from "./page/profilesPage";
|
import ProfilePage from "./page/profilesPage";
|
||||||
|
import ContentInfoPage from "./page/contentInfoPage";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
@ -31,7 +32,8 @@ const App = () => {
|
|||||||
<Route path="/search" component={Gallery} />
|
<Route path="/search" component={Gallery} />
|
||||||
<Route path="/login" component={LoginPage} />
|
<Route path="/login" component={LoginPage} />
|
||||||
<Route path="/profile" component={ProfilePage}/>
|
<Route path="/profile" component={ProfilePage}/>
|
||||||
{/* <Route path="/doc/:id" component={<DocumentAbout />}></Route>
|
<Route path="/doc/:id" component={ContentInfoPage}/>
|
||||||
|
{/*
|
||||||
<Route path="/doc/:id/reader" component={<ReaderPage />}></Route>
|
<Route path="/doc/:id/reader" component={<ReaderPage />}></Route>
|
||||||
<Route path="/difference" component={<DifferencePage />}></Route>
|
<Route path="/difference" component={<DifferencePage />}></Route>
|
||||||
<Route path="/setting" component={<SettingPage />}></Route>
|
<Route path="/setting" component={<SettingPage />}></Route>
|
||||||
|
@ -2,6 +2,8 @@ import type { Document } from "dbtype/api";
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import TagBadge from "@/components/gallery/TagBadge";
|
import TagBadge from "@/components/gallery/TagBadge";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { Link, useLocation } from "wouter";
|
||||||
|
import { LazyImage } from "./LazyImage";
|
||||||
|
|
||||||
function clipTagsWhenOverflow(tags: string[], limit: number) {
|
function clipTagsWhenOverflow(tags: string[], limit: number) {
|
||||||
let l = 0;
|
let l = 0;
|
||||||
@ -15,48 +17,12 @@ function clipTagsWhenOverflow(tags: string[], limit: number) {
|
|||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
function LazyImage({ src, alt, className }: { src: string; alt: string; className?: string; }) {
|
|
||||||
const ref = useRef<HTMLImageElement>(null);
|
|
||||||
const [loaded, setLoaded] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ref.current) {
|
|
||||||
let toggle = false;
|
|
||||||
const observer = new IntersectionObserver((entries) => {
|
|
||||||
if (entries.some(x => x.isIntersecting)) {
|
|
||||||
setLoaded(true);
|
|
||||||
toggle = !toggle;
|
|
||||||
ref.current?.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 500, easing: "ease-in-out" });
|
|
||||||
observer.disconnect();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (toggle) {
|
|
||||||
console.log("fade out");
|
|
||||||
ref.current?.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 500, easing: "ease-in-out" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
observer.observe(ref.current);
|
|
||||||
return () => {
|
|
||||||
observer.disconnect();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <img
|
|
||||||
ref={ref}
|
|
||||||
src={loaded ? src : undefined}
|
|
||||||
alt={alt}
|
|
||||||
className={className}
|
|
||||||
loading="lazy"
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GalleryCard({
|
export function GalleryCard({
|
||||||
doc: x
|
doc: x
|
||||||
}: { doc: Document; }) {
|
}: { doc: Document; }) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const [clipCharCount, setClipCharCount] = useState(200);
|
const [clipCharCount, setClipCharCount] = useState(200);
|
||||||
|
const [location] = useLocation();
|
||||||
|
|
||||||
const artists = x.tags.filter(x => x.startsWith("artist:")).map(x => x.replace("artist:", ""));
|
const artists = x.tags.filter(x => x.startsWith("artist:")).map(x => x.replace("artist:", ""));
|
||||||
const groups = x.tags.filter(x => x.startsWith("group:")).map(x => x.replace("group:", ""));
|
const groups = x.tags.filter(x => x.startsWith("group:")).map(x => x.replace("group:", ""));
|
||||||
@ -84,11 +50,13 @@ export function GalleryCard({
|
|||||||
<LazyImage src={`/api/doc/${x.id}/comic/thumbnail`}
|
<LazyImage src={`/api/doc/${x.id}/comic/thumbnail`}
|
||||||
alt={x.title}
|
alt={x.title}
|
||||||
className="max-h-full max-w-full object-cover object-center"
|
className="max-h-full max-w-full object-cover object-center"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col">
|
||||||
<CardHeader className="flex-none">
|
<CardHeader className="flex-none">
|
||||||
<CardTitle>{x.title}</CardTitle>
|
<CardTitle>
|
||||||
|
<Link href={`/doc/${x.id}`} state={{fromUrl: location}}>{x.title}</Link>
|
||||||
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
{artists.join(", ")} {groups.length > 0 && "|"} {groups.join(", ")}
|
{artists.join(", ")} {groups.length > 0 && "|"} {groups.join(", ")}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
|
37
packages/client/src/components/gallery/LazyImage.tsx
Normal file
37
packages/client/src/components/gallery/LazyImage.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
export function LazyImage({ src, alt, className }: { src: string; alt: string; className?: string; }) {
|
||||||
|
const ref = useRef<HTMLImageElement>(null);
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ref.current) {
|
||||||
|
let toggle = false;
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
if (entries.some(x => x.isIntersecting)) {
|
||||||
|
setLoaded(true);
|
||||||
|
toggle = !toggle;
|
||||||
|
ref.current?.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 500, easing: "ease-in-out" });
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (toggle) {
|
||||||
|
console.log("fade out");
|
||||||
|
ref.current?.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 500, easing: "ease-in-out" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(ref.current);
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <img
|
||||||
|
ref={ref}
|
||||||
|
src={loaded ? src : undefined}
|
||||||
|
alt={alt}
|
||||||
|
className={className}
|
||||||
|
loading="lazy" />;
|
||||||
|
}
|
20
packages/client/src/page/contentInfoPage.tsx
Normal file
20
packages/client/src/page/contentInfoPage.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export interface ContentInfoPageProps {
|
||||||
|
params: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function ContentInfoPage({params}: ContentInfoPageProps) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h1>ContentInfoPage</h1>
|
||||||
|
{params.id}
|
||||||
|
<p>Find me in packages/client/src/page/contentInfoPage.tsx</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContentInfoPage;
|
@ -1,4 +1,4 @@
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { useLogin } from "@/state/user";
|
import { useLogin } from "@/state/user";
|
||||||
import { Redirect } from "wouter";
|
import { Redirect } from "wouter";
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export function ProfilePage() {
|
|||||||
return <Redirect to="/login" />;
|
return <Redirect to="/login" />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="m-4">
|
<div className="p-4">
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-2xl">Profile</CardTitle>
|
<CardTitle className="text-2xl">Profile</CardTitle>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { type Context, DefaultContext, DefaultState, Next } from "koa";
|
import type { Context } from "koa";
|
||||||
import Router from "koa-router";
|
import Router from "koa-router";
|
||||||
import { createReadableStreamFromZip, entriesByNaturalOrder, readZip } from "../util/zipwrap";
|
import { createReadableStreamFromZip, entriesByNaturalOrder, readZip } from "../util/zipwrap";
|
||||||
import type { ContentContext } from "./context";
|
import type { ContentContext } from "./context";
|
||||||
import { since_last_modified } from "./util";
|
import { since_last_modified } from "./util";
|
||||||
import type { ZipReader } from "@zip.js/zip.js";
|
import type { ZipReader } from "@zip.js/zip.js";
|
||||||
import type { FileHandle } from "node:fs/promises";
|
import type { FileHandle } from "node:fs/promises";
|
||||||
import { Readable, Writable } from "node:stream";
|
import { Readable } from "node:stream";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* zip stream cache.
|
* zip stream cache.
|
||||||
@ -91,11 +91,15 @@ async function renderZipImage(ctx: Context, path: string, page: number) {
|
|||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
nodeReadableStream.push(null);
|
nodeReadableStream.push(null);
|
||||||
releaseZip(path);
|
|
||||||
// setTimeout(() => {
|
|
||||||
// }, 500);
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
nodeReadableStream.on("error", (err) => {
|
||||||
|
console.error(err);
|
||||||
|
releaseZip(path);
|
||||||
|
});
|
||||||
|
nodeReadableStream.on("close", () => {
|
||||||
|
releaseZip(path);
|
||||||
|
});
|
||||||
|
|
||||||
ctx.body = nodeReadableStream;
|
ctx.body = nodeReadableStream;
|
||||||
ctx.response.length = entry.uncompressedSize;
|
ctx.response.length = entry.uncompressedSize;
|
||||||
|
@ -4,15 +4,12 @@ import { ZipReader, Reader, type Entry } from "@zip.js/zip.js";
|
|||||||
|
|
||||||
class FileReader extends Reader<FileHandle> {
|
class FileReader extends Reader<FileHandle> {
|
||||||
private fd: FileHandle;
|
private fd: FileHandle;
|
||||||
private offset: number;
|
|
||||||
constructor(fd: FileHandle) {
|
constructor(fd: FileHandle) {
|
||||||
super(fd);
|
super(fd);
|
||||||
this.fd = fd;
|
this.fd = fd;
|
||||||
this.offset = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
this.offset = 0;
|
|
||||||
this.size = (await this.fd.stat()).size;
|
this.size = (await this.fd.stat()).size;
|
||||||
}
|
}
|
||||||
close(): void {
|
close(): void {
|
||||||
|
Loading…
Reference in New Issue
Block a user