Improved stuff
This commit is contained in:
parent
6ae54c39d4
commit
bc0764c0d0
|
@ -27,7 +27,7 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
source: "/gallery",
|
||||
destination: "https://gallery.accords-library.com/",
|
||||
destination: "https://gallery.accords-library.com/posts",
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -5,7 +5,13 @@ import { AppStaticProps } from "graphql/getAppStaticProps";
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { prettyLanguage, prettySlug } from "helpers/formatters";
|
||||
import { getOgImage, ImageQuality } from "helpers/img";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
isUndefined,
|
||||
iterateMap,
|
||||
} from "helpers/others";
|
||||
// import { getClient, Indexes, search, SearchResult } from "helpers/search";
|
||||
|
||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||
|
@ -19,6 +25,7 @@ import { ButtonGroup } from "./Inputs/ButtonGroup";
|
|||
import { OrderableList } from "./Inputs/OrderableList";
|
||||
import { Select } from "./Inputs/Select";
|
||||
import { TextInput } from "./Inputs/TextInput";
|
||||
import { ContentPlaceholder } from "./PanelComponents/ContentPlaceholder";
|
||||
import { MainPanel } from "./Panels/MainPanel";
|
||||
import { Popup } from "./Popup";
|
||||
|
||||
|
@ -98,7 +105,7 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
||||
if (mainPanelOpen === true) {
|
||||
setMainPanelOpen(false);
|
||||
} else if (subPanel === true && contentPanel === true) {
|
||||
} else if (isDefined(subPanel) && isDefined(contentPanel)) {
|
||||
setSubPanelOpen(true);
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +123,7 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
});
|
||||
|
||||
const turnSubIntoContent = useMemo(
|
||||
() => isDefined(subPanel) && isDefined(contentPanel),
|
||||
() => isDefined(subPanel) && isUndefined(contentPanel),
|
||||
[contentPanel, subPanel]
|
||||
);
|
||||
|
||||
|
@ -173,11 +180,8 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
|
||||
const currencyOptions = useMemo(() => {
|
||||
const list: string[] = [];
|
||||
currencies.map((currentCurrency) => {
|
||||
if (
|
||||
currentCurrency.attributes &&
|
||||
isDefinedAndNotEmpty(currentCurrency.attributes.code)
|
||||
)
|
||||
filterHasAttributes(currencies).map((currentCurrency) => {
|
||||
if (isDefinedAndNotEmpty(currentCurrency.attributes.code))
|
||||
list.push(currentCurrency.attributes.code);
|
||||
});
|
||||
return list;
|
||||
|
@ -283,15 +287,10 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
{isDefined(contentPanel) ? (
|
||||
contentPanel
|
||||
) : (
|
||||
<div className="grid h-full place-content-center">
|
||||
<div
|
||||
className="grid grid-flow-col place-items-center gap-9 rounded-2xl
|
||||
border-2 border-dotted border-dark p-8 text-dark opacity-40"
|
||||
>
|
||||
<p className="text-4xl">❮</p>
|
||||
<p className="w-64 text-2xl">{langui.select_option_sidebar}</p>
|
||||
</div>
|
||||
</div>
|
||||
<ContentPlaceholder
|
||||
message={langui.select_option_sidebar ?? ""}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
@ -299,11 +298,10 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
{isDefined(subPanel) && (
|
||||
<div
|
||||
className={cJoin(
|
||||
`texture-paper-dots overflow-y-scroll border-r-[1px] border-dotted
|
||||
border-black bg-light transition-transform duration-300 [grid-area:sub]
|
||||
[scrollbar-width:none] webkit-scrollbar:w-0 mobile:z-10 mobile:w-[90%]
|
||||
mobile:justify-self-end mobile:border-r-0 mobile:border-l-[1px]
|
||||
mobile:[grid-area:content]`,
|
||||
`texture-paper-dots overflow-y-scroll border-r-[1px] border-dark/50 bg-light
|
||||
transition-transform duration-300 [grid-area:sub] [scrollbar-width:none]
|
||||
webkit-scrollbar:w-0 mobile:z-10 mobile:w-[90%] mobile:justify-self-end
|
||||
mobile:border-r-0 mobile:border-l-[1px] mobile:[grid-area:content]`,
|
||||
turnSubIntoContent
|
||||
? "mobile:w-full mobile:border-l-0"
|
||||
: subPanelOpen === true
|
||||
|
@ -318,10 +316,10 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
{/* Main panel */}
|
||||
<div
|
||||
className={cJoin(
|
||||
`texture-paper-dots overflow-y-scroll border-r-[1px] border-dotted
|
||||
border-black bg-light transition-transform duration-300 [grid-area:main]
|
||||
[scrollbar-width:none] webkit-scrollbar:w-0 mobile:z-10 mobile:w-[90%]
|
||||
mobile:justify-self-start mobile:[grid-area:content]`,
|
||||
`texture-paper-dots overflow-y-scroll border-r-[1px] border-dark/50 bg-light
|
||||
transition-transform duration-300 [grid-area:main] [scrollbar-width:none]
|
||||
webkit-scrollbar:w-0 mobile:z-10 mobile:w-[90%] mobile:justify-self-start
|
||||
mobile:[grid-area:content]`,
|
||||
cIf(mainPanelOpen === false, "mobile:-translate-x-full")
|
||||
)}
|
||||
>
|
||||
|
@ -399,8 +397,9 @@ export function AppLayout(props: Props): JSX.Element {
|
|||
])
|
||||
}
|
||||
onChange={(items) => {
|
||||
const newPreferredLanguages = [...items].map(
|
||||
([code]) => code
|
||||
const newPreferredLanguages = iterateMap(
|
||||
items,
|
||||
(code) => code
|
||||
);
|
||||
setPreferredLanguages(newPreferredLanguages);
|
||||
if (router.locale !== newPreferredLanguages[0]) {
|
||||
|
|
|
@ -63,9 +63,8 @@ export function Button(props: Props): JSX.Element {
|
|||
text-dark transition-all`,
|
||||
cIf(
|
||||
active,
|
||||
"!border-black bg-black text-light drop-shadow-black-lg",
|
||||
`cursor-pointer hover:bg-dark hover:text-light hover:drop-shadow-shade-lg
|
||||
active:border-black active:bg-black active:text-light active:drop-shadow-black-lg`
|
||||
"!border-black bg-black !text-light drop-shadow-black-lg",
|
||||
"cursor-pointer hover:bg-dark hover:text-light hover:drop-shadow-shade-lg"
|
||||
),
|
||||
className
|
||||
)}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Icon } from "components/Ico";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { cJoin } from "helpers/className";
|
||||
import { prettyLanguage } from "helpers/formatters";
|
||||
import { iterateMap } from "helpers/others";
|
||||
|
||||
import { Fragment } from "react";
|
||||
import { ToolTip } from "../ToolTip";
|
||||
|
@ -22,15 +23,13 @@ export function LanguageSwitcher(props: Props): JSX.Element {
|
|||
<ToolTip
|
||||
content={
|
||||
<div className={cJoin("flex flex-col gap-2", className)}>
|
||||
{[...locales].map(([locale, value], index) => (
|
||||
{iterateMap(locales, (locale, value, index) => (
|
||||
<Fragment key={index}>
|
||||
{locale && (
|
||||
<Button
|
||||
active={value === localesIndex}
|
||||
onClick={() => onLanguageChanged(value)}
|
||||
text={prettyLanguage(locale, props.languages)}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { arrayMove, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty, iterateMap, mapMoveEntry } from "helpers/others";
|
||||
|
||||
import { Fragment, useCallback, useState } from "react";
|
||||
|
||||
|
@ -16,17 +16,16 @@ export function OrderableList(props: Props): JSX.Element {
|
|||
|
||||
const updateOrder = useCallback(
|
||||
(sourceIndex: number, targetIndex: number) => {
|
||||
const newItems = arrayMove([...items], sourceIndex, targetIndex);
|
||||
const map = new Map(newItems);
|
||||
setItems(map);
|
||||
onChange?.(map);
|
||||
const newItems = mapMoveEntry(items, sourceIndex, targetIndex);
|
||||
setItems(newItems);
|
||||
onChange?.(newItems);
|
||||
},
|
||||
[items, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid gap-2">
|
||||
{[...items].map(([key, value], index) => (
|
||||
{iterateMap(items, (key, value, index) => (
|
||||
<Fragment key={key}>
|
||||
{props.insertLabels &&
|
||||
isDefinedAndNotEmpty(props.insertLabels.get(index)) && (
|
||||
|
|
|
@ -43,7 +43,7 @@ export function Select(props: Props): JSX.Element {
|
|||
className="!text-xs"
|
||||
onClick={() => {
|
||||
setState(-1);
|
||||
toggleOpened();
|
||||
setOpened(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -11,7 +11,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export function Switch(props: Props): JSX.Element {
|
||||
const { state, setState, className, disabled } = props;
|
||||
const { state, setState, className, disabled = false } = props;
|
||||
const toggleState = useToggle(setState);
|
||||
return (
|
||||
<div
|
||||
|
@ -26,7 +26,7 @@ export function Switch(props: Props): JSX.Element {
|
|||
className
|
||||
)}
|
||||
onClick={() => {
|
||||
if (disabled === false) toggleState();
|
||||
if (!disabled) toggleState();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
|
|
@ -3,7 +3,9 @@ import { Ico, Icon } from "components/Ico";
|
|||
import { Button } from "components/Inputs/Button";
|
||||
import { GetLibraryItemQuery } from "graphql/generated";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
|
||||
import { useToggle } from "hooks/useToggle";
|
||||
import { useState } from "react";
|
||||
|
@ -29,9 +31,10 @@ export function ContentLine(props: Props): JSX.Element {
|
|||
if (content.attributes) {
|
||||
return (
|
||||
<div
|
||||
className={`grid gap-2 rounded-lg px-4 ${
|
||||
opened && "my-2 h-auto bg-mid py-3 shadow-inner-sm shadow-shade"
|
||||
}`}
|
||||
className={cJoin(
|
||||
"grid gap-2 rounded-lg px-4",
|
||||
cIf(opened, "my-2 h-auto bg-mid py-3 shadow-inner-sm shadow-shade")
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-[auto_auto_1fr_auto_12ch] place-items-center
|
||||
|
@ -52,11 +55,11 @@ export function ContentLine(props: Props): JSX.Element {
|
|||
</h3>
|
||||
</a>
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
{content.attributes.content?.data?.attributes?.categories?.data.map(
|
||||
(category) => (
|
||||
<Chip key={category.id}>{category.attributes?.short}</Chip>
|
||||
)
|
||||
)}
|
||||
{filterHasAttributes(
|
||||
content.attributes.content?.data?.attributes?.categories?.data
|
||||
).map((category) => (
|
||||
<Chip key={category.id}>{category.attributes.short}</Chip>
|
||||
))}
|
||||
</div>
|
||||
<p className="h-4 w-full border-b-2 border-dotted border-black opacity-30"></p>
|
||||
<p>
|
||||
|
|
|
@ -7,10 +7,15 @@ import { GetLibraryItemScansQuery } from "graphql/generated";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getAssetFilename, getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { isInteger } from "helpers/numbers";
|
||||
import { getStatusDescription, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
getStatusDescription,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
} from "helpers/others";
|
||||
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { Fragment } from "react";
|
||||
import { Fragment, useMemo } from "react";
|
||||
|
||||
interface Props {
|
||||
openLightBox: (images: string[], index?: number) => void;
|
||||
|
@ -81,9 +86,14 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
},
|
||||
});
|
||||
|
||||
const pages = useMemo(
|
||||
() => selectedScan && filterHasAttributes(selectedScan.pages?.data),
|
||||
[selectedScan]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedScan && (
|
||||
{selectedScan && isDefined(pages) && (
|
||||
<div>
|
||||
<div
|
||||
className="flex flex-row flex-wrap place-items-center
|
||||
|
@ -126,16 +136,16 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{"Scanners"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedScan.scanners.data.map((scanner) => (
|
||||
{filterHasAttributes(selectedScan.scanners.data).map(
|
||||
(scanner) => (
|
||||
<Fragment key={scanner.id}>
|
||||
{scanner.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={scanner.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -144,7 +154,8 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{"Cleaners"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedScan.cleaners.data.map((cleaner) => (
|
||||
{filterHasAttributes(selectedScan.cleaners.data).map(
|
||||
(cleaner) => (
|
||||
<Fragment key={cleaner.id}>
|
||||
{cleaner.attributes && (
|
||||
<RecorderChip
|
||||
|
@ -153,7 +164,8 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -163,7 +175,8 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{"Typesetters"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedScan.typesetters.data.map((typesetter) => (
|
||||
{filterHasAttributes(selectedScan.typesetters.data).map(
|
||||
(typesetter) => (
|
||||
<Fragment key={typesetter.id}>
|
||||
{typesetter.attributes && (
|
||||
<RecorderChip
|
||||
|
@ -172,7 +185,8 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -188,18 +202,15 @@ export function ScanSet(props: Props): JSX.Element {
|
|||
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"
|
||||
>
|
||||
{selectedScan.pages?.data.map((page, index) => (
|
||||
{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[] = [];
|
||||
selectedScan.pages?.data.map((image) => {
|
||||
if (
|
||||
image.attributes &&
|
||||
isDefinedAndNotEmpty(image.attributes.url)
|
||||
)
|
||||
pages.map((image) => {
|
||||
if (isDefinedAndNotEmpty(image.attributes.url))
|
||||
images.push(
|
||||
getAssetURL(image.attributes.url, ImageQuality.Large)
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "graphql/generated";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { Fragment } from "react";
|
||||
|
@ -87,16 +87,16 @@ export function ScanSetCover(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{"Scanners"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedScan.scanners.data.map((scanner) => (
|
||||
{filterHasAttributes(selectedScan.scanners.data).map(
|
||||
(scanner) => (
|
||||
<Fragment key={scanner.id}>
|
||||
{scanner.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={scanner.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -105,16 +105,16 @@ export function ScanSetCover(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{"Cleaners"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedScan.cleaners.data.map((cleaner) => (
|
||||
{filterHasAttributes(selectedScan.cleaners.data).map(
|
||||
(cleaner) => (
|
||||
<Fragment key={cleaner.id}>
|
||||
{cleaner.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={cleaner.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -124,16 +124,16 @@ export function ScanSetCover(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{"Typesetters"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedScan.typesetters.data.map((typesetter) => (
|
||||
{filterHasAttributes(selectedScan.typesetters.data).map(
|
||||
(typesetter) => (
|
||||
<Fragment key={typesetter.id}>
|
||||
{typesetter.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={typesetter.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefined } from "helpers/others";
|
||||
|
||||
interface Props {
|
||||
message: string;
|
||||
icon?: Icon;
|
||||
}
|
||||
|
||||
export function ContentPlaceholder(props: Props): JSX.Element {
|
||||
const { message, icon } = props;
|
||||
return (
|
||||
<div className="grid h-full place-content-center">
|
||||
<div
|
||||
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
||||
border-dark p-8 text-dark opacity-40"
|
||||
>
|
||||
{isDefined(icon) && <Ico icon={icon} className="!text-[300%]" />}
|
||||
<p
|
||||
className={cJoin(
|
||||
"w-64 text-2xl",
|
||||
cIf(!isDefined(icon), "text-center")
|
||||
)}
|
||||
>
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -17,7 +17,15 @@ interface Props {
|
|||
}
|
||||
|
||||
export function NavOption(props: Props): JSX.Element {
|
||||
const { url, icon, title, subtitle, border, reduced, onClick } = props;
|
||||
const {
|
||||
url,
|
||||
icon,
|
||||
title,
|
||||
subtitle,
|
||||
border = false,
|
||||
reduced = false,
|
||||
onClick,
|
||||
} = props;
|
||||
const router = useRouter();
|
||||
const isActive = useMemo(
|
||||
() => router.asPath.startsWith(url),
|
||||
|
@ -36,7 +44,7 @@ export function NavOption(props: Props): JSX.Element {
|
|||
}
|
||||
placement="right"
|
||||
className="text-left"
|
||||
disabled={reduced === false}
|
||||
disabled={!reduced}
|
||||
>
|
||||
<div
|
||||
onClick={(event) => {
|
||||
|
@ -63,7 +71,7 @@ export function NavOption(props: Props): JSX.Element {
|
|||
>
|
||||
{icon && <Ico icon={icon} className="mt-[-.1em] !text-2xl" />}
|
||||
|
||||
{reduced === false && (
|
||||
{!reduced && (
|
||||
<div>
|
||||
<h3 className="text-2xl">{title}</h3>
|
||||
{isDefinedAndNotEmpty(subtitle) && (
|
||||
|
|
|
@ -15,10 +15,10 @@ export function ContentPanel(props: Props): JSX.Element {
|
|||
const { width = ContentPanelWidthSizes.Default, children } = props;
|
||||
|
||||
return (
|
||||
<div className={`grid px-4 pt-10 pb-20 desktop:py-20 desktop:px-10`}>
|
||||
<div className={`grid h-full px-4 desktop:px-10`}>
|
||||
<main
|
||||
className={cJoin(
|
||||
"place-self-center",
|
||||
"justify-self-center pt-10 pb-20 desktop:pt-20 desktop:pb-32",
|
||||
width === ContentPanelWidthSizes.Default
|
||||
? "max-w-2xl"
|
||||
: width === ContentPanelWidthSizes.Large
|
||||
|
|
|
@ -60,7 +60,7 @@ export function MainPanel(props: Props): JSX.Element {
|
|||
</Link>
|
||||
|
||||
{(!mainPanelReduced || !isDesktop) && (
|
||||
<h2 className="text-3xl">Accord’s Library</h2>
|
||||
<h2 className="mb-4 text-3xl">Accord’s Library</h2>
|
||||
)}
|
||||
|
||||
<div
|
||||
|
@ -155,7 +155,7 @@ export function MainPanel(props: Props): JSX.Element {
|
|||
*/}
|
||||
|
||||
<NavOption
|
||||
url="https://gallery.accords-library.com/"
|
||||
url="https://gallery.accords-library.com/posts/"
|
||||
icon={Icon.Collections}
|
||||
title={langui.gallery}
|
||||
reduced={mainPanelReduced && isDesktop}
|
||||
|
|
|
@ -28,7 +28,7 @@ export function Popup(props: Props): JSX.Element {
|
|||
const { setMenuGestures } = useAppLayout();
|
||||
|
||||
useEffect(() => {
|
||||
setMenuGestures(state);
|
||||
setMenuGestures(!state);
|
||||
}, [setMenuGestures, state]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getDescription } from "helpers/description";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||
import { PostWithTranslations } from "helpers/types";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { Fragment, useMemo } from "react";
|
||||
|
@ -102,14 +102,12 @@ export function PostPage(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{"Authors"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{post.authors.data.map((author) => (
|
||||
{filterHasAttributes(post.authors.data).map((author) => (
|
||||
<Fragment key={author.id}>
|
||||
{author.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={author.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -244,7 +244,8 @@ export function PreviewCard(props: Props): JSX.Element {
|
|||
cIf(
|
||||
!keepInfoVisible,
|
||||
`-inset-x-0.5 bottom-2 opacity-0 group-hover:opacity-100 hoverable:absolute
|
||||
hoverable:drop-shadow-shade-lg notHoverable:rounded-b-md`
|
||||
hoverable:drop-shadow-shade-lg notHoverable:opacity-100
|
||||
notHoverable:rounded-b-md`
|
||||
)
|
||||
)}
|
||||
>
|
||||
|
@ -302,9 +303,7 @@ interface TranslatedProps
|
|||
languages: AppStaticProps["languages"];
|
||||
}
|
||||
|
||||
export function TranslatedPreviewCard(
|
||||
props: Immutable<TranslatedProps>
|
||||
): JSX.Element {
|
||||
export function TranslatedPreviewCard(props: TranslatedProps): JSX.Element {
|
||||
const {
|
||||
translations = [{ title: props.slug, language: "default" }],
|
||||
slug,
|
||||
|
|
|
@ -89,7 +89,7 @@ interface TranslatedProps
|
|||
}
|
||||
|
||||
export function TranslatedPreviewLine(
|
||||
props: Immutable<TranslatedProps>
|
||||
props: TranslatedProps
|
||||
): JSX.Element {
|
||||
const {
|
||||
translations = [{ title: props.slug, language: "default" }],
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Chip } from "components/Chip";
|
|||
import { RecorderChipFragment } from "graphql/generated";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
|
||||
import { Fragment } from "react";
|
||||
import { Img } from "./Img";
|
||||
|
@ -33,13 +34,13 @@ export function RecorderChip(props: Props): JSX.Element {
|
|||
{recorder.languages?.data && recorder.languages.data.length > 0 && (
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
<p>{langui.languages}:</p>
|
||||
{recorder.languages.data.map((language) => (
|
||||
<Fragment key={language.attributes?.code}>
|
||||
{language.attributes && (
|
||||
{filterHasAttributes(recorder.languages.data).map(
|
||||
(language) => (
|
||||
<Fragment key={language.attributes.code}>
|
||||
<Chip>{language.attributes.code.toUpperCase()}</Chip>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{recorder.pronouns && (
|
||||
|
|
|
@ -6,6 +6,7 @@ import { GetContentTextQuery, UploadImageFragment } from "graphql/generated";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { prettyinlineTitle, prettySlug, slugify } from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
|
||||
|
@ -89,9 +90,11 @@ export function ThumbnailHeader(props: Props): JSX.Element {
|
|||
<div className="flex flex-col place-items-center gap-2">
|
||||
<h3 className="text-xl">{langui.categories}</h3>
|
||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||
{categories.data.map((category) => (
|
||||
<Chip key={category.id}>{category.attributes?.name}</Chip>
|
||||
))}
|
||||
{filterHasAttributes(categories.data).map(
|
||||
(category) => (
|
||||
<Chip key={category.id}>{category.attributes.name}</Chip>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -6,7 +6,11 @@ import {
|
|||
GetChronologyItemsQuery,
|
||||
} from "graphql/generated";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import {
|
||||
filterDefined,
|
||||
filterHasAttributes,
|
||||
getStatusDescription,
|
||||
} from "helpers/others";
|
||||
|
||||
import { Fragment } from "react";
|
||||
|
||||
|
@ -44,13 +48,16 @@ export function ChronologyItemComponent(props: Props): JSX.Element {
|
|||
</p>
|
||||
|
||||
<div className="col-start-2 row-span-2 row-start-1 grid gap-4">
|
||||
{props.item.attributes.events?.map((event) => (
|
||||
<Fragment key={event?.id}>
|
||||
{event && (
|
||||
{props.item.attributes.events &&
|
||||
filterHasAttributes(props.item.attributes.events, [
|
||||
"id",
|
||||
"translations",
|
||||
]).map((event) => (
|
||||
<Fragment key={event.id}>
|
||||
<div className="m-0">
|
||||
{event.translations?.map((translation, translationIndex) => (
|
||||
{filterDefined(event.translations).map(
|
||||
(translation, translationIndex) => (
|
||||
<Fragment key={translationIndex}>
|
||||
{translation && (
|
||||
<Fragment>
|
||||
<div
|
||||
className="grid
|
||||
|
@ -94,9 +101,9 @@ export function ChronologyItemComponent(props: Props): JSX.Element {
|
|||
""
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
|
||||
<p className="mt-1 grid grid-flow-col gap-1 place-self-start text-xs text-dark">
|
||||
{event.source?.data ? (
|
||||
|
@ -109,7 +116,6 @@ export function ChronologyItemComponent(props: Props): JSX.Element {
|
|||
)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { GetLibraryItemsPreviewQuery } from "graphql/generated";
|
|||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { prettyinlineTitle, prettyDate } from "./formatters";
|
||||
import { convertPrice } from "./numbers";
|
||||
import { isDefined } from "./others";
|
||||
import { isDefined, mapRemoveEmptyValues } from "./others";
|
||||
import { LibraryItemUserStatus } from "./types";
|
||||
type Items = NonNullable<GetLibraryItemsPreviewQuery["libraryItems"]>["data"];
|
||||
type GroupLibraryItems = Map<string, Items>;
|
||||
|
@ -13,67 +13,67 @@ export function getGroups(
|
|||
groupByType: number,
|
||||
items: Items
|
||||
): GroupLibraryItems {
|
||||
const groups: GroupLibraryItems = new Map();
|
||||
|
||||
switch (groupByType) {
|
||||
case 0: {
|
||||
const typeGroup = new Map();
|
||||
typeGroup.set("Drakengard 1", []);
|
||||
typeGroup.set("Drakengard 1.3", []);
|
||||
typeGroup.set("Drakengard 2", []);
|
||||
typeGroup.set("Drakengard 3", []);
|
||||
typeGroup.set("Drakengard 4", []);
|
||||
typeGroup.set("NieR Gestalt", []);
|
||||
typeGroup.set("NieR Replicant", []);
|
||||
typeGroup.set("NieR Replicant ver.1.22474487139...", []);
|
||||
typeGroup.set("NieR:Automata", []);
|
||||
typeGroup.set("NieR Re[in]carnation", []);
|
||||
typeGroup.set("SINoALICE", []);
|
||||
typeGroup.set("Voice of Cards", []);
|
||||
typeGroup.set("Final Fantasy XIV", []);
|
||||
typeGroup.set("Thou Shalt Not Die", []);
|
||||
typeGroup.set("Bakuken", []);
|
||||
typeGroup.set("YoRHa", []);
|
||||
typeGroup.set("YoRHa Boys", []);
|
||||
typeGroup.set(langui.no_category, []);
|
||||
const noCategory = langui.no_category ?? "No category";
|
||||
groups.set("Drakengard 1", []);
|
||||
groups.set("Drakengard 1.3", []);
|
||||
groups.set("Drakengard 2", []);
|
||||
groups.set("Drakengard 3", []);
|
||||
groups.set("Drakengard 4", []);
|
||||
groups.set("NieR Gestalt", []);
|
||||
groups.set("NieR Replicant", []);
|
||||
groups.set("NieR Replicant ver.1.22474487139...", []);
|
||||
groups.set("NieR:Automata", []);
|
||||
groups.set("NieR Re[in]carnation", []);
|
||||
groups.set("SINoALICE", []);
|
||||
groups.set("Voice of Cards", []);
|
||||
groups.set("Final Fantasy XIV", []);
|
||||
groups.set("Thou Shalt Not Die", []);
|
||||
groups.set("Bakuken", []);
|
||||
groups.set("YoRHa", []);
|
||||
groups.set("YoRHa Boys", []);
|
||||
groups.set(noCategory, []);
|
||||
|
||||
items.map((item) => {
|
||||
if (item.attributes?.categories?.data.length === 0) {
|
||||
typeGroup.get(langui.no_category)?.push(item);
|
||||
groups.get(noCategory)?.push(item);
|
||||
} else {
|
||||
item.attributes?.categories?.data.map((category) => {
|
||||
typeGroup.get(category.attributes?.name)?.push(item);
|
||||
groups.get(category.attributes?.name ?? noCategory)?.push(item);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return typeGroup;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
const group = new Map();
|
||||
group.set(langui.audio ?? "Audio", []);
|
||||
group.set(langui.game ?? "Game", []);
|
||||
group.set(langui.textual ?? "Textual", []);
|
||||
group.set(langui.video ?? "Video", []);
|
||||
group.set(langui.other ?? "Other", []);
|
||||
group.set(langui.group ?? "Group", []);
|
||||
group.set(langui.no_type ?? "No type", []);
|
||||
groups.set(langui.audio ?? "Audio", []);
|
||||
groups.set(langui.game ?? "Game", []);
|
||||
groups.set(langui.textual ?? "Textual", []);
|
||||
groups.set(langui.video ?? "Video", []);
|
||||
groups.set(langui.other ?? "Other", []);
|
||||
groups.set(langui.group ?? "Group", []);
|
||||
groups.set(langui.no_type ?? "No type", []);
|
||||
items.map((item) => {
|
||||
if (item.attributes?.metadata && item.attributes.metadata.length > 0) {
|
||||
switch (item.attributes.metadata[0]?.__typename) {
|
||||
case "ComponentMetadataAudio":
|
||||
group.get(langui.audio ?? "Audio")?.push(item);
|
||||
groups.get(langui.audio ?? "Audio")?.push(item);
|
||||
break;
|
||||
case "ComponentMetadataGame":
|
||||
group.get(langui.game ?? "Game")?.push(item);
|
||||
groups.get(langui.game ?? "Game")?.push(item);
|
||||
break;
|
||||
case "ComponentMetadataBooks":
|
||||
group.get(langui.textual ?? "Textual")?.push(item);
|
||||
groups.get(langui.textual ?? "Textual")?.push(item);
|
||||
break;
|
||||
case "ComponentMetadataVideo":
|
||||
group.get(langui.video ?? "Video")?.push(item);
|
||||
groups.get(langui.video ?? "Video")?.push(item);
|
||||
break;
|
||||
case "ComponentMetadataOther":
|
||||
group.get(langui.other ?? "Other")?.push(item);
|
||||
groups.get(langui.other ?? "Other")?.push(item);
|
||||
break;
|
||||
case "ComponentMetadataGroup":
|
||||
switch (
|
||||
|
@ -81,19 +81,19 @@ export function getGroups(
|
|||
?.slug
|
||||
) {
|
||||
case "audio":
|
||||
group.get(langui.audio ?? "Audio")?.push(item);
|
||||
groups.get(langui.audio ?? "Audio")?.push(item);
|
||||
break;
|
||||
case "video":
|
||||
group.get(langui.video ?? "Video")?.push(item);
|
||||
groups.get(langui.video ?? "Video")?.push(item);
|
||||
break;
|
||||
case "game":
|
||||
group.get(langui.game ?? "Game")?.push(item);
|
||||
groups.get(langui.game ?? "Game")?.push(item);
|
||||
break;
|
||||
case "textual":
|
||||
group.get(langui.textual ?? "Textual")?.push(item);
|
||||
groups.get(langui.textual ?? "Textual")?.push(item);
|
||||
break;
|
||||
case "mixed":
|
||||
group.get(langui.group ?? "Group")?.push(item);
|
||||
groups.get(langui.group ?? "Group")?.push(item);
|
||||
break;
|
||||
default: {
|
||||
throw new Error(
|
||||
|
@ -107,10 +107,10 @@ export function getGroups(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
group.get(langui.no_type ?? "No type")?.push(item);
|
||||
groups.get(langui.no_type ?? "No type")?.push(item);
|
||||
}
|
||||
});
|
||||
return group;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
|
@ -121,29 +121,28 @@ export function getGroups(
|
|||
years.push(item.attributes.release_date.year);
|
||||
}
|
||||
});
|
||||
const group = new Map();
|
||||
|
||||
years.sort((a, b) => a - b);
|
||||
years.map((year) => {
|
||||
group.set(year.toString(), []);
|
||||
groups.set(year.toString(), []);
|
||||
});
|
||||
group.set(langui.no_year ?? "No year", []);
|
||||
groups.set(langui.no_year ?? "No year", []);
|
||||
items.map((item) => {
|
||||
if (item.attributes?.release_date?.year) {
|
||||
group.get(item.attributes.release_date.year.toString())?.push(item);
|
||||
groups.get(item.attributes.release_date.year.toString())?.push(item);
|
||||
} else {
|
||||
group.get(langui.no_year ?? "No year")?.push(item);
|
||||
groups.get(langui.no_year ?? "No year")?.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return group;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const group = new Map();
|
||||
group.set("", items);
|
||||
return group;
|
||||
groups.set("", items);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mapRemoveEmptyValues(groups);
|
||||
}
|
||||
|
||||
export function filterItems(
|
||||
|
@ -155,7 +154,7 @@ export function filterItems(
|
|||
showSecondaryItems: boolean,
|
||||
filterUserStatus: LibraryItemUserStatus | undefined
|
||||
): Items {
|
||||
return [...items].filter((item) => {
|
||||
return items.filter((item) => {
|
||||
if (!showSubitems && !item.attributes?.root_item) return false;
|
||||
if (showSubitems && isUntangibleGroupItem(item.attributes?.metadata?.[0])) {
|
||||
return false;
|
||||
|
@ -212,7 +211,7 @@ export function sortBy(
|
|||
): Items {
|
||||
switch (orderByType) {
|
||||
case 0:
|
||||
return [...items].sort((a, b) => {
|
||||
return items.sort((a, b) => {
|
||||
const titleA = prettyinlineTitle(
|
||||
"",
|
||||
a.attributes?.title,
|
||||
|
@ -226,7 +225,7 @@ export function sortBy(
|
|||
return titleA.localeCompare(titleB);
|
||||
});
|
||||
case 1:
|
||||
return [...items].sort((a, b) => {
|
||||
return items.sort((a, b) => {
|
||||
const priceA = a.attributes?.price
|
||||
? convertPrice(a.attributes.price, currencies[0])
|
||||
: 99999;
|
||||
|
@ -236,7 +235,7 @@ export function sortBy(
|
|||
return priceA - priceB;
|
||||
});
|
||||
case 2:
|
||||
return [...items].sort((a, b) => {
|
||||
return items.sort((a, b) => {
|
||||
const dateA = a.attributes?.release_date
|
||||
? prettyDate(a.attributes.release_date)
|
||||
: "9999";
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
GetLibraryItemScansQuery,
|
||||
} from "graphql/generated";
|
||||
import { AppStaticProps } from "../graphql/getAppStaticProps";
|
||||
import { SelectiveRequiredNonNullable } from "./types";
|
||||
|
||||
type SortContentProps =
|
||||
| NonNullable<
|
||||
|
@ -59,11 +60,6 @@ export function getStatusDescription(
|
|||
}
|
||||
}
|
||||
|
||||
export function arrayMove<T>(arr: T[], old_index: number, new_index: number) {
|
||||
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
export function isDefined<T>(t: T): t is NonNullable<T> {
|
||||
return t !== null && t !== undefined;
|
||||
}
|
||||
|
@ -78,6 +74,51 @@ export function isDefinedAndNotEmpty(
|
|||
return isDefined(string) && string.length > 0;
|
||||
}
|
||||
|
||||
export function filterDefined<T>(t: T[]): NonNullable<T>[] {
|
||||
export function filterDefined<T>(t: T[] | undefined | null): NonNullable<T>[] {
|
||||
if (isUndefined(t)) return [];
|
||||
return t.filter((item) => isDefined(item)) as NonNullable<T>[];
|
||||
}
|
||||
|
||||
export function filterHasAttributes<T, P extends keyof NonNullable<T>>(
|
||||
t: T[] | undefined | null,
|
||||
attributes?: P[]
|
||||
): SelectiveRequiredNonNullable<NonNullable<T>, P>[] {
|
||||
if (isUndefined(t)) return [];
|
||||
return t.filter((item) => {
|
||||
if (isDefined(item)) {
|
||||
const attributesToCheck = attributes ?? (Object.keys(item) as P[]);
|
||||
return attributesToCheck.every((attribute) => isDefined(item[attribute]));
|
||||
}
|
||||
return false;
|
||||
}) as unknown as SelectiveRequiredNonNullable<NonNullable<T>, P>[];
|
||||
}
|
||||
|
||||
export function iterateMap<K, V, U>(
|
||||
map: Map<K, V>,
|
||||
callbackfn: (key: K, value: V, index: number) => U
|
||||
): U[] {
|
||||
const result: U[] = [];
|
||||
let index = 0;
|
||||
for (const [key, value] of map.entries()) {
|
||||
result.push(callbackfn(key, value, index));
|
||||
index += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function mapMoveEntry<K, V>(
|
||||
map: Map<K, V>,
|
||||
sourceIndex: number,
|
||||
targetIndex: number
|
||||
) {
|
||||
return new Map(arrayMove([...map], sourceIndex, targetIndex));
|
||||
}
|
||||
|
||||
function arrayMove<T>(arr: T[], sourceIndex: number, targetIndex: number) {
|
||||
arr.splice(targetIndex, 0, arr.splice(sourceIndex, 1)[0]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
export function mapRemoveEmptyValues<K, V>(groups: Map<K, V[]>): Map<K, V[]> {
|
||||
return new Map([...groups].filter(([_, items]) => items.length > 0));
|
||||
}
|
||||
|
|
|
@ -30,9 +30,13 @@ export interface WikiPageWithTranslations
|
|||
translations: NonNullable<WikiPage["translations"]>;
|
||||
}
|
||||
|
||||
export type RequiredNonNullable<T> = Required<{
|
||||
[P in keyof T]: NonNullable<T[P]>;
|
||||
}>;
|
||||
export type RequiredNonNullable<T> = {
|
||||
[P in keyof T]-?: NonNullable<T[P]>;
|
||||
};
|
||||
|
||||
export type SelectiveRequiredNonNullable<T, K extends keyof T> = Omit<T, K> & {
|
||||
[P in K]-?: NonNullable<T[P]>;
|
||||
};
|
||||
|
||||
export enum LibraryItemUserStatus {
|
||||
None = 0,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { LanguageSwitcher } from "components/Inputs/LanguageSwitcher";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { filterDefined, isDefined } from "helpers/others";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
@ -39,11 +39,9 @@ export function useSmartLanguage<T>(
|
|||
|
||||
const availableLocales = useMemo(() => {
|
||||
const map = new Map<string, number>();
|
||||
items.map((elem, index) => {
|
||||
if (isDefined(elem)) {
|
||||
filterDefined(items).map((elem, index) => {
|
||||
const result = languageExtractor(elem);
|
||||
if (isDefined(result)) map.set(result, index);
|
||||
}
|
||||
});
|
||||
return map;
|
||||
}, [items, languageExtractor]);
|
||||
|
|
|
@ -24,7 +24,7 @@ import { Fragment, useState } from "react";
|
|||
import { Icon } from "components/Ico";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
channel: NonNullable<
|
||||
|
@ -74,9 +74,8 @@ export default function Channel(props: Props): JSX.Element {
|
|||
className="grid items-start gap-8 border-b-[3px] border-dotted pb-12 last-of-type:border-0
|
||||
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:grid-cols-2"
|
||||
>
|
||||
{channel?.videos?.data.map((video) => (
|
||||
{filterHasAttributes(channel?.videos?.data).map((video) => (
|
||||
<Fragment key={video.id}>
|
||||
{video.attributes && (
|
||||
<PreviewCard
|
||||
href={`/archives/videos/v/${video.attributes.uid}`}
|
||||
title={video.attributes.title}
|
||||
|
@ -86,7 +85,7 @@ export default function Channel(props: Props): JSX.Element {
|
|||
metadata={{
|
||||
release_date: video.attributes.published_date,
|
||||
views: video.attributes.views,
|
||||
author: channel.title,
|
||||
author: channel?.title,
|
||||
position: "Top",
|
||||
}}
|
||||
hoverlay={{
|
||||
|
@ -94,7 +93,6 @@ export default function Channel(props: Props): JSX.Element {
|
|||
duration: video.attributes.duration,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
@ -137,9 +135,8 @@ export async function getStaticPaths(
|
|||
const channels = await sdk.getVideoChannelsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
if (channels.videoChannels?.data)
|
||||
channels.videoChannels.data.map((channel) => {
|
||||
filterHasAttributes(channels.videoChannels.data).map((channel) => {
|
||||
context.locales?.map((local) => {
|
||||
if (channel.attributes)
|
||||
paths.push({
|
||||
params: { uid: channel.attributes.uid },
|
||||
locale: local,
|
||||
|
|
|
@ -18,6 +18,7 @@ import { GetVideosPreviewQuery } from "graphql/generated";
|
|||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyDate } from "helpers/formatters";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { getVideoThumbnailURL } from "helpers/videos";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
|
@ -95,9 +96,8 @@ export default function Videos(props: Props): JSX.Element {
|
|||
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:grid-cols-2
|
||||
thin:grid-cols-1"
|
||||
>
|
||||
{paginatedVideos[page].map((video) => (
|
||||
{filterHasAttributes(paginatedVideos[page]).map((video) => (
|
||||
<Fragment key={video.id}>
|
||||
{video.attributes && (
|
||||
<PreviewCard
|
||||
href={`/archives/videos/v/${video.attributes.uid}`}
|
||||
title={video.attributes.title}
|
||||
|
@ -115,7 +115,6 @@ export default function Videos(props: Props): JSX.Element {
|
|||
duration: video.attributes.duration,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -18,7 +18,7 @@ import { GetVideoQuery } from "graphql/generated";
|
|||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { getVideoFile } from "helpers/videos";
|
||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||
import {
|
||||
|
@ -213,9 +213,8 @@ export async function getStaticPaths(
|
|||
const videos = await sdk.getVideosSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
if (videos.videos?.data)
|
||||
videos.videos.data.map((video) => {
|
||||
filterHasAttributes(videos.videos.data).map((video) => {
|
||||
context.locales?.map((local) => {
|
||||
if (video.attributes)
|
||||
paths.push({ params: { uid: video.attributes.uid }, locale: local });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,7 +26,11 @@ import {
|
|||
prettySlug,
|
||||
} from "helpers/formatters";
|
||||
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
getStatusDescription,
|
||||
isDefinedAndNotEmpty,
|
||||
} from "helpers/others";
|
||||
import { ContentWithTranslations } from "helpers/types";
|
||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
|
@ -82,19 +86,17 @@ export default function Content(props: Props): JSX.Element {
|
|||
horizontalLine
|
||||
/>
|
||||
|
||||
{selectedTranslation?.text_set && (
|
||||
{selectedTranslation?.text_set?.source_language?.data?.attributes
|
||||
?.code !== undefined && (
|
||||
<div className="grid gap-5">
|
||||
<h2 className="text-xl">
|
||||
{selectedTranslation.text_set.source_language?.data?.attributes
|
||||
?.code === selectedTranslation.language?.data?.attributes?.code
|
||||
{selectedTranslation.text_set.source_language.data.attributes
|
||||
.code === selectedTranslation.language?.data?.attributes?.code
|
||||
? langui.transcript_notice
|
||||
: langui.translation_notice}
|
||||
</h2>
|
||||
|
||||
{selectedTranslation.text_set.source_language?.data?.attributes
|
||||
?.code &&
|
||||
selectedTranslation.text_set.source_language.data.attributes
|
||||
.code !==
|
||||
{selectedTranslation.text_set.source_language.data.attributes.code !==
|
||||
selectedTranslation.language?.data?.attributes?.code && (
|
||||
<div className="grid place-items-center gap-2">
|
||||
<p className="font-headers">{langui.source_language}:</p>
|
||||
|
@ -127,18 +129,16 @@ export default function Content(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{langui.transcribers}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedTranslation.text_set.transcribers.data.map(
|
||||
(recorder) => (
|
||||
{filterHasAttributes(
|
||||
selectedTranslation.text_set.transcribers.data
|
||||
).map((recorder) => (
|
||||
<Fragment key={recorder.id}>
|
||||
{recorder.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={recorder.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -148,18 +148,16 @@ export default function Content(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{langui.translators}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedTranslation.text_set.translators.data.map(
|
||||
(recorder) => (
|
||||
{filterHasAttributes(
|
||||
selectedTranslation.text_set.translators.data
|
||||
).map((recorder) => (
|
||||
<Fragment key={recorder.id}>
|
||||
{recorder.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={recorder.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -169,23 +167,21 @@ export default function Content(props: Props): JSX.Element {
|
|||
<div>
|
||||
<p className="font-headers">{langui.proofreaders}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
{selectedTranslation.text_set.proofreaders.data.map(
|
||||
(recorder) => (
|
||||
{filterHasAttributes(
|
||||
selectedTranslation.text_set.proofreaders.data
|
||||
).map((recorder) => (
|
||||
<Fragment key={recorder.id}>
|
||||
{recorder.attributes && (
|
||||
<RecorderChip
|
||||
langui={langui}
|
||||
recorder={recorder.attributes}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedTranslation.text_set.notes && (
|
||||
{isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (
|
||||
<div>
|
||||
<p className="font-headers">{"Notes"}:</p>
|
||||
<div className="grid place-content-center place-items-center gap-2">
|
||||
|
@ -450,9 +446,8 @@ export async function getStaticPaths(
|
|||
const sdk = getReadySdk();
|
||||
const contents = await sdk.getContentsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
contents.contents?.data.map((item) => {
|
||||
filterHasAttributes(contents.contents?.data).map((item) => {
|
||||
context.locales?.map((local) => {
|
||||
if (item.attributes)
|
||||
paths.push({
|
||||
params: { slug: item.attributes.slug },
|
||||
locale: local,
|
||||
|
|
|
@ -21,6 +21,12 @@ import { WithLabel } from "components/Inputs/WithLabel";
|
|||
import { Button } from "components/Inputs/Button";
|
||||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
iterateMap,
|
||||
mapRemoveEmptyValues,
|
||||
} from "helpers/others";
|
||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
contents: NonNullable<GetContentsQuery["contents"]>["data"];
|
||||
|
@ -128,8 +134,18 @@ export default function Contents(props: Props): JSX.Element {
|
|||
);
|
||||
const contentPanel = (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
{[...groups].map(
|
||||
([name, items], index) =>
|
||||
{/* TODO: Add to langui */}
|
||||
{groups.size === 0 && (
|
||||
<ContentPlaceholder
|
||||
message={
|
||||
"No results. You can try changing or resetting the search parameters."
|
||||
}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
{iterateMap(
|
||||
groups,
|
||||
(name, items, index) =>
|
||||
items.length > 0 && (
|
||||
<Fragment key={index}>
|
||||
{name && (
|
||||
|
@ -140,7 +156,10 @@ export default function Contents(props: Props): JSX.Element {
|
|||
{name}
|
||||
<Chip>{`${items.reduce((currentSum, item) => {
|
||||
if (effectiveCombineRelatedContent) {
|
||||
if (item.attributes?.group?.data?.attributes?.combine) {
|
||||
if (
|
||||
item.attributes?.group?.data?.attributes?.combine ===
|
||||
true
|
||||
) {
|
||||
return (
|
||||
currentSum +
|
||||
(item.attributes.group.data.attributes.contents?.data
|
||||
|
@ -161,9 +180,8 @@ export default function Contents(props: Props): JSX.Element {
|
|||
className="grid grid-cols-2 items-end gap-8
|
||||
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:gap-4"
|
||||
>
|
||||
{items.map((item) => (
|
||||
{filterHasAttributes(items).map((item) => (
|
||||
<Fragment key={item.id}>
|
||||
{item.attributes && (
|
||||
<TranslatedPreviewCard
|
||||
href={`/contents/${item.attributes.slug}`}
|
||||
translations={item.attributes.translations?.map(
|
||||
|
@ -182,17 +200,18 @@ export default function Contents(props: Props): JSX.Element {
|
|||
thumbnailForceAspectRatio
|
||||
stackNumber={
|
||||
effectiveCombineRelatedContent &&
|
||||
item.attributes.group?.data?.attributes?.combine
|
||||
? item.attributes.group.data.attributes.contents
|
||||
?.data.length
|
||||
item.attributes.group?.data?.attributes?.combine ===
|
||||
true
|
||||
? item.attributes.group.data.attributes.contents?.data
|
||||
.length
|
||||
: 0
|
||||
}
|
||||
topChips={
|
||||
item.attributes.type?.data?.attributes
|
||||
? [
|
||||
item.attributes.type.data.attributes.titles?.[0]
|
||||
? item.attributes.type.data.attributes
|
||||
.titles[0]?.title
|
||||
? item.attributes.type.data.attributes.titles[0]
|
||||
?.title
|
||||
: prettySlug(
|
||||
item.attributes.type.data.attributes.slug
|
||||
),
|
||||
|
@ -204,7 +223,6 @@ export default function Contents(props: Props): JSX.Element {
|
|||
)}
|
||||
keepInfoVisible={keepInfoVisible}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
@ -252,71 +270,72 @@ function getGroups(
|
|||
groupByType: number,
|
||||
items: Props["contents"]
|
||||
): GroupContentItems {
|
||||
const groups: GroupContentItems = new Map();
|
||||
|
||||
switch (groupByType) {
|
||||
case 0: {
|
||||
const group = new Map();
|
||||
group.set("Drakengard 1", []);
|
||||
group.set("Drakengard 1.3", []);
|
||||
group.set("Drakengard 2", []);
|
||||
group.set("Drakengard 3", []);
|
||||
group.set("Drakengard 4", []);
|
||||
group.set("NieR Gestalt", []);
|
||||
group.set("NieR Replicant", []);
|
||||
group.set("NieR Replicant ver.1.22474487139...", []);
|
||||
group.set("NieR:Automata", []);
|
||||
group.set("NieR Re[in]carnation", []);
|
||||
group.set("SINoALICE", []);
|
||||
group.set("Voice of Cards", []);
|
||||
group.set("Final Fantasy XIV", []);
|
||||
group.set("Thou Shalt Not Die", []);
|
||||
group.set("Bakuken", []);
|
||||
group.set("YoRHa", []);
|
||||
group.set("YoRHa Boys", []);
|
||||
group.set(langui.no_category, []);
|
||||
const noCategory = langui.no_category ?? "No category";
|
||||
groups.set("Drakengard 1", []);
|
||||
groups.set("Drakengard 1.3", []);
|
||||
groups.set("Drakengard 2", []);
|
||||
groups.set("Drakengard 3", []);
|
||||
groups.set("Drakengard 4", []);
|
||||
groups.set("NieR Gestalt", []);
|
||||
groups.set("NieR Replicant", []);
|
||||
groups.set("NieR Replicant ver.1.22474487139...", []);
|
||||
groups.set("NieR:Automata", []);
|
||||
groups.set("NieR Re[in]carnation", []);
|
||||
groups.set("SINoALICE", []);
|
||||
groups.set("Voice of Cards", []);
|
||||
groups.set("Final Fantasy XIV", []);
|
||||
groups.set("Thou Shalt Not Die", []);
|
||||
groups.set("Bakuken", []);
|
||||
groups.set("YoRHa", []);
|
||||
groups.set("YoRHa Boys", []);
|
||||
groups.set(noCategory, []);
|
||||
|
||||
items.map((item) => {
|
||||
if (item.attributes?.categories?.data.length === 0) {
|
||||
group.get(langui.no_category)?.push(item);
|
||||
groups.get(noCategory)?.push(item);
|
||||
} else {
|
||||
item.attributes?.categories?.data.map((category) => {
|
||||
group.get(category.attributes?.name)?.push(item);
|
||||
groups.get(category.attributes?.name ?? noCategory)?.push(item);
|
||||
});
|
||||
}
|
||||
});
|
||||
return group;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
const group = new Map();
|
||||
items.map((item) => {
|
||||
const noType = langui.no_type ?? "No type";
|
||||
const type =
|
||||
item.attributes?.type?.data?.attributes?.titles?.[0]?.title ??
|
||||
item.attributes?.type?.data?.attributes?.slug
|
||||
? prettySlug(item.attributes.type.data.attributes.slug)
|
||||
: langui.no_type;
|
||||
if (!group.has(type)) group.set(type, []);
|
||||
group.get(type)?.push(item);
|
||||
if (!groups.has(type ?? noType)) groups.set(type ?? noType, []);
|
||||
groups.get(type ?? noType)?.push(item);
|
||||
});
|
||||
return group;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const group: GroupContentItems = new Map();
|
||||
group.set("", items);
|
||||
return group;
|
||||
groups.set("", items);
|
||||
}
|
||||
}
|
||||
return mapRemoveEmptyValues(groups);
|
||||
}
|
||||
|
||||
function filterContents(
|
||||
contents: Immutable<Props["contents"]>,
|
||||
contents: Props["contents"],
|
||||
combineRelatedContent: boolean,
|
||||
searchName: string
|
||||
): Immutable<Props["contents"]> {
|
||||
): Props["contents"] {
|
||||
return contents.filter((content) => {
|
||||
if (
|
||||
combineRelatedContent &&
|
||||
content.attributes?.group?.data?.attributes?.combine &&
|
||||
content.attributes?.group?.data?.attributes?.combine === true &&
|
||||
content.attributes.group.data.attributes.contents?.data[0].id !==
|
||||
content.id
|
||||
) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { ToolTip } from "components/ToolTip";
|
|||
import { DevGetContentsQuery } from "graphql/generated";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterDefined, filterHasAttributes } from "helpers/others";
|
||||
|
||||
import { GetStaticPropsContext } from "next";
|
||||
|
||||
|
@ -116,8 +117,7 @@ function testingContent(contents: Props["contents"]): Report {
|
|||
lines: [],
|
||||
};
|
||||
|
||||
contents.contents?.data.map((content) => {
|
||||
if (content.attributes) {
|
||||
filterHasAttributes(contents.contents?.data).map((content) => {
|
||||
const backendUrl = `${process.env.NEXT_PUBLIC_URL_CMS}/admin/content-manager/collectionType/api::content.content/${content.id}`;
|
||||
const frontendUrl = `${process.env.NEXT_PUBLIC_URL_SELF}/contents/${content.attributes.slug}`;
|
||||
|
||||
|
@ -188,8 +188,8 @@ function testingContent(contents: Props["contents"]): Report {
|
|||
} else {
|
||||
const titleLanguages: string[] = [];
|
||||
|
||||
content.attributes.translations?.map((translation, titleIndex) => {
|
||||
if (translation && content.attributes) {
|
||||
filterDefined(content.attributes.translations).map(
|
||||
(translation, titleIndex) => {
|
||||
if (translation.language?.data?.id) {
|
||||
if (translation.language.data.id in titleLanguages) {
|
||||
report.lines.push({
|
||||
|
@ -429,8 +429,7 @@ function testingContent(contents: Props["contents"]): Report {
|
|||
frontendUrl: frontendUrl,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
return report;
|
||||
|
|
|
@ -35,7 +35,12 @@ import {
|
|||
} from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { convertMmToInch } from "helpers/numbers";
|
||||
import { isDefined, sortContent } from "helpers/others";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
sortContent,
|
||||
} from "helpers/others";
|
||||
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
|
@ -172,7 +177,9 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
)}
|
||||
<div className="grid place-items-center text-center">
|
||||
<h1 className="text-3xl">{item?.title}</h1>
|
||||
{item?.subtitle && <h2 className="text-2xl">{item.subtitle}</h2>}
|
||||
{item && isDefinedAndNotEmpty(item.subtitle) && (
|
||||
<h2 className="text-2xl">{item.subtitle}</h2>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<PreviewCardCTAs
|
||||
|
@ -196,17 +203,13 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
{item?.urls && item.urls.length ? (
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
<p>{langui.available_at}</p>
|
||||
{item.urls
|
||||
.filter((url) => url)
|
||||
.map((url, index) => (
|
||||
{filterHasAttributes(item.urls).map((url, index) => (
|
||||
<Fragment key={index}>
|
||||
{url?.url && (
|
||||
<Button
|
||||
href={url.url}
|
||||
target={"_blank"}
|
||||
text={prettyURL(url.url)}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
@ -225,26 +228,19 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
className="grid w-full grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-end
|
||||
gap-8"
|
||||
>
|
||||
{item.gallery.data.map((galleryItem, index) => (
|
||||
{filterHasAttributes(item.gallery.data).map(
|
||||
(galleryItem, index) => (
|
||||
<Fragment key={galleryItem.id}>
|
||||
{galleryItem.attributes && (
|
||||
<div
|
||||
className="relative aspect-square cursor-pointer
|
||||
transition-transform hover:scale-[1.02]"
|
||||
onClick={() => {
|
||||
if (item.gallery?.data) {
|
||||
const images: string[] = [];
|
||||
item.gallery.data.map((image) => {
|
||||
if (image.attributes)
|
||||
images.push(
|
||||
getAssetURL(
|
||||
image.attributes.url,
|
||||
ImageQuality.Large
|
||||
)
|
||||
const images: string[] = filterHasAttributes(
|
||||
item.gallery?.data
|
||||
).map((image) =>
|
||||
getAssetURL(image.attributes.url, ImageQuality.Large)
|
||||
);
|
||||
});
|
||||
openLightBox(images, index);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Img
|
||||
|
@ -253,9 +249,9 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
image={galleryItem.attributes}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -437,9 +433,8 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
className="grid w-full grid-cols-[repeat(auto-fill,minmax(15rem,1fr))]
|
||||
items-end gap-8 mobile:grid-cols-2 thin:grid-cols-1"
|
||||
>
|
||||
{item.subitems.data.map((subitem) => (
|
||||
{filterHasAttributes(item.subitems.data).map((subitem) => (
|
||||
<Fragment key={subitem.id}>
|
||||
{subitem.attributes && subitem.id && (
|
||||
<PreviewCard
|
||||
href={`/library/${subitem.attributes.slug}`}
|
||||
title={subitem.attributes.title}
|
||||
|
@ -476,7 +471,6 @@ export default function LibrarySlug(props: Props): JSX.Element {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
@ -548,13 +542,12 @@ export async function getStaticPaths(
|
|||
const sdk = getReadySdk();
|
||||
const libraryItems = await sdk.getLibraryItemsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
if (libraryItems.libraryItems) {
|
||||
libraryItems.libraryItems.data.map((item) => {
|
||||
context.locales?.map((local) => {
|
||||
paths.push({ params: { slug: item.attributes?.slug }, locale: local });
|
||||
filterHasAttributes(libraryItems.libraryItems?.data).map((item) => {
|
||||
context.locales?.map((local) =>
|
||||
paths.push({ params: { slug: item.attributes?.slug }, locale: local })
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
paths,
|
||||
fallback: "blocking",
|
||||
|
|
|
@ -15,7 +15,7 @@ import { GetLibraryItemScansQuery } from "graphql/generated";
|
|||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
||||
import { isDefined, sortContent } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined, sortContent } from "helpers/others";
|
||||
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import {
|
||||
|
@ -146,14 +146,12 @@ export async function getStaticPaths(
|
|||
const sdk = getReadySdk();
|
||||
const libraryItems = await sdk.getLibraryItemsSlugs({});
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
if (libraryItems.libraryItems) {
|
||||
libraryItems.libraryItems.data.map((item) => {
|
||||
context.locales?.map((local) => {
|
||||
if (item.attributes)
|
||||
paths.push({ params: { slug: item.attributes.slug }, locale: local });
|
||||
filterHasAttributes(libraryItems.libraryItems?.data).map((item) => {
|
||||
context.locales?.map((local) =>
|
||||
paths.push({ params: { slug: item.attributes.slug }, locale: local })
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
paths,
|
||||
fallback: "blocking",
|
||||
|
|
|
@ -31,7 +31,13 @@ import {
|
|||
import { PreviewCard } from "components/PreviewCard";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
||||
import { isDefinedAndNotEmpty, isUndefined } from "helpers/others";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
isDefinedAndNotEmpty,
|
||||
isUndefined,
|
||||
iterateMap,
|
||||
} from "helpers/others";
|
||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
items: NonNullable<GetLibraryItemsPreviewQuery["libraryItems"]>["data"];
|
||||
|
@ -234,11 +240,18 @@ export default function Library(props: Props): JSX.Element {
|
|||
);
|
||||
const contentPanel = (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
{[...groups].map(([name, items]) => (
|
||||
{/* TODO: Add to langui */}
|
||||
{groups.size === 0 && (
|
||||
<ContentPlaceholder
|
||||
message={
|
||||
"No results. You can try changing or resetting the search parameters."
|
||||
}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
{iterateMap(groups, (name, items) => (
|
||||
<Fragment key={name}>
|
||||
{items.length > 0 && (
|
||||
<>
|
||||
{name && (
|
||||
{isDefinedAndNotEmpty(name) && (
|
||||
<h2
|
||||
className="flex flex-row place-items-center gap-2
|
||||
pb-2 pt-10 text-2xl first-of-type:pt-0"
|
||||
|
@ -256,9 +269,8 @@ export default function Library(props: Props): JSX.Element {
|
|||
last-of-type:border-0 desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]
|
||||
mobile:grid-cols-2 mobile:gap-4"
|
||||
>
|
||||
{items.map((item) => (
|
||||
{filterHasAttributes(items).map((item) => (
|
||||
<Fragment key={item.id}>
|
||||
{isDefinedAndNotEmpty(item.id) && item.attributes && (
|
||||
<PreviewCard
|
||||
href={`/library/${item.attributes.slug}`}
|
||||
title={item.attributes.title}
|
||||
|
@ -287,20 +299,15 @@ export default function Library(props: Props): JSX.Element {
|
|||
<PreviewCardCTAs
|
||||
id={item.id}
|
||||
displayCTAs={
|
||||
!isUntangibleGroupItem(
|
||||
item.attributes.metadata?.[0]
|
||||
)
|
||||
!isUntangibleGroupItem(item.attributes.metadata?.[0])
|
||||
}
|
||||
langui={langui}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</ContentPanel>
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
PostStaticProps,
|
||||
} from "graphql/getPostStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
|
||||
import {
|
||||
GetStaticPathsContext,
|
||||
|
@ -48,12 +48,11 @@ export async function getStaticPaths(
|
|||
const sdk = getReadySdk();
|
||||
const posts = await sdk.getPostsSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
if (posts.posts)
|
||||
posts.posts.data.map((item) => {
|
||||
context.locales?.map((local) => {
|
||||
if (item.attributes)
|
||||
paths.push({ params: { slug: item.attributes.slug }, locale: local });
|
||||
});
|
||||
|
||||
filterHasAttributes(posts.posts?.data).map((item) => {
|
||||
context.locales?.map((local) =>
|
||||
paths.push({ params: { slug: item.attributes.slug }, locale: local })
|
||||
);
|
||||
});
|
||||
return {
|
||||
paths,
|
||||
|
|
|
@ -19,6 +19,7 @@ import { WithLabel } from "components/Inputs/WithLabel";
|
|||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
posts: NonNullable<GetPostsPreviewQuery["posts"]>["data"];
|
||||
|
@ -87,9 +88,8 @@ export default function News(props: Props): JSX.Element {
|
|||
className="grid grid-cols-1 items-end gap-8
|
||||
desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]"
|
||||
>
|
||||
{filteredItems.map((post) => (
|
||||
{filterHasAttributes(filteredItems).map((post) => (
|
||||
<Fragment key={post.id}>
|
||||
{post.attributes && (
|
||||
<PreviewCard
|
||||
href={`/news/${post.attributes.slug}`}
|
||||
title={
|
||||
|
@ -109,7 +109,6 @@ export default function News(props: Props): JSX.Element {
|
|||
position: "Top",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
@ -145,19 +144,17 @@ export async function getStaticProps(
|
|||
}
|
||||
|
||||
function sortPosts(posts: Props["posts"]): Props["posts"] {
|
||||
const sortedPosts = [...posts];
|
||||
sortedPosts
|
||||
return posts
|
||||
.sort((a, b) => {
|
||||
const dateA = a.attributes?.date ? prettyDate(a.attributes.date) : "9999";
|
||||
const dateB = b.attributes?.date ? prettyDate(b.attributes.date) : "9999";
|
||||
return dateA.localeCompare(dateB);
|
||||
})
|
||||
.reverse();
|
||||
return sortedPosts;
|
||||
}
|
||||
|
||||
function filterItems(posts: Props["posts"], searchName: string) {
|
||||
return [...posts].filter((post) => {
|
||||
return posts.filter((post) => {
|
||||
if (searchName.length > 1) {
|
||||
if (
|
||||
post.attributes?.translations?.[0]?.title
|
||||
|
|
|
@ -14,7 +14,11 @@ import { SubPanel } from "components/Panels/SubPanel";
|
|||
import DefinitionCard from "components/Wiki/DefinitionCard";
|
||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
} from "helpers/others";
|
||||
import { WikiPageWithTranslations } from "helpers/types";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import {
|
||||
|
@ -89,20 +93,24 @@ export default function WikiPage(props: Props): JSX.Element {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{page.definitions?.map((definition, index) => (
|
||||
{filterHasAttributes(page.definitions, ["translations"]).map(
|
||||
(definition, index) => (
|
||||
<DefinitionCard
|
||||
key={index}
|
||||
source={definition?.source?.data?.attributes?.name}
|
||||
translations={definition?.translations?.map((translation) => ({
|
||||
language: translation?.language?.data?.attributes?.code,
|
||||
definition: translation?.definition,
|
||||
status: translation?.status,
|
||||
}))}
|
||||
source={definition.source?.data?.attributes?.name}
|
||||
translations={filterHasAttributes(definition.translations).map(
|
||||
(translation) => ({
|
||||
language: translation.language.data?.attributes?.code,
|
||||
definition: translation.definition,
|
||||
status: translation.status,
|
||||
})
|
||||
)}
|
||||
index={index + 1}
|
||||
languages={languages}
|
||||
langui={langui}
|
||||
/>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</ContentPanel>
|
||||
|
@ -147,14 +155,13 @@ export async function getStaticPaths(
|
|||
const sdk = getReadySdk();
|
||||
const contents = await sdk.getWikiPagesSlugs();
|
||||
const paths: GetStaticPathsResult["paths"] = [];
|
||||
contents.wikiPages?.data.map((wikiPage) => {
|
||||
context.locales?.map((local) => {
|
||||
if (wikiPage.attributes)
|
||||
filterHasAttributes(contents.wikiPages?.data).map((wikiPage) => {
|
||||
context.locales?.map((local) =>
|
||||
paths.push({
|
||||
params: { slug: wikiPage.attributes.slug },
|
||||
locale: local,
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
return {
|
||||
paths,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { GetChronologyItemsQuery, GetErasQuery } from "graphql/generated";
|
|||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { GetStaticPropsContext } from "next";
|
||||
import { Fragment } from "react";
|
||||
|
||||
|
@ -70,9 +70,8 @@ export default function Chronology(props: Props): JSX.Element {
|
|||
horizontalLine
|
||||
/>
|
||||
|
||||
{chronologyEras.map((era) => (
|
||||
{filterHasAttributes(chronologyEras).map((era) => (
|
||||
<Fragment key={era.id}>
|
||||
{era.attributes && (
|
||||
<NavOption
|
||||
url={`#${era.attributes.slug}`}
|
||||
title={
|
||||
|
@ -85,7 +84,6 @@ export default function Chronology(props: Props): JSX.Element {
|
|||
subtitle={`${era.attributes.starting_year} → ${era.attributes.ending_year}`}
|
||||
border
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</SubPanel>
|
||||
|
|
|
@ -20,6 +20,8 @@ import { Switch } from "components/Inputs/Switch";
|
|||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||
|
||||
interface Props extends AppStaticProps {
|
||||
pages: NonNullable<GetWikiPagesPreviewsQuery["wikiPages"]>["data"];
|
||||
|
@ -93,9 +95,17 @@ export default function Wiki(props: Props): JSX.Element {
|
|||
className="grid grid-cols-2 items-end gap-8
|
||||
desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))] mobile:gap-4"
|
||||
>
|
||||
{filteredPages.map((page) => (
|
||||
{/* TODO: Add to langui */}
|
||||
{filteredPages.length === 0 && (
|
||||
<ContentPlaceholder
|
||||
message={
|
||||
"No results. You can try changing or resetting the search parameters."
|
||||
}
|
||||
icon={Icon.ChevronLeft}
|
||||
/>
|
||||
)}
|
||||
{filterHasAttributes(filteredPages).map((page) => (
|
||||
<Fragment key={page.id}>
|
||||
{page.attributes && (
|
||||
<TranslatedPreviewCard
|
||||
href={`/wiki/${page.attributes.slug}`}
|
||||
translations={page.attributes.translations?.map(
|
||||
|
@ -116,7 +126,6 @@ export default function Wiki(props: Props): JSX.Element {
|
|||
(category) => category.attributes?.short ?? ""
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
@ -150,17 +159,15 @@ export async function getStaticProps(
|
|||
}
|
||||
|
||||
function sortPages(pages: Props["pages"]): Props["pages"] {
|
||||
const sortedPages = [...pages];
|
||||
sortedPages.sort((a, b) => {
|
||||
return pages.sort((a, b) => {
|
||||
const slugA = a.attributes?.slug ?? "";
|
||||
const slugB = b.attributes?.slug ?? "";
|
||||
return slugA.localeCompare(slugB);
|
||||
});
|
||||
return sortedPages;
|
||||
}
|
||||
|
||||
function filterPages(posts: Props["pages"], searchName: string) {
|
||||
return [...posts].filter((post) => {
|
||||
return posts.filter((post) => {
|
||||
if (searchName.length > 1) {
|
||||
if (
|
||||
post.attributes?.translations?.[0]?.title
|
||||
|
|
|
@ -31,7 +31,7 @@ mark {
|
|||
/* SCROLLBARS STYLING */
|
||||
|
||||
* {
|
||||
@apply [scrollbar-color:theme(colors.dark)_transparent] [scrollbar-width:thin];
|
||||
@apply [scrollbar-color:theme(colors.dark/1)_transparent] [scrollbar-width:thin];
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
|
|
Loading…
Reference in New Issue