Some new stuff
This commit is contained in:
parent
113c6566d9
commit
5adae32429
|
@ -0,0 +1,27 @@
|
|||
fragment reinCostume on ReinCostume {
|
||||
slug
|
||||
sprite {
|
||||
data {
|
||||
attributes {
|
||||
...uploadImage
|
||||
}
|
||||
}
|
||||
}
|
||||
translations {
|
||||
id
|
||||
language {
|
||||
data {
|
||||
attributes {
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
name
|
||||
description
|
||||
}
|
||||
emblem {
|
||||
data {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
query getReinCostumesWithoutEmblem {
|
||||
reinCostumes(filters: { emblem: { id: { null: true } } }, pagination: { limit: -1 }) {
|
||||
data {
|
||||
id
|
||||
attributes {
|
||||
...reinCostume
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
query getReinEmblems {
|
||||
reinEmblems(pagination: { limit: -1 }) {
|
||||
data {
|
||||
id
|
||||
attributes {
|
||||
slug
|
||||
translations {
|
||||
id
|
||||
name
|
||||
description
|
||||
language {
|
||||
data {
|
||||
attributes {
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
query getReinEmblemsSlugs {
|
||||
reinEmblems(pagination: { limit: -1 }) {
|
||||
data {
|
||||
id
|
||||
attributes {
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue