232 lines
7.9 KiB
TypeScript
232 lines
7.9 KiB
TypeScript
import { Chip } from "components/Chip";
|
|
import { Img } from "components/Img";
|
|
import { Button } from "components/Inputs/Button";
|
|
import { RecorderChip } from "components/RecorderChip";
|
|
import { ToolTip } from "components/ToolTip";
|
|
import { GetLibraryItemScansQuery } from "graphql/generated";
|
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
|
import { getAssetFilename, getAssetURL, ImageQuality } from "helpers/img";
|
|
import { isInteger } from "helpers/numbers";
|
|
import {
|
|
filterHasAttributes,
|
|
getStatusDescription,
|
|
isDefined,
|
|
isDefinedAndNotEmpty,
|
|
} from "helpers/others";
|
|
|
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
|
import { Fragment, useMemo } from "react";
|
|
|
|
interface Props {
|
|
openLightBox: (images: string[], index?: number) => void;
|
|
scanSet: NonNullable<
|
|
NonNullable<
|
|
NonNullable<
|
|
NonNullable<
|
|
NonNullable<
|
|
GetLibraryItemScansQuery["libraryItems"]
|
|
>["data"][number]["attributes"]
|
|
>["contents"]
|
|
>["data"][number]["attributes"]
|
|
>["scan_set"]
|
|
>;
|
|
slug: string;
|
|
title: string;
|
|
languages: AppStaticProps["languages"];
|
|
langui: AppStaticProps["langui"];
|
|
content: NonNullable<
|
|
NonNullable<
|
|
NonNullable<
|
|
NonNullable<
|
|
GetLibraryItemScansQuery["libraryItems"]
|
|
>["data"][number]["attributes"]
|
|
>["contents"]
|
|
>["data"][number]["attributes"]
|
|
>["content"];
|
|
}
|
|
|
|
export function ScanSet(props: Props): JSX.Element {
|
|
const { openLightBox, scanSet, slug, title, languages, langui, content } =
|
|
props;
|
|
|
|
const [selectedScan, LanguageSwitcher] = useSmartLanguage({
|
|
items: scanSet,
|
|
languages: languages,
|
|
languageExtractor: (item) => item.language?.data?.attributes?.code,
|
|
transform: (item) => {
|
|
item.pages?.data.sort((a, b) => {
|
|
if (
|
|
a.attributes &&
|
|
b.attributes &&
|
|
isDefinedAndNotEmpty(a.attributes.url) &&
|
|
isDefinedAndNotEmpty(b.attributes.url)
|
|
) {
|
|
let aName = getAssetFilename(a.attributes.url);
|
|
let bName = getAssetFilename(b.attributes.url);
|
|
|
|
/*
|
|
* If the number is a succession of 0s, make the number
|
|
* incrementally smaller than 0 (i.e: 00 becomes -1)
|
|
*/
|
|
if (aName.replaceAll("0", "").length === 0) {
|
|
aName = (1 - aName.length).toString(10);
|
|
}
|
|
if (bName.replaceAll("0", "").length === 0) {
|
|
bName = (1 - bName.length).toString(10);
|
|
}
|
|
|
|
if (isInteger(aName) && isInteger(bName)) {
|
|
return parseInt(aName, 10) - parseInt(bName, 10);
|
|
}
|
|
return a.attributes.url.localeCompare(b.attributes.url);
|
|
}
|
|
return 0;
|
|
});
|
|
return item;
|
|
},
|
|
});
|
|
|
|
const pages = useMemo(
|
|
() => selectedScan && filterHasAttributes(selectedScan.pages?.data),
|
|
[selectedScan]
|
|
);
|
|
|
|
return (
|
|
<>
|
|
{selectedScan && isDefined(pages) && (
|
|
<div>
|
|
<div
|
|
className="flex flex-row flex-wrap place-items-center
|
|
gap-6 pt-10 text-base first-of-type:pt-0"
|
|
>
|
|
<h2 id={slug} className="text-2xl">
|
|
{title}
|
|
</h2>
|
|
|
|
<Chip>
|
|
{selectedScan.language?.data?.attributes?.code ===
|
|
selectedScan.source_language?.data?.attributes?.code
|
|
? "Scan"
|
|
: "Scanlation"}
|
|
</Chip>
|
|
</div>
|
|
|
|
<div className="flex flex-row flex-wrap place-items-center gap-4 pb-6">
|
|
{content?.data?.attributes &&
|
|
isDefinedAndNotEmpty(content.data.attributes.slug) && (
|
|
<Button
|
|
href={`/contents/${content.data.attributes.slug}`}
|
|
text={langui.open_content}
|
|
/>
|
|
)}
|
|
|
|
<LanguageSwitcher />
|
|
|
|
<div className="grid place-content-center place-items-center">
|
|
<p className="font-headers">{langui.status}:</p>
|
|
<ToolTip
|
|
content={getStatusDescription(selectedScan.status, langui)}
|
|
maxWidth={"20rem"}
|
|
>
|
|
<Chip>{selectedScan.status}</Chip>
|
|
</ToolTip>
|
|
</div>
|
|
|
|
{selectedScan.scanners && selectedScan.scanners.data.length > 0 && (
|
|
<div>
|
|
<p className="font-headers">{"Scanners"}:</p>
|
|
<div className="grid place-content-center place-items-center gap-2">
|
|
{filterHasAttributes(selectedScan.scanners.data).map(
|
|
(scanner) => (
|
|
<Fragment key={scanner.id}>
|
|
<RecorderChip
|
|
langui={langui}
|
|
recorder={scanner.attributes}
|
|
/>
|
|
</Fragment>
|
|
)
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{selectedScan.cleaners && selectedScan.cleaners.data.length > 0 && (
|
|
<div>
|
|
<p className="font-headers">{"Cleaners"}:</p>
|
|
<div className="grid place-content-center place-items-center gap-2">
|
|
{filterHasAttributes(selectedScan.cleaners.data).map(
|
|
(cleaner) => (
|
|
<Fragment key={cleaner.id}>
|
|
{cleaner.attributes && (
|
|
<RecorderChip
|
|
langui={langui}
|
|
recorder={cleaner.attributes}
|
|
/>
|
|
)}
|
|
</Fragment>
|
|
)
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{selectedScan.typesetters &&
|
|
selectedScan.typesetters.data.length > 0 && (
|
|
<div>
|
|
<p className="font-headers">{"Typesetters"}:</p>
|
|
<div className="grid place-content-center place-items-center gap-2">
|
|
{filterHasAttributes(selectedScan.typesetters.data).map(
|
|
(typesetter) => (
|
|
<Fragment key={typesetter.id}>
|
|
{typesetter.attributes && (
|
|
<RecorderChip
|
|
langui={langui}
|
|
recorder={typesetter.attributes}
|
|
/>
|
|
)}
|
|
</Fragment>
|
|
)
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{isDefinedAndNotEmpty(selectedScan.notes) && (
|
|
<ToolTip content={selectedScan.notes}>
|
|
<Chip>{"Notes"}</Chip>
|
|
</ToolTip>
|
|
)}
|
|
</div>
|
|
|
|
<div
|
|
className="grid items-end gap-8 border-b-[3px] border-dotted pb-12 last-of-type:border-0
|
|
desktop:grid-cols-[repeat(auto-fill,_minmax(10rem,1fr))] mobile:grid-cols-2"
|
|
>
|
|
{pages.map((page, index) => (
|
|
<div
|
|
key={page.id}
|
|
className="cursor-pointer transition-transform
|
|
drop-shadow-shade-lg hover:scale-[1.02]"
|
|
onClick={() => {
|
|
const images: string[] = [];
|
|
pages.map((image) => {
|
|
if (isDefinedAndNotEmpty(image.attributes.url))
|
|
images.push(
|
|
getAssetURL(image.attributes.url, ImageQuality.Large)
|
|
);
|
|
});
|
|
openLightBox(images, index);
|
|
}}
|
|
>
|
|
{page.attributes && (
|
|
<Img image={page.attributes} quality={ImageQuality.Small} />
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|