Some new stuff

This commit is contained in:
DrMint 2023-03-18 22:13:34 +01:00
parent 113c6566d9
commit 5adae32429
10 changed files with 507 additions and 0 deletions

View File

@ -0,0 +1,27 @@
fragment reinCostume on ReinCostume {
slug
sprite {
data {
attributes {
...uploadImage
}
}
}
translations {
id
language {
data {
attributes {
code
}
}
}
name
description
}
emblem {
data {
id
}
}
}

View File

@ -0,0 +1,34 @@
query getReinCostumes {
reinCostumes(pagination: { limit: -1 }) {
data {
id
attributes {
slug
sprite {
data {
attributes {
...uploadImage
}
}
}
translations {
id
language {
data {
attributes {
code
}
}
}
name
description
}
emblem {
data {
id
}
}
}
}
}
}

View File

@ -0,0 +1,10 @@
query getReinCostumesWithoutEmblem {
reinCostumes(filters: { emblem: { id: { null: true } } }, pagination: { limit: -1 }) {
data {
id
attributes {
...reinCostume
}
}
}
}

View File

@ -0,0 +1,30 @@
query getReinEmblem($slug: String) {
reinEmblems(filters: { slug: { eq: $slug } }) {
data {
id
attributes {
slug
translations {
id
name
description
language {
data {
attributes {
code
}
}
}
}
costumes {
data {
id
attributes {
...reinCostume
}
}
}
}
}
}
}

View File

@ -0,0 +1,22 @@
query getReinEmblems {
reinEmblems(pagination: { limit: -1 }) {
data {
id
attributes {
slug
translations {
id
name
description
language {
data {
attributes {
code
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,10 @@
query getReinEmblemsSlugs {
reinEmblems(pagination: { limit: -1 }) {
data {
id
attributes {
slug
}
}
}
}

View File

@ -167,6 +167,13 @@ const Wiki = (props: Props): JSX.Element => {
onClick={closeSubPanel}
border
/>
<NavOption
title={"Re[in]carnation"}
subtitle={format("costume", { count: Infinity })}
url="/wiki/rein/costumes"
onClick={closeSubPanel}
border
/>
</SubPanel>
);

View File

@ -0,0 +1,206 @@
import { GetStaticProps } from "next";
import { useCallback } from "react";
import Markdown from "markdown-to-jsx";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { getFormat } from "helpers/i18n";
import { getOpenGraph } from "helpers/openGraph";
import { ReinCostume, ReinEmblemCostume } from "types/types";
import { getReadySdk } from "graphql/sdk";
import { filterHasAttributes, isDefinedAndNotEmpty, isUndefined } from "helpers/asserts";
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { TranslatedProps } from "types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { prettySlug } from "helpers/formatters";
import { ElementsSeparator } from "helpers/component";
import { SubPanel } from "components/Containers/SubPanel";
import { ReturnButton } from "components/PanelComponents/ReturnButton";
import { useFormat } from "hooks/useFormat";
import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { TranslatedNavOption } from "components/PanelComponents/NavOption";
import { Img } from "components/Img";
import { TranslatedPreviewCard } from "components/PreviewCard";
/*
*
* PAGE
*/
interface Props extends AppLayoutRequired {
emblems: ReinEmblemCostume[];
}
const ReincarnationCostumes = ({ emblems, ...otherProps }: Props): JSX.Element => {
const { format } = useFormat();
const subPanel = (
<SubPanel>
<ElementsSeparator>
{[
<>
<ReturnButton
href="/wiki"
title={format("wiki")}
displayOnlyOn="3ColumnsLayout"
className="mb-10"
/>
<PanelHeader
icon="accessibility_new"
title={format("costume", { count: Infinity })}
description={format("costume_description")}
/>
</>,
<>
{emblems.map((emblem) => (
<TranslatedNavOption
key={emblem.id}
translations={filterHasAttributes(emblem.attributes.translations, [
"language.data.attributes.code",
] as const).map((translation) => ({
language: translation.language.data.attributes.code,
title: translation.name,
}))}
fallback={{ title: prettySlug(emblem.attributes.slug) }}
url={`#${emblem.attributes.slug}`}
/>
))}
</>,
]}
</ElementsSeparator>
</SubPanel>
);
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<div className="grid gap-12">
<ElementsSeparator>
{emblems.map((emblem) => (
<TranslatedEmblemCostume
key={emblem.id}
translations={filterHasAttributes(emblem.attributes.translations, [
"language.data.attributes.code",
] as const).map((translation) => ({
language: translation.language.data.attributes.code,
title: translation.name,
description: translation.description,
}))}
fallback={{ title: prettySlug(emblem.attributes.slug) }}
slug={emblem.attributes.slug}
costumes={emblem.attributes.costumes}
/>
))}
</ElementsSeparator>
</div>
</ContentPanel>
);
return <AppLayout subPanel={subPanel} contentPanel={contentPanel} {...otherProps} />;
};
export default ReincarnationCostumes;
/*
*
* NEXT DATA FETCHING
*/
export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk();
const { format } = getFormat(context.locale);
const emblems = (await sdk.getReinEmblems()).reinEmblems?.data;
const costumes = (await sdk.getReinCostumes()).reinCostumes?.data;
if (isUndefined(emblems) || isUndefined(costumes)) {
return { notFound: true };
}
const processedEmblems: ReinEmblemCostume[] = [];
filterHasAttributes(emblems, ["id", "attributes"] as const).forEach(({ id, attributes }) => {
const costumesOfCurrentEmblem = costumes.filter(
(costume) => costume.attributes?.emblem?.data?.id === id
);
const emblemCostume: ReinEmblemCostume = {
id,
attributes: { ...attributes, costumes: costumesOfCurrentEmblem },
};
processedEmblems.push(emblemCostume);
});
const costumesWithoutEmblem = costumes.filter((costume) =>
isUndefined(costume.attributes?.emblem?.data)
);
processedEmblems.push({
id: "others",
attributes: { slug: "others", costumes: costumesWithoutEmblem },
});
const props: Props = {
emblems: processedEmblems,
openGraph: getOpenGraph(format, format("costume", { count: Infinity })),
};
return {
props: props,
};
};
/*
*
* PRIVATE COMPONENTS
*/
interface EmblemCostumeProps {
slug: string;
title: string;
description?: string;
costumes: ReinCostume[];
}
const EmblemCostume = ({ slug, title, description, costumes }: EmblemCostumeProps): JSX.Element => (
<div>
<h2 id={slug} className="text-2xl">
{title}
</h2>
{isDefinedAndNotEmpty(description) && <Markdown>{description}</Markdown>}
<div className="grid grid-cols-[repeat(auto-fill,_minmax(12rem,1fr))] items-end gap-x-6 gap-y-8">
{costumes.map((costume) => (
<TranslatedPreviewCard
key={costume.id}
translations={filterHasAttributes(costume.attributes?.translations, [
"language.data.attributes.code",
] as const).map((translation) => ({
language: translation.language.data.attributes.code,
title: translation.name,
}))}
thumbnail={costume.attributes?.sprite?.data?.attributes}
keepInfoVisible
thumbnailAspectRatio="1/1"
thumbnailForceAspectRatio
thumbnailFitMethod="contain"
fallback={{ title: prettySlug(costume.attributes?.slug) }}
href="#"
/>
))}
</div>
</div>
);
export const TranslatedEmblemCostume = ({
translations,
fallback,
...otherProps
}: TranslatedProps<EmblemCostumeProps, "description" | "title">): JSX.Element => {
const [selectedTranslation] = useSmartLanguage({
items: translations,
languageExtractor: useCallback((item: { language: string }): string => item.language, []),
});
return (
<EmblemCostume
title={selectedTranslation?.title ?? fallback.title}
description={selectedTranslation?.description ?? fallback.description}
{...otherProps}
/>
);
};

View File

@ -0,0 +1,148 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import Markdown from "markdown-to-jsx";
import { useCallback } from "react";
import { getReadySdk } from "graphql/sdk";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty, isUndefined } from "helpers/asserts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { useFormat } from "hooks/useFormat";
import { GetReinEmblemQuery, ReinCostumeFragment } from "graphql/generated";
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
import { Img } from "components/Img";
import { TranslatedProps } from "types/TranslatedProps";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { prettySlug } from "helpers/formatters";
import { getFormat } from "helpers/i18n";
import { getOpenGraph } from "helpers/openGraph";
/*
*
* PAGE
*/
interface Props extends AppLayoutRequired {
emblem: NonNullable<
NonNullable<NonNullable<GetReinEmblemQuery["reinEmblems"]>["data"][number]>["attributes"]
>;
}
const ReinEmblem = ({ emblem, ...otherProps }: Props): JSX.Element => {
const { format } = useFormat();
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<div>
<TranslatedEmblemCostume
translations={filterHasAttributes(emblem.translations, [
"language.data.attributes.code",
] as const).map((translation) => ({
language: translation.language.data.attributes.code,
title: translation.name,
description: translation.description,
}))}
fallback={{ title: prettySlug(emblem.slug) }}
slug={emblem.slug}
costumes={filterHasAttributes(emblem.costumes?.data, ["attributes"] as const).map(
(costume) => costume.attributes
)}
/>
</div>
</ContentPanel>
);
return <AppLayout contentPanel={contentPanel} {...otherProps} />;
};
export default ReinEmblem;
/*
*
* NEXT DATA FETCHING
*/
export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk();
const { format } = getFormat(context.locale);
const slug =
context.params && isDefined(context.params.slug) ? context.params.slug.toString() : "";
const emblem = (await sdk.getReinEmblem({ slug })).reinEmblems?.data?.[0]?.attributes;
if (isUndefined(emblem)) {
return { notFound: true };
}
const props: Props = {
emblem,
openGraph: getOpenGraph(format, format("costume", { count: Infinity })),
};
return {
props: props,
};
};
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const getStaticPaths: GetStaticPaths = async (context) => {
const sdk = getReadySdk();
const contents = await sdk.getReinEmblemsSlugs();
const paths: GetStaticPathsResult["paths"] = [];
filterHasAttributes(contents.reinEmblems?.data, ["attributes"] as const).map((emblem) => {
context.locales?.map((local) =>
paths.push({
params: { slug: emblem.attributes.slug },
locale: local,
})
);
});
return {
paths,
fallback: "blocking",
};
};
/*
*
* PRIVATE COMPONENTS
*/
interface EmblemCostumeProps {
slug: string;
title: string;
description?: string;
costumes: ReinCostumeFragment[];
}
const EmblemCostume = ({ slug, title, description, costumes }: EmblemCostumeProps): JSX.Element => (
<div>
<h2 id={slug} className="text-2xl">
{title}
</h2>
{isDefinedAndNotEmpty(description) && <Markdown>{description}</Markdown>}
<div className="grid grid-cols-[repeat(auto-fill,_minmax(12rem,1fr))] items-start gap-x-6 gap-y-8">
{costumes.map((costume) => (
<div key={costume.slug}>
{costume.sprite?.data?.attributes && <Img src={costume.sprite.data.attributes} />}
<h3 className="text-xl">{costume.translations?.[0]?.name}</h3>
<Markdown>{costume.translations?.[0]?.description ?? ""}</Markdown>
</div>
))}
</div>
</div>
);
export const TranslatedEmblemCostume = ({
translations,
fallback,
...otherProps
}: TranslatedProps<EmblemCostumeProps, "description" | "title">): JSX.Element => {
const [selectedTranslation] = useSmartLanguage({
items: translations,
languageExtractor: useCallback((item: { language: string }): string => item.language, []),
});
return (
<EmblemCostume
title={selectedTranslation?.title ?? fallback.title}
description={selectedTranslation?.description ?? fallback.description}
{...otherProps}
/>
);
};

View File

@ -2,6 +2,8 @@ import {
GetChronicleQuery,
GetContentTextQuery,
GetPostQuery,
GetReinCostumesQuery,
GetReinEmblemsQuery,
GetWeaponQuery,
GetWikiPageQuery,
} from "graphql/generated";
@ -68,6 +70,17 @@ export type WeaponGroupPreview = NonNullable<
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
type ReinEmblem = NonNullable<GetReinEmblemsQuery["reinEmblems"]>["data"][number];
export type ReinCostume = NonNullable<GetReinCostumesQuery["reinCostumes"]>["data"][number];
export type ReinEmblemCostume = {
id: ReinEmblem["id"];
attributes: NonNullable<ReinEmblem["attributes"]> & { costumes: ReinCostume[] };
};
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export enum LibraryItemUserStatus {
None = 0,
Want = 1,