Extracted language selector logic into a custom hook
This commit is contained in:
parent
6f69aaf236
commit
85106d1735
@ -1,15 +1,17 @@
|
|||||||
import Chip from "components/Chip";
|
import Chip from "components/Chip";
|
||||||
import Img, { getAssetURL, ImageQuality } from "components/Img";
|
import Img, {
|
||||||
|
getAssetFilename,
|
||||||
|
getAssetURL,
|
||||||
|
ImageQuality,
|
||||||
|
} from "components/Img";
|
||||||
import Button from "components/Inputs/Button";
|
import Button from "components/Inputs/Button";
|
||||||
import LanguageSwitcher from "components/Inputs/LanguageSwitcher";
|
|
||||||
import RecorderChip from "components/RecorderChip";
|
import RecorderChip from "components/RecorderChip";
|
||||||
import ToolTip from "components/ToolTip";
|
import ToolTip from "components/ToolTip";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { GetLibraryItemScansQuery } from "graphql/generated";
|
import { GetLibraryItemScansQuery } from "graphql/generated";
|
||||||
import { useRouter } from "next/router";
|
import useSmartLanguage from "hooks/useSmartLanguage";
|
||||||
import { AppStaticProps } from "queries/getAppStaticProps";
|
import { AppStaticProps } from "queries/getAppStaticProps";
|
||||||
import { getPreferredLanguage, getStatusDescription } from "queries/helpers";
|
import { getStatusDescription, isInteger } from "queries/helpers";
|
||||||
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from "react";
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setLightboxOpen: Dispatch<SetStateAction<boolean>>;
|
setLightboxOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
@ -62,52 +64,28 @@ export default function ScanSet(props: Props): JSX.Element {
|
|||||||
langui,
|
langui,
|
||||||
content,
|
content,
|
||||||
} = props;
|
} = props;
|
||||||
const appLayout = useAppLayout();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const [selectedScan, setSelectedScan] = useState<Props["scanSet"][number]>();
|
const [selectedScan, LanguageSwitcher] = useSmartLanguage({
|
||||||
const scanLocales: Map<string, number> = new Map();
|
items: scanSet,
|
||||||
|
languages: languages,
|
||||||
const [selectedScanIndex, setSelectedScanIndex] = useState<
|
languageExtractor: (item) => item?.language?.data?.attributes?.code,
|
||||||
number | undefined
|
transform: (item) => {
|
||||||
>();
|
item?.pages?.data.sort((a, b) => {
|
||||||
|
|
||||||
scanSet.map((scan, index) => {
|
|
||||||
if (scan?.language?.data?.attributes?.code) {
|
|
||||||
scanLocales.set(scan.language.data.attributes.code, index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
setSelectedScanIndex(
|
|
||||||
getPreferredLanguage(
|
|
||||||
appLayout.preferredLanguages ?? [router.locale],
|
|
||||||
scanLocales
|
|
||||||
)
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [appLayout.preferredLanguages]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedScanIndex !== undefined) {
|
|
||||||
const selectedScanSet = scanSet[selectedScanIndex];
|
|
||||||
selectedScanSet?.pages?.data.sort((a, b) => {
|
|
||||||
function isInteger(value: string): boolean {
|
|
||||||
// eslint-disable-next-line require-unicode-regexp
|
|
||||||
return /^\d+$/.test(value);
|
|
||||||
}
|
|
||||||
function getFileName(path: string): string {
|
|
||||||
let result = path.split("/");
|
|
||||||
result = result[result.length - 1].split(".");
|
|
||||||
result = result
|
|
||||||
.splice(0, result.length - 1)
|
|
||||||
.join(".")
|
|
||||||
.split("_");
|
|
||||||
return result[0];
|
|
||||||
}
|
|
||||||
if (a.attributes?.url && b.attributes?.url) {
|
if (a.attributes?.url && b.attributes?.url) {
|
||||||
const aName = getFileName(a.attributes.url);
|
let aName = getAssetFilename(a.attributes.url);
|
||||||
const bName = getFileName(b.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)) {
|
if (isInteger(aName) && isInteger(bName)) {
|
||||||
return parseInt(aName, 10) - parseInt(bName, 10);
|
return parseInt(aName, 10) - parseInt(bName, 10);
|
||||||
}
|
}
|
||||||
@ -115,10 +93,9 @@ export default function ScanSet(props: Props): JSX.Element {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
setSelectedScan(selectedScanSet);
|
return item;
|
||||||
}
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
});
|
||||||
}, [selectedScanIndex]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -144,12 +121,7 @@ export default function ScanSet(props: Props): JSX.Element {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<LanguageSwitcher
|
<LanguageSwitcher />
|
||||||
languages={languages}
|
|
||||||
locales={scanLocales}
|
|
||||||
localesIndex={selectedScanIndex}
|
|
||||||
setLocalesIndex={setSelectedScanIndex}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="grid place-items-center place-content-center">
|
<div className="grid place-items-center place-content-center">
|
||||||
<p className="font-headers">{langui.status}:</p>
|
<p className="font-headers">{langui.status}:</p>
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
import Chip from "components/Chip";
|
import Chip from "components/Chip";
|
||||||
import Img, { getAssetURL, ImageQuality } from "components/Img";
|
import Img, { getAssetURL, ImageQuality } from "components/Img";
|
||||||
import LanguageSwitcher from "components/Inputs/LanguageSwitcher";
|
|
||||||
import RecorderChip from "components/RecorderChip";
|
import RecorderChip from "components/RecorderChip";
|
||||||
import ToolTip from "components/ToolTip";
|
import ToolTip from "components/ToolTip";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import {
|
import {
|
||||||
GetLibraryItemScansQuery,
|
GetLibraryItemScansQuery,
|
||||||
UploadImageFragment,
|
UploadImageFragment,
|
||||||
} from "graphql/generated";
|
} from "graphql/generated";
|
||||||
import { useRouter } from "next/router";
|
import useSmartLanguage from "hooks/useSmartLanguage";
|
||||||
import { AppStaticProps } from "queries/getAppStaticProps";
|
import { AppStaticProps } from "queries/getAppStaticProps";
|
||||||
import { getPreferredLanguage, getStatusDescription } from "queries/helpers";
|
import { getStatusDescription } from "queries/helpers";
|
||||||
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from "react";
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setLightboxOpen: Dispatch<SetStateAction<boolean>>;
|
setLightboxOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
@ -40,38 +38,13 @@ export default function ScanSetCover(props: Props): JSX.Element {
|
|||||||
languages,
|
languages,
|
||||||
langui,
|
langui,
|
||||||
} = props;
|
} = props;
|
||||||
const appLayout = useAppLayout();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const [selectedScan, setSelectedScan] = useState<Props["images"][number]>();
|
const [selectedScan, LanguageSwitcher] = useSmartLanguage({
|
||||||
const scanLocales: Map<string, number> = new Map();
|
items: images,
|
||||||
|
languages: languages,
|
||||||
const [selectedScanIndex, setSelectedScanIndex] = useState<
|
languageExtractor: (item) => item?.language?.data?.attributes?.code,
|
||||||
number | undefined
|
|
||||||
>();
|
|
||||||
|
|
||||||
images.map((scan, index) => {
|
|
||||||
if (scan?.language?.data?.attributes?.code) {
|
|
||||||
scanLocales.set(scan.language.data.attributes.code, index);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
setSelectedScanIndex(
|
|
||||||
getPreferredLanguage(
|
|
||||||
appLayout.preferredLanguages ?? [router.locale],
|
|
||||||
scanLocales
|
|
||||||
)
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [appLayout.preferredLanguages]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedScanIndex !== undefined)
|
|
||||||
setSelectedScan(images[selectedScanIndex]);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [selectedScanIndex]);
|
|
||||||
|
|
||||||
const coverImages: UploadImageFragment[] = [];
|
const coverImages: UploadImageFragment[] = [];
|
||||||
if (selectedScan?.obi_belt?.full?.data?.attributes)
|
if (selectedScan?.obi_belt?.full?.data?.attributes)
|
||||||
coverImages.push(selectedScan.obi_belt.full.data.attributes);
|
coverImages.push(selectedScan.obi_belt.full.data.attributes);
|
||||||
@ -105,12 +78,7 @@ export default function ScanSetCover(props: Props): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row flex-wrap gap-4 pb-6 place-items-center">
|
<div className="flex flex-row flex-wrap gap-4 pb-6 place-items-center">
|
||||||
<LanguageSwitcher
|
<LanguageSwitcher />
|
||||||
languages={languages}
|
|
||||||
locales={scanLocales}
|
|
||||||
localesIndex={selectedScanIndex}
|
|
||||||
setLocalesIndex={setSelectedScanIndex}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="grid place-items-center place-content-center">
|
<div className="grid place-items-center place-content-center">
|
||||||
<p className="font-headers">{langui.status}:</p>
|
<p className="font-headers">{langui.status}:</p>
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { GetPostQuery } from "graphql/generated";
|
import { GetPostQuery } from "graphql/generated";
|
||||||
import { useRouter } from "next/router";
|
import useSmartLanguage from "hooks/useSmartLanguage";
|
||||||
import { AppStaticProps } from "queries/getAppStaticProps";
|
import { AppStaticProps } from "queries/getAppStaticProps";
|
||||||
import {
|
import { getStatusDescription, prettySlug } from "queries/helpers";
|
||||||
getPreferredLanguage,
|
|
||||||
getStatusDescription,
|
|
||||||
prettySlug,
|
|
||||||
} from "queries/helpers";
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
import AppLayout from "./AppLayout";
|
import AppLayout from "./AppLayout";
|
||||||
import Chip from "./Chip";
|
import Chip from "./Chip";
|
||||||
import HorizontalLine from "./HorizontalLine";
|
import HorizontalLine from "./HorizontalLine";
|
||||||
import LanguageSwitcher from "./Inputs/LanguageSwitcher";
|
|
||||||
import Markdawn from "./Markdown/Markdawn";
|
import Markdawn from "./Markdown/Markdawn";
|
||||||
import TOC from "./Markdown/TOC";
|
import TOC from "./Markdown/TOC";
|
||||||
import ReturnButton, { ReturnButtonType } from "./PanelComponents/ReturnButton";
|
import ReturnButton, { ReturnButtonType } from "./PanelComponents/ReturnButton";
|
||||||
@ -23,9 +16,12 @@ import ToolTip from "./ToolTip";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
post: Exclude<
|
post: Exclude<
|
||||||
GetPostQuery["posts"],
|
Exclude<
|
||||||
|
GetPostQuery["posts"],
|
||||||
|
null | undefined
|
||||||
|
>["data"][number]["attributes"],
|
||||||
null | undefined
|
null | undefined
|
||||||
>["data"][number]["attributes"];
|
>;
|
||||||
langui: AppStaticProps["langui"];
|
langui: AppStaticProps["langui"];
|
||||||
languages: AppStaticProps["languages"];
|
languages: AppStaticProps["languages"];
|
||||||
currencies: AppStaticProps["currencies"];
|
currencies: AppStaticProps["currencies"];
|
||||||
@ -56,54 +52,18 @@ export default function Post(props: Props): JSX.Element {
|
|||||||
} = props;
|
} = props;
|
||||||
const displayTitle = props.displayTitle ?? true;
|
const displayTitle = props.displayTitle ?? true;
|
||||||
|
|
||||||
const appLayout = useAppLayout();
|
const [selectedTranslation, LanguageSwitcher] = useSmartLanguage({
|
||||||
const router = useRouter();
|
items: post.translations,
|
||||||
|
languages: languages,
|
||||||
const [selectedTranslation, setSelectedTranslation] = useState<
|
languageExtractor: (item) => item?.language?.data?.attributes?.code,
|
||||||
| Exclude<
|
});
|
||||||
Exclude<Props["post"], null | undefined>["translations"],
|
|
||||||
null | undefined
|
|
||||||
>[number]
|
|
||||||
>();
|
|
||||||
const translationLocales: Map<string, number> = new Map();
|
|
||||||
|
|
||||||
const [selectedTranslationIndex, setSelectedTranslationIndex] = useState<
|
|
||||||
number | undefined
|
|
||||||
>();
|
|
||||||
|
|
||||||
if (post?.translations) {
|
|
||||||
post.translations.map((translation, index) => {
|
|
||||||
if (translation?.language?.data?.attributes?.code) {
|
|
||||||
translationLocales.set(
|
|
||||||
translation.language.data.attributes.code,
|
|
||||||
index
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
setSelectedTranslationIndex(
|
|
||||||
getPreferredLanguage(
|
|
||||||
appLayout.preferredLanguages ?? [router.locale],
|
|
||||||
translationLocales
|
|
||||||
)
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [appLayout.preferredLanguages]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedTranslationIndex !== undefined)
|
|
||||||
setSelectedTranslation(post?.translations?.[selectedTranslationIndex]);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [selectedTranslationIndex]);
|
|
||||||
|
|
||||||
const thumbnail =
|
const thumbnail =
|
||||||
selectedTranslation?.thumbnail?.data?.attributes ??
|
selectedTranslation?.thumbnail?.data?.attributes ??
|
||||||
post?.thumbnail?.data?.attributes;
|
post.thumbnail?.data?.attributes;
|
||||||
|
|
||||||
const body = selectedTranslation?.body ?? "";
|
const body = selectedTranslation?.body ?? "";
|
||||||
const title = selectedTranslation?.title ?? prettySlug(post?.slug);
|
const title = selectedTranslation?.title ?? prettySlug(post.slug);
|
||||||
const except = selectedTranslation?.excerpt ?? "";
|
const except = selectedTranslation?.excerpt ?? "";
|
||||||
|
|
||||||
const subPanel =
|
const subPanel =
|
||||||
@ -137,7 +97,7 @@ export default function Post(props: Props): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{post?.authors && post.authors.data.length > 0 && (
|
{post.authors && post.authors.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Authors"}:</p>
|
<p className="font-headers">{"Authors"}:</p>
|
||||||
<div className="grid place-items-center place-content-center gap-2">
|
<div className="grid place-items-center place-content-center gap-2">
|
||||||
@ -183,15 +143,8 @@ export default function Post(props: Props): JSX.Element {
|
|||||||
title={title}
|
title={title}
|
||||||
description={except}
|
description={except}
|
||||||
langui={langui}
|
langui={langui}
|
||||||
categories={post?.categories}
|
categories={post.categories}
|
||||||
languageSwitcher={
|
languageSwitcher={<LanguageSwitcher />}
|
||||||
<LanguageSwitcher
|
|
||||||
languages={languages}
|
|
||||||
locales={translationLocales}
|
|
||||||
localesIndex={selectedTranslationIndex}
|
|
||||||
setLocalesIndex={setSelectedTranslationIndex}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
@ -200,12 +153,7 @@ export default function Post(props: Props): JSX.Element {
|
|||||||
<>
|
<>
|
||||||
{displayLanguageSwitcher && (
|
{displayLanguageSwitcher && (
|
||||||
<div className="grid place-content-end place-items-start">
|
<div className="grid place-content-end place-items-start">
|
||||||
<LanguageSwitcher
|
<LanguageSwitcher />
|
||||||
languages={languages}
|
|
||||||
locales={translationLocales}
|
|
||||||
localesIndex={selectedTranslationIndex}
|
|
||||||
setLocalesIndex={setSelectedTranslationIndex}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{displayTitle && (
|
{displayTitle && (
|
||||||
|
184
src/components/PostPage.tsx
Normal file
184
src/components/PostPage.tsx
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import { GetPostQuery } from "graphql/generated";
|
||||||
|
import useSmartLanguage from "hooks/useSmartLanguage";
|
||||||
|
import { AppStaticProps } from "queries/getAppStaticProps";
|
||||||
|
import { getStatusDescription, prettySlug } from "queries/helpers";
|
||||||
|
import AppLayout from "./AppLayout";
|
||||||
|
import Chip from "./Chip";
|
||||||
|
import HorizontalLine from "./HorizontalLine";
|
||||||
|
import Markdawn from "./Markdown/Markdawn";
|
||||||
|
import TOC from "./Markdown/TOC";
|
||||||
|
import ReturnButton, { ReturnButtonType } from "./PanelComponents/ReturnButton";
|
||||||
|
import ContentPanel from "./Panels/ContentPanel";
|
||||||
|
import SubPanel from "./Panels/SubPanel";
|
||||||
|
import RecorderChip from "./RecorderChip";
|
||||||
|
import ThumbnailHeader from "./ThumbnailHeader";
|
||||||
|
import ToolTip from "./ToolTip";
|
||||||
|
|
||||||
|
export type Post = Exclude<
|
||||||
|
Exclude<
|
||||||
|
GetPostQuery["posts"],
|
||||||
|
null | undefined
|
||||||
|
>["data"][number]["attributes"],
|
||||||
|
null | undefined
|
||||||
|
>;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
post: Post;
|
||||||
|
langui: AppStaticProps["langui"];
|
||||||
|
languages: AppStaticProps["languages"];
|
||||||
|
currencies: AppStaticProps["currencies"];
|
||||||
|
returnHref?: string;
|
||||||
|
returnTitle?: string | null | undefined;
|
||||||
|
displayCredits?: boolean;
|
||||||
|
displayToc?: boolean;
|
||||||
|
displayThumbnailHeader?: boolean;
|
||||||
|
displayTitle?: boolean;
|
||||||
|
displayLanguageSwitcher?: boolean;
|
||||||
|
prependBody?: JSX.Element;
|
||||||
|
appendBody?: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PostPage(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
post,
|
||||||
|
langui,
|
||||||
|
languages,
|
||||||
|
returnHref,
|
||||||
|
returnTitle,
|
||||||
|
displayCredits,
|
||||||
|
displayToc,
|
||||||
|
displayThumbnailHeader,
|
||||||
|
displayLanguageSwitcher,
|
||||||
|
appendBody,
|
||||||
|
prependBody,
|
||||||
|
} = props;
|
||||||
|
const displayTitle = props.displayTitle ?? true;
|
||||||
|
|
||||||
|
const [selectedTranslation, LanguageSwitcher] = useSmartLanguage({
|
||||||
|
items: post.translations,
|
||||||
|
languages: languages,
|
||||||
|
languageExtractor: (item) => item?.language?.data?.attributes?.code,
|
||||||
|
});
|
||||||
|
|
||||||
|
const thumbnail =
|
||||||
|
selectedTranslation?.thumbnail?.data?.attributes ??
|
||||||
|
post.thumbnail?.data?.attributes;
|
||||||
|
|
||||||
|
const body = selectedTranslation?.body ?? "";
|
||||||
|
const title = selectedTranslation?.title ?? prettySlug(post.slug);
|
||||||
|
const except = selectedTranslation?.excerpt ?? "";
|
||||||
|
|
||||||
|
const subPanel =
|
||||||
|
returnHref || returnTitle || displayCredits || displayToc ? (
|
||||||
|
<SubPanel>
|
||||||
|
{returnHref && returnTitle && (
|
||||||
|
<ReturnButton
|
||||||
|
href={returnHref}
|
||||||
|
title={returnTitle}
|
||||||
|
langui={langui}
|
||||||
|
displayOn={ReturnButtonType.desktop}
|
||||||
|
horizontalLine
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{displayCredits && (
|
||||||
|
<>
|
||||||
|
{selectedTranslation && (
|
||||||
|
<div className="grid grid-flow-col place-items-center place-content-center gap-2">
|
||||||
|
<p className="font-headers">{langui.status}:</p>
|
||||||
|
|
||||||
|
<ToolTip
|
||||||
|
content={getStatusDescription(
|
||||||
|
selectedTranslation.status,
|
||||||
|
langui
|
||||||
|
)}
|
||||||
|
maxWidth={"20rem"}
|
||||||
|
>
|
||||||
|
<Chip>{selectedTranslation.status}</Chip>
|
||||||
|
</ToolTip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{post.authors && post.authors.data.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<p className="font-headers">{"Authors"}:</p>
|
||||||
|
<div className="grid place-items-center place-content-center gap-2">
|
||||||
|
{post.authors.data.map((author) => (
|
||||||
|
<>
|
||||||
|
{author.attributes && (
|
||||||
|
<RecorderChip
|
||||||
|
key={author.id}
|
||||||
|
langui={langui}
|
||||||
|
recorder={author.attributes}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<HorizontalLine />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{displayToc && <TOC text={body} title={title} />}
|
||||||
|
</SubPanel>
|
||||||
|
) : undefined;
|
||||||
|
|
||||||
|
const contentPanel = (
|
||||||
|
<ContentPanel>
|
||||||
|
{returnHref && returnTitle && (
|
||||||
|
<ReturnButton
|
||||||
|
href={returnHref}
|
||||||
|
title={returnTitle}
|
||||||
|
langui={langui}
|
||||||
|
displayOn={ReturnButtonType.mobile}
|
||||||
|
horizontalLine
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{displayThumbnailHeader ? (
|
||||||
|
<>
|
||||||
|
<ThumbnailHeader
|
||||||
|
thumbnail={thumbnail}
|
||||||
|
title={title}
|
||||||
|
description={except}
|
||||||
|
langui={langui}
|
||||||
|
categories={post.categories}
|
||||||
|
languageSwitcher={<LanguageSwitcher />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HorizontalLine />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{displayLanguageSwitcher && (
|
||||||
|
<div className="grid place-content-end place-items-start">
|
||||||
|
<LanguageSwitcher />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{displayTitle && (
|
||||||
|
<h1 className="text-center flex gap-3 justify-center text-4xl my-16">
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{prependBody}
|
||||||
|
<Markdawn text={body} />
|
||||||
|
{appendBody}
|
||||||
|
</ContentPanel>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppLayout
|
||||||
|
navTitle={title}
|
||||||
|
contentPanel={contentPanel}
|
||||||
|
subPanel={subPanel}
|
||||||
|
thumbnail={thumbnail ?? undefined}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
65
src/hooks/useSmartLanguage.tsx
Normal file
65
src/hooks/useSmartLanguage.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import LanguageSwitcher from "components/Inputs/LanguageSwitcher";
|
||||||
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { AppStaticProps } from "queries/getAppStaticProps";
|
||||||
|
import { getPreferredLanguage } from "queries/helpers";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
|
interface Props<T> {
|
||||||
|
items: T[];
|
||||||
|
languages: AppStaticProps["languages"];
|
||||||
|
languageExtractor: (item: T) => string | undefined;
|
||||||
|
transform?: (item: T) => T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useSmartLanguage<T>(
|
||||||
|
props: Props<T>
|
||||||
|
): [T | undefined, () => JSX.Element] {
|
||||||
|
const {
|
||||||
|
items,
|
||||||
|
languageExtractor,
|
||||||
|
languages,
|
||||||
|
transform = (item) => item,
|
||||||
|
} = props;
|
||||||
|
const appLayout = useAppLayout();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const availableLocales: Map<string, number> = useMemo(() => new Map(), []);
|
||||||
|
const [selectedTranslationIndex, setSelectedTranslationIndex] = useState<
|
||||||
|
number | undefined
|
||||||
|
>();
|
||||||
|
const [selectedTranslation, setSelectedTranslation] = useState<T>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
items.map((elem, index) => {
|
||||||
|
const result = languageExtractor(elem);
|
||||||
|
if (result !== undefined) availableLocales.set(result, index);
|
||||||
|
});
|
||||||
|
}, [availableLocales, items, languageExtractor]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedTranslationIndex(
|
||||||
|
getPreferredLanguage(
|
||||||
|
appLayout.preferredLanguages ?? [router.locale],
|
||||||
|
availableLocales
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [appLayout.preferredLanguages, availableLocales, router.locale]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedTranslationIndex !== undefined)
|
||||||
|
setSelectedTranslation(transform(items[selectedTranslationIndex]));
|
||||||
|
}, [items, selectedTranslationIndex, transform]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
selectedTranslation,
|
||||||
|
() => (
|
||||||
|
<LanguageSwitcher
|
||||||
|
languages={languages}
|
||||||
|
locales={availableLocales}
|
||||||
|
localesIndex={selectedTranslationIndex}
|
||||||
|
setLocalesIndex={setSelectedTranslationIndex}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import AppLayout from "components/AppLayout";
|
import AppLayout from "components/AppLayout";
|
||||||
import Chip from "components/Chip";
|
import Chip from "components/Chip";
|
||||||
import HorizontalLine from "components/HorizontalLine";
|
import HorizontalLine from "components/HorizontalLine";
|
||||||
import LanguageSwitcher from "components/Inputs/LanguageSwitcher";
|
|
||||||
import Markdawn from "components/Markdown/Markdawn";
|
import Markdawn from "components/Markdown/Markdawn";
|
||||||
import TOC from "components/Markdown/TOC";
|
import TOC from "components/Markdown/TOC";
|
||||||
import ReturnButton, {
|
import ReturnButton, {
|
||||||
@ -13,25 +12,22 @@ import PreviewLine from "components/PreviewLine";
|
|||||||
import RecorderChip from "components/RecorderChip";
|
import RecorderChip from "components/RecorderChip";
|
||||||
import ThumbnailHeader from "components/ThumbnailHeader";
|
import ThumbnailHeader from "components/ThumbnailHeader";
|
||||||
import ToolTip from "components/ToolTip";
|
import ToolTip from "components/ToolTip";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { GetContentTextQuery } from "graphql/generated";
|
import { GetContentTextQuery } from "graphql/generated";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||||
|
import useSmartLanguage from "hooks/useSmartLanguage";
|
||||||
import {
|
import {
|
||||||
GetStaticPathsContext,
|
GetStaticPathsContext,
|
||||||
GetStaticPathsResult,
|
GetStaticPathsResult,
|
||||||
GetStaticPropsContext,
|
GetStaticPropsContext,
|
||||||
} from "next";
|
} from "next";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
|
||||||
import {
|
import {
|
||||||
getPreferredLanguage,
|
|
||||||
getStatusDescription,
|
getStatusDescription,
|
||||||
prettyinlineTitle,
|
prettyinlineTitle,
|
||||||
prettyLanguage,
|
prettyLanguage,
|
||||||
prettySlug,
|
prettySlug,
|
||||||
} from "queries/helpers";
|
} from "queries/helpers";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
|
|
||||||
interface Props extends AppStaticProps {
|
interface Props extends AppStaticProps {
|
||||||
content: Exclude<
|
content: Exclude<
|
||||||
@ -46,53 +42,15 @@ interface Props extends AppStaticProps {
|
|||||||
|
|
||||||
export default function Content(props: Props): JSX.Element {
|
export default function Content(props: Props): JSX.Element {
|
||||||
const { langui, content, languages } = props;
|
const { langui, content, languages } = props;
|
||||||
const router = useRouter();
|
|
||||||
const appLayout = useAppLayout();
|
|
||||||
|
|
||||||
const isMobile = useMediaMobile();
|
const isMobile = useMediaMobile();
|
||||||
|
|
||||||
const [selectedTextSet, setSelectedTextSet] = useState<
|
const [selectedTextSet, LanguageSwitcher] = useSmartLanguage({
|
||||||
| Exclude<
|
items: content?.text_set,
|
||||||
Exclude<Props["content"], null | undefined>["text_set"],
|
languages: languages,
|
||||||
null | undefined
|
languageExtractor: (item) => item?.language?.data?.attributes?.code,
|
||||||
>[number]
|
});
|
||||||
>();
|
|
||||||
const [selectedTitle, setSelectedTitle] = useState<
|
|
||||||
| Exclude<
|
|
||||||
Exclude<Props["content"], null | undefined>["titles"],
|
|
||||||
null | undefined
|
|
||||||
>[number]
|
|
||||||
>();
|
|
||||||
const textSetLocales: Map<string, number> = new Map();
|
|
||||||
|
|
||||||
const [selectedTextSetIndex, setSelectedTextSetIndex] = useState<
|
const selectedTitle = content?.titles?.[0];
|
||||||
number | undefined
|
|
||||||
>();
|
|
||||||
|
|
||||||
if (content?.text_set) {
|
|
||||||
content.text_set.map((textSet, index) => {
|
|
||||||
if (textSet?.language?.data?.attributes?.code && textSet.text) {
|
|
||||||
textSetLocales.set(textSet.language.data.attributes.code, index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
setSelectedTextSetIndex(
|
|
||||||
getPreferredLanguage(
|
|
||||||
appLayout.preferredLanguages ?? [router.locale],
|
|
||||||
textSetLocales
|
|
||||||
)
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [appLayout.preferredLanguages]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedTextSetIndex !== undefined) {
|
|
||||||
setSelectedTextSet(content?.text_set?.[selectedTextSetIndex]);
|
|
||||||
setSelectedTitle(content?.titles?.[selectedTextSetIndex]);
|
|
||||||
}
|
|
||||||
}, [content?.text_set, content?.titles, selectedTextSetIndex]);
|
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
@ -252,16 +210,7 @@ export default function Content(props: Props): JSX.Element {
|
|||||||
type={content.type}
|
type={content.type}
|
||||||
categories={content.categories}
|
categories={content.categories}
|
||||||
langui={langui}
|
langui={langui}
|
||||||
languageSwitcher={
|
languageSwitcher={<LanguageSwitcher />}
|
||||||
selectedTextSet ? (
|
|
||||||
<LanguageSwitcher
|
|
||||||
locales={textSetLocales}
|
|
||||||
languages={props.languages}
|
|
||||||
localesIndex={selectedTextSetIndex}
|
|
||||||
setLocalesIndex={setSelectedTextSetIndex}
|
|
||||||
/>
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{content.previous_recommended?.data?.attributes && (
|
{content.previous_recommended?.data?.attributes && (
|
||||||
|
@ -10,9 +10,12 @@ import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
|
|||||||
|
|
||||||
interface Props extends AppStaticProps {
|
interface Props extends AppStaticProps {
|
||||||
post: Exclude<
|
post: Exclude<
|
||||||
GetPostQuery["posts"],
|
Exclude<
|
||||||
|
GetPostQuery["posts"],
|
||||||
|
null | undefined
|
||||||
|
>["data"][number]["attributes"],
|
||||||
null | undefined
|
null | undefined
|
||||||
>["data"][number]["attributes"];
|
>;
|
||||||
postId: Exclude<
|
postId: Exclude<
|
||||||
GetPostQuery["posts"],
|
GetPostQuery["posts"],
|
||||||
null | undefined
|
null | undefined
|
||||||
@ -45,7 +48,7 @@ export async function getStaticProps(
|
|||||||
slug: slug,
|
slug: slug,
|
||||||
language_code: context.locale ?? "en",
|
language_code: context.locale ?? "en",
|
||||||
});
|
});
|
||||||
if (!post.posts?.data[0]) return { notFound: true };
|
if (!post.posts?.data[0].attributes) return { notFound: true };
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...(await getAppStaticProps(context)),
|
...(await getAppStaticProps(context)),
|
||||||
post: post.posts.data[0].attributes,
|
post: post.posts.data[0].attributes,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user