Categories and recorders are now localdata

This commit is contained in:
DrMint 2023-06-05 22:03:27 +02:00
parent c3796b4fe8
commit 284bbd6272
49 changed files with 1325 additions and 2158 deletions

View File

@ -1 +1,2 @@
.next .next
public/local-data/*

View File

@ -1,100 +1 @@
{ {"currencies":{"data":[{"id":"1","attributes":{"code":"EUR","symbol":"€","rate_to_usd":1.036166,"display_decimals":true}},{"id":"2","attributes":{"code":"CAD","symbol":"$","rate_to_usd":0.79319156,"display_decimals":true}},{"id":"3","attributes":{"code":"USD","symbol":"$","rate_to_usd":1,"display_decimals":true}},{"id":"4","attributes":{"code":"JPY","symbol":"¥","rate_to_usd":0.0083864261,"display_decimals":false}},{"id":"5","attributes":{"code":"BRL","symbol":"R$","rate_to_usd":0.19904328,"display_decimals":true}},{"id":"6","attributes":{"code":"GBP","symbol":"£","rate_to_usd":1.3181323,"display_decimals":true}},{"id":"7","attributes":{"code":"AUD","symbol":"$","rate_to_usd":0.7422,"display_decimals":true}},{"id":"8","attributes":{"code":"INR","symbol":"₹","rate_to_usd":0.013162881,"display_decimals":false}},{"id":"9","attributes":{"code":"NZD","symbol":"$","rate_to_usd":0.69089984,"display_decimals":true}},{"id":"10","attributes":{"code":"CHF","symbol":"CHF","rate_to_usd":1.0728706,"display_decimals":true}},{"id":"11","attributes":{"code":"CNY","symbol":"¥","rate_to_usd":0.141546,"display_decimals":true}}]}}
"currencies": {
"data": [
{
"id": "1",
"attributes": {
"code": "EUR",
"symbol": "€",
"rate_to_usd": 1.036166,
"display_decimals": true
}
},
{
"id": "2",
"attributes": {
"code": "CAD",
"symbol": "$",
"rate_to_usd": 0.79319156,
"display_decimals": true
}
},
{
"id": "3",
"attributes": { "code": "USD", "symbol": "$", "rate_to_usd": 1, "display_decimals": true }
},
{
"id": "4",
"attributes": {
"code": "JPY",
"symbol": "¥",
"rate_to_usd": 0.0083864261,
"display_decimals": false
}
},
{
"id": "5",
"attributes": {
"code": "BRL",
"symbol": "R$",
"rate_to_usd": 0.19904328,
"display_decimals": true
}
},
{
"id": "6",
"attributes": {
"code": "GBP",
"symbol": "£",
"rate_to_usd": 1.3181323,
"display_decimals": true
}
},
{
"id": "7",
"attributes": {
"code": "AUD",
"symbol": "$",
"rate_to_usd": 0.7422,
"display_decimals": true
}
},
{
"id": "8",
"attributes": {
"code": "INR",
"symbol": "₹",
"rate_to_usd": 0.013162881,
"display_decimals": false
}
},
{
"id": "9",
"attributes": {
"code": "NZD",
"symbol": "$",
"rate_to_usd": 0.69089984,
"display_decimals": true
}
},
{
"id": "10",
"attributes": {
"code": "CHF",
"symbol": "CHF",
"rate_to_usd": 1.0728706,
"display_decimals": true
}
},
{
"id": "11",
"attributes": {
"code": "CNY",
"symbol": "¥",
"rate_to_usd": 0.141546,
"display_decimals": true
}
}
]
}
}

View File

@ -1,29 +1 @@
{ {"languages":{"data":[{"id":"1","attributes":{"name":"French","code":"fr","localized_name":"Français"}},{"id":"2","attributes":{"name":"English","code":"en","localized_name":"English"}},{"id":"3","attributes":{"name":"Japanese","code":"ja","localized_name":"日本語"}},{"id":"4","attributes":{"name":"Spanish","code":"es","localized_name":"Español"}},{"id":"6","attributes":{"name":"Portuguese (Brazil)","code":"pt-br","localized_name":"Português (Brasil)"}},{"id":"8","attributes":{"name":"German","code":"de","localized_name":"Deutsch"}},{"id":"9","attributes":{"name":"Italian","code":"it","localized_name":"Italiano"}},{"id":"10","attributes":{"name":"Russian","code":"ru","localized_name":"русский"}},{"id":"11","attributes":{"name":"Korean","code":"ko","localized_name":"한국어"}},{"id":"12","attributes":{"name":"Chinese","code":"zh","localized_name":"中文"}}]}}
"languages": {
"data": [
{ "id": "1", "attributes": { "name": "French", "code": "fr", "localized_name": "Français" } },
{ "id": "2", "attributes": { "name": "English", "code": "en", "localized_name": "English" } },
{ "id": "3", "attributes": { "name": "Japanese", "code": "ja", "localized_name": "日本語" } },
{ "id": "4", "attributes": { "name": "Spanish", "code": "es", "localized_name": "Español" } },
{
"id": "6",
"attributes": {
"name": "Portuguese (Brazil)",
"code": "pt-br",
"localized_name": "Português (Brasil)"
}
},
{ "id": "8", "attributes": { "name": "German", "code": "de", "localized_name": "Deutsch" } },
{
"id": "9",
"attributes": { "name": "Italian", "code": "it", "localized_name": "Italiano" }
},
{
"id": "10",
"attributes": { "name": "Russian", "code": "ru", "localized_name": "русский" }
},
{ "id": "11", "attributes": { "name": "Korean", "code": "ko", "localized_name": "한국어" } },
{ "id": "12", "attributes": { "name": "Chinese", "code": "zh", "localized_name": "中文" } }
]
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,6 @@ import { Markdawn } from "components/Markdown/Markdawn";
import { RecorderChip } from "components/RecorderChip"; import { RecorderChip } from "components/RecorderChip";
import { ToolTip } from "components/ToolTip"; import { ToolTip } from "components/ToolTip";
import { atoms } from "contexts/atoms"; import { atoms } from "contexts/atoms";
import { RecorderChipFragment } from "graphql/generated";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { useAtomGetter } from "helpers/atoms"; import { useAtomGetter } from "helpers/atoms";
import { prettyLanguage } from "helpers/formatters"; import { prettyLanguage } from "helpers/formatters";
@ -18,12 +17,12 @@ interface Props {
languageCode?: string; languageCode?: string;
sourceLanguageCode?: string; sourceLanguageCode?: string;
status?: ContentStatus | null; status?: ContentStatus | null;
transcribers?: { attributes?: RecorderChipFragment | null }[]; transcribers?: RecorderChipsProps["recorders"];
translators?: { attributes?: RecorderChipFragment | null }[]; translators?: RecorderChipsProps["recorders"];
proofreaders?: { attributes?: RecorderChipFragment | null }[]; proofreaders?: RecorderChipsProps["recorders"];
dubbers?: { attributes?: RecorderChipFragment | null }[]; dubbers?: RecorderChipsProps["recorders"];
subbers?: { attributes?: RecorderChipFragment | null }[]; subbers?: RecorderChipsProps["recorders"];
authors?: { attributes?: RecorderChipFragment | null }[]; authors?: RecorderChipsProps["recorders"];
notes?: string | null; notes?: string | null;
} }
@ -118,14 +117,14 @@ export const Credits = ({
interface RecorderChipsProps { interface RecorderChipsProps {
title: string; title: string;
recorders: { attributes?: RecorderChipFragment | null }[]; recorders: { attributes?: { username: string } | null }[];
} }
const RecorderChips = ({ title, recorders }: RecorderChipsProps) => ( const RecorderChips = ({ title, recorders }: RecorderChipsProps) => (
<div className="flex flex-wrap place-content-center place-items-center gap-1"> <div className="flex flex-wrap place-content-center place-items-center gap-1">
<p className="pr-1 font-headers font-bold">{title}:</p> <p className="pr-1 font-headers font-bold">{title}:</p>
{filterHasAttributes(recorders, ["attributes"]).map((recorder) => ( {filterHasAttributes(recorders, ["attributes"]).map((recorder) => (
<RecorderChip key={recorder.attributes.anonymous_code} recorder={recorder.attributes} /> <RecorderChip key={recorder.attributes.username} username={recorder.attributes.username} />
))} ))}
</div> </div>
); );

View File

@ -24,7 +24,7 @@ import {
} from "shared/meilisearch-graphql-typings/meiliTypes"; } from "shared/meilisearch-graphql-typings/meiliTypes";
import { getVideoThumbnailURL } from "helpers/videos"; import { getVideoThumbnailURL } from "helpers/videos";
import { UpPressable } from "components/Containers/UpPressable"; import { UpPressable } from "components/Containers/UpPressable";
import { prettyItemSubType, prettySlug } from "helpers/formatters"; import { prettySlug } from "helpers/formatters";
import { Ico } from "components/Ico"; import { Ico } from "components/Ico";
import { useFormat } from "hooks/useFormat"; import { useFormat } from "hooks/useFormat";
@ -52,7 +52,14 @@ interface MultiResult {
export const SearchPopup = (): JSX.Element => { export const SearchPopup = (): JSX.Element => {
const [isSearchOpened, setSearchOpened] = useAtomPair(atoms.layout.searchOpened); const [isSearchOpened, setSearchOpened] = useAtomPair(atoms.layout.searchOpened);
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const { format } = useFormat(); const {
format,
formatCategory,
formatContentType,
formatWikiTag,
formatLibraryItemSubType,
formatWeaponType,
} = useFormat();
const [multiResult, setMultiResult] = useState<MultiResult>({}); const [multiResult, setMultiResult] = useState<MultiResult>({});
const fetchSearchResults = useCallback((q: string) => { const fetchSearchResults = useCallback((q: string) => {
@ -243,11 +250,11 @@ export const SearchPopup = (): JSX.Element => {
keepInfoVisible keepInfoVisible
topChips={ topChips={
item.metadata && item.metadata.length > 0 && item.metadata[0] item.metadata && item.metadata.length > 0 && item.metadata[0]
? [prettyItemSubType(item.metadata[0])] ? [formatLibraryItemSubType(item.metadata[0])]
: [] : []
} }
bottomChips={item.categories?.data.map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes?.short ?? "" (category) => formatCategory(category.attributes.slug)
)} )}
metadata={{ metadata={{
releaseDate: item.release_date, releaseDate: item.release_date,
@ -288,15 +295,11 @@ export const SearchPopup = (): JSX.Element => {
thumbnailForceAspectRatio thumbnailForceAspectRatio
topChips={ topChips={
item.type?.data?.attributes item.type?.data?.attributes
? [ ? [formatContentType(item.type.data.attributes.slug)]
item.type.data.attributes.titles?.[0]
? item.type.data.attributes.titles[0]?.title
: prettySlug(item.type.data.attributes.slug),
]
: undefined : undefined
} }
bottomChips={item.categories?.data.map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes?.short ?? "" (category) => formatCategory(category.attributes.slug)
)} )}
keepInfoVisible keepInfoVisible
/> />
@ -345,11 +348,11 @@ export const SearchPopup = (): JSX.Element => {
thumbnailRounded thumbnailRounded
thumbnailForceAspectRatio thumbnailForceAspectRatio
keepInfoVisible keepInfoVisible
topChips={filterHasAttributes(item.tags?.data, ["attributes"]).map( topChips={filterHasAttributes(item.tags?.data, ["attributes"]).map((tag) =>
(tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug) formatWikiTag(tag.attributes.slug)
)} )}
bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes.short (category) => formatCategory(category.attributes.slug)
)} )}
/> />
))} ))}
@ -384,8 +387,8 @@ export const SearchPopup = (): JSX.Element => {
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
thumbnailForceAspectRatio thumbnailForceAspectRatio
keepInfoVisible keepInfoVisible
bottomChips={item.categories?.data.map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes?.short ?? "" (category) => formatCategory(category.attributes.slug)
)} )}
metadata={{ metadata={{
releaseDate: item.date, releaseDate: item.date,
@ -466,11 +469,11 @@ export const SearchPopup = (): JSX.Element => {
keepInfoVisible keepInfoVisible
topChips={ topChips={
item.type?.data?.attributes?.slug item.type?.data?.attributes?.slug
? [prettySlug(item.type.data.attributes.slug)] ? [formatWeaponType(item.type.data.attributes.slug)]
: undefined : undefined
} }
bottomChips={filterHasAttributes(item.categories, ["attributes.short"]).map( bottomChips={filterHasAttributes(item.categories, ["attributes"]).map(
(category) => category.attributes.short (category) => formatCategory(category.attributes.slug)
)} )}
/> />
))} ))}

View File

@ -7,13 +7,14 @@ import { SubPanel } from "./Containers/SubPanel";
import { ThumbnailHeader } from "./ThumbnailHeader"; import { ThumbnailHeader } from "./ThumbnailHeader";
import { useSmartLanguage } from "hooks/useSmartLanguage"; import { useSmartLanguage } from "hooks/useSmartLanguage";
import { PostWithTranslations } from "types/types"; import { PostWithTranslations } from "types/types";
import { isDefined } from "helpers/asserts"; import { filterHasAttributes, isDefined } from "helpers/asserts";
import { prettySlug } from "helpers/formatters"; import { prettySlug } from "helpers/formatters";
import { useAtomGetter, useAtomSetter } from "helpers/atoms"; import { useAtomGetter, useAtomSetter } from "helpers/atoms";
import { atoms } from "contexts/atoms"; import { atoms } from "contexts/atoms";
import { ElementsSeparator } from "helpers/component"; import { ElementsSeparator } from "helpers/component";
import { HorizontalLine } from "components/HorizontalLine"; import { HorizontalLine } from "components/HorizontalLine";
import { Credits } from "components/Credits"; import { Credits } from "components/Credits";
import { useFormat } from "hooks/useFormat";
/* /*
* *
@ -48,6 +49,7 @@ export const PostPage = ({
displayTitle = true, displayTitle = true,
...otherProps ...otherProps
}: Props): JSX.Element => { }: Props): JSX.Element => {
const { formatCategory } = useFormat();
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
@ -104,7 +106,9 @@ export const PostPage = ({
thumbnail={thumbnail} thumbnail={thumbnail}
title={title} title={title}
description={excerpt} description={excerpt}
categories={post.categories} categories={filterHasAttributes(post.categories?.data, ["attributes"]).map((category) =>
formatCategory(category.attributes.slug)
)}
languageSwitcher={ languageSwitcher={
languageSwitcherProps.locales.size > 1 ? ( languageSwitcherProps.locales.size > 1 ? (
<LanguageSwitcher {...languageSwitcherProps} /> <LanguageSwitcher {...languageSwitcherProps} />

View File

@ -3,10 +3,12 @@ import { Img } from "./Img";
import { Markdawn } from "./Markdown/Markdawn"; import { Markdawn } from "./Markdown/Markdawn";
import { ToolTip } from "./ToolTip"; import { ToolTip } from "./ToolTip";
import { Chip } from "components/Chip"; import { Chip } from "components/Chip";
import { RecorderChipFragment } from "graphql/generated";
import { ImageQuality } from "helpers/img"; import { ImageQuality } from "helpers/img";
import { filterHasAttributes } from "helpers/asserts"; import { filterHasAttributes, isUndefined } from "helpers/asserts";
import { useFormat } from "hooks/useFormat"; import { useFormat } from "hooks/useFormat";
import { useAtomGetter } from "helpers/atoms";
import { atoms } from "contexts/atoms";
import { useSmartLanguage } from "hooks/useSmartLanguage";
/* /*
* *
@ -14,14 +16,22 @@ import { useFormat } from "hooks/useFormat";
*/ */
interface Props { interface Props {
className?: string; username: string;
recorder: RecorderChipFragment;
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const RecorderChip = ({ recorder }: Props): JSX.Element => { export const RecorderChip = ({ username }: Props): JSX.Element => {
const { format } = useFormat(); const { format } = useFormat();
const recorders = useAtomGetter(atoms.localData.recorders);
const recorder = recorders.find((elem) => elem.attributes?.username === username)?.attributes;
const [selectedBioTranslation] = useSmartLanguage({
items: recorder?.bio ?? [],
languageExtractor: (bio) => bio.language?.data?.attributes?.code,
});
if (isUndefined(recorder)) return <></>;
return ( return (
<ToolTip <ToolTip
@ -55,7 +65,7 @@ export const RecorderChip = ({ recorder }: Props): JSX.Element => {
)} )}
</div> </div>
</div> </div>
{recorder.bio?.[0] && <Markdawn text={recorder.bio[0].bio ?? ""} />} {selectedBioTranslation?.bio && <Markdawn text={selectedBioTranslation.bio} />}
</div> </div>
} }
placement="top"> placement="top">

View File

@ -2,10 +2,9 @@ import { Chip } from "components/Chip";
import { Img } from "components/Img"; import { Img } from "components/Img";
import { InsetBox } from "components/Containers/InsetBox"; import { InsetBox } from "components/Containers/InsetBox";
import { Markdawn } from "components/Markdown/Markdawn"; import { Markdawn } from "components/Markdown/Markdawn";
import { GetContentTextQuery, UploadImageFragment } from "graphql/generated"; import { UploadImageFragment } from "graphql/generated";
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters"; import { prettyInlineTitle, slugify } from "helpers/formatters";
import { ImageQuality } from "helpers/img"; import { ImageQuality } from "helpers/img";
import { filterHasAttributes } from "helpers/asserts";
import { useAtomGetter } from "helpers/atoms"; import { useAtomGetter } from "helpers/atoms";
import { atoms } from "contexts/atoms"; import { atoms } from "contexts/atoms";
import { useFormat } from "hooks/useFormat"; import { useFormat } from "hooks/useFormat";
@ -20,12 +19,8 @@ interface Props {
title: string | null | undefined; title: string | null | undefined;
subtitle?: string | null | undefined; subtitle?: string | null | undefined;
description?: string | null | undefined; description?: string | null | undefined;
type?: NonNullable< type?: string;
NonNullable<GetContentTextQuery["contents"]>["data"][number]["attributes"] categories?: string[];
>["type"];
categories?: NonNullable<
NonNullable<GetContentTextQuery["contents"]>["data"][number]["attributes"]
>["categories"];
thumbnail?: UploadImageFragment | null | undefined; thumbnail?: UploadImageFragment | null | undefined;
className?: string; className?: string;
languageSwitcher?: JSX.Element; languageSwitcher?: JSX.Element;
@ -72,25 +67,21 @@ export const ThumbnailHeader = ({
</div> </div>
<div className="flew-wrap flex flex-row place-content-center gap-8"> <div className="flew-wrap flex flex-row place-content-center gap-8">
{type?.data?.attributes && ( {type && (
<div className="flex flex-col place-items-center gap-2"> <div className="flex flex-col place-items-center gap-2">
<h3 className="text-xl">{format("type", { count: 1 })}</h3> <h3 className="text-xl">{format("type", { count: 1 })}</h3>
<div className="flex flex-row flex-wrap"> <div className="flex flex-row flex-wrap">
<Chip <Chip text={type} />
text={
type.data.attributes.titles?.[0]?.title ?? prettySlug(type.data.attributes.slug)
}
/>
</div> </div>
</div> </div>
)} )}
{categories && categories.data.length > 0 && ( {categories && categories.length > 0 && (
<div className="flex flex-col place-items-center gap-2"> <div className="flex flex-col place-items-center gap-2">
<h3 className="text-xl">{format("category", { count: categories.data.length })}</h3> <h3 className="text-xl">{format("category", { count: categories.length })}</h3>
<div className="flex flex-row flex-wrap place-content-center gap-2"> <div className="flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(categories.data, ["attributes", "id"]).map((category) => ( {categories.map((category) => (
<Chip key={category.id} text={category.attributes.name} /> <Chip key={category} text={category} />
))} ))}
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@ import { userAgent } from "contexts/userAgent";
import { atomPairing } from "helpers/atoms"; import { atomPairing } from "helpers/atoms";
import { settings } from "contexts/settings"; import { settings } from "contexts/settings";
import { UploadImageFragment } from "graphql/generated"; import { UploadImageFragment } from "graphql/generated";
import { Languages, Currencies, Langui } from "helpers/localData"; import { Languages, Currencies, Langui, Recorders, TypesTranslations } from "helpers/localData";
/* [ LOCAL DATA ATOMS ] */ /* [ LOCAL DATA ATOMS ] */
@ -13,12 +13,29 @@ const languages = atomPairing(atom<Languages>([]));
const currencies = atomPairing(atom<Currencies>([])); const currencies = atomPairing(atom<Currencies>([]));
const langui = atomPairing(atom<Langui>({})); const langui = atomPairing(atom<Langui>({}));
const fallbackLangui = atomPairing(atom<Langui>({})); const fallbackLangui = atomPairing(atom<Langui>({}));
const recorders = atomPairing(atom<Recorders>([]));
const typesTranslations = atomPairing(
atom<TypesTranslations>({
audioSubtypes: [],
categories: [],
contentTypes: [],
gamePlatforms: [],
groupSubtypes: [],
metadataTypes: [],
textualSubtypes: [],
videoSubtypes: [],
wikiPagesTags: [],
weaponTypes: [],
})
);
const localData = { const localData = {
languages: languages[0], languages: languages[0],
currencies: currencies[0], currencies: currencies[0],
langui: langui[0], langui: langui[0],
fallbackLangui: fallbackLangui[0], fallbackLangui: fallbackLangui[0],
recorders: recorders[0],
typesTranslations: typesTranslations[0],
}; };
/* [ LIGHTBOX ATOMS ] */ /* [ LIGHTBOX ATOMS ] */
@ -70,5 +87,5 @@ export const atoms = {
// Do not import outside of the "contexts" folder // Do not import outside of the "contexts" folder
export const internalAtoms = { export const internalAtoms = {
lightBox: lightBoxAtom, lightBox: lightBoxAtom,
localData: { languages, currencies, langui, fallbackLangui }, localData: { languages, currencies, langui, fallbackLangui, recorders, typesTranslations },
}; };

View File

@ -7,10 +7,17 @@ import {
LocalDataGetWebsiteInterfacesQuery, LocalDataGetWebsiteInterfacesQuery,
LocalDataGetCurrenciesQuery, LocalDataGetCurrenciesQuery,
LocalDataGetLanguagesQuery, LocalDataGetLanguagesQuery,
LocalDataGetRecordersQuery,
} from "graphql/generated"; } from "graphql/generated";
import { LocalDataFile } from "graphql/fetchLocalData"; import { LocalDataFile } from "graphql/fetchLocalData";
import { internalAtoms } from "contexts/atoms"; import { internalAtoms } from "contexts/atoms";
import { processLanguages, processCurrencies, processLangui } from "helpers/localData"; import {
processLanguages,
processCurrencies,
processLangui,
processRecorders,
processTypesTranslations,
} from "helpers/localData";
import { getLogger } from "helpers/logger"; import { getLogger } from "helpers/logger";
const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`; const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`;
@ -21,6 +28,8 @@ export const useLocalData = (): void => {
const setCurrencies = useAtomSetter(internalAtoms.localData.currencies); const setCurrencies = useAtomSetter(internalAtoms.localData.currencies);
const setLangui = useAtomSetter(internalAtoms.localData.langui); const setLangui = useAtomSetter(internalAtoms.localData.langui);
const setFallbackLangui = useAtomSetter(internalAtoms.localData.fallbackLangui); const setFallbackLangui = useAtomSetter(internalAtoms.localData.fallbackLangui);
const setRecorders = useAtomSetter(internalAtoms.localData.recorders);
const setTypesTranslations = useAtomSetter(internalAtoms.localData.typesTranslations);
const { locale } = useRouter(); const { locale } = useRouter();
const { data: rawLanguages } = useFetch<LocalDataGetLanguagesQuery>(getFileName("languages")); const { data: rawLanguages } = useFetch<LocalDataGetLanguagesQuery>(getFileName("languages"));
@ -28,6 +37,10 @@ export const useLocalData = (): void => {
const { data: rawLangui } = useFetch<LocalDataGetWebsiteInterfacesQuery>( const { data: rawLangui } = useFetch<LocalDataGetWebsiteInterfacesQuery>(
getFileName("websiteInterfaces") getFileName("websiteInterfaces")
); );
const { data: rawRecorders } = useFetch<LocalDataGetRecordersQuery>(getFileName("recorders"));
const { data: rawTypesTranslations } = useFetch<LocalDataGetRecordersQuery>(
getFileName("typesTranslations")
);
useEffect(() => { useEffect(() => {
logger.log("Refresh languages"); logger.log("Refresh languages");
@ -48,4 +61,14 @@ export const useLocalData = (): void => {
logger.log("Refresh fallback langui"); logger.log("Refresh fallback langui");
setFallbackLangui(processLangui(rawLangui, "en")); setFallbackLangui(processLangui(rawLangui, "en"));
}, [rawLangui, setFallbackLangui]); }, [rawLangui, setFallbackLangui]);
useEffect(() => {
logger.log("Refresh recorders");
setRecorders(processRecorders(rawRecorders));
}, [rawRecorders, setRecorders]);
useEffect(() => {
logger.log("Refresh types translations");
setTypesTranslations(processTypesTranslations(rawTypesTranslations));
}, [rawTypesTranslations, setTypesTranslations]);
}; };

View File

@ -3,8 +3,16 @@ import { resolve } from "path";
import { readFileSync, writeFileSync } from "fs"; import { readFileSync, writeFileSync } from "fs";
import { config } from "dotenv"; import { config } from "dotenv";
import { getReadySdk } from "./sdk"; import { getReadySdk } from "./sdk";
import { LocalDataGetWebsiteInterfacesQuery } from "./generated"; import {
import { processLangui, Langui } from "helpers/localData"; LocalDataGetTypesTranslationsQuery,
LocalDataGetWebsiteInterfacesQuery,
} from "./generated";
import {
processLangui,
Langui,
TypesTranslations,
processTypesTranslations,
} from "helpers/localData";
import { getLogger } from "helpers/logger"; import { getLogger } from "helpers/logger";
config({ path: resolve(process.cwd(), ".env.local") }); config({ path: resolve(process.cwd(), ".env.local") });
@ -12,7 +20,7 @@ config({ path: resolve(process.cwd(), ".env.local") });
const LOCAL_DATA_FOLDER = `${process.cwd()}/public/local-data`; const LOCAL_DATA_FOLDER = `${process.cwd()}/public/local-data`;
const logger = getLogger("💽 [Local Data]", "server"); const logger = getLogger("💽 [Local Data]", "server");
const writeLocalData = (name: LocalDataFile, localData: unknown) => { const writeLocalData = (name: LocalDataFile, localData: object) => {
const path = `${LOCAL_DATA_FOLDER}/${name}.json`; const path = `${LOCAL_DATA_FOLDER}/${name}.json`;
writeFileSync(path, JSON.stringify(localData), { encoding: "utf-8" }); writeFileSync(path, JSON.stringify(localData), { encoding: "utf-8" });
logger.log(`${name}.json has been written`); logger.log(`${name}.json has been written`);
@ -23,22 +31,58 @@ const readLocalData = <T>(name: LocalDataFile): T => {
return JSON.parse(readFileSync(path, { encoding: "utf8" })); return JSON.parse(readFileSync(path, { encoding: "utf8" }));
}; };
export const fetchLocalData = async (): Promise<void> => { export const fetchWebsiteInterfaces = async (): Promise<void> => {
const sdk = getReadySdk(); const sdk = getReadySdk();
writeLocalData("websiteInterfaces", await sdk.localDataGetWebsiteInterfaces()); writeLocalData("websiteInterfaces", await sdk.localDataGetWebsiteInterfaces());
};
export const fetchCurrencies = async (): Promise<void> => {
const sdk = getReadySdk();
writeLocalData("currencies", await sdk.localDataGetCurrencies()); writeLocalData("currencies", await sdk.localDataGetCurrencies());
};
export const fetchLanguages = async (): Promise<void> => {
const sdk = getReadySdk();
writeLocalData("languages", await sdk.localDataGetLanguages()); writeLocalData("languages", await sdk.localDataGetLanguages());
}; };
export const fetchRecorders = async (): Promise<void> => {
const sdk = getReadySdk();
writeLocalData("recorders", await sdk.localDataGetRecorders());
};
export const fetchTypesTranslations = async (): Promise<void> => {
const sdk = getReadySdk();
writeLocalData("typesTranslations", await sdk.localDataGetTypesTranslations());
};
const fetchLocalData = async (): Promise<void> => {
await fetchWebsiteInterfaces();
await fetchCurrencies();
await fetchLanguages();
await fetchRecorders();
await fetchTypesTranslations();
};
if (process.argv[2] === "--esrun") { if (process.argv[2] === "--esrun") {
fetchLocalData(); fetchLocalData();
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export type LocalDataFile = "currencies" | "languages" | "websiteInterfaces"; export type LocalDataFile =
| "currencies"
| "languages"
| "recorders"
| "typesTranslations"
| "websiteInterfaces";
export const getLangui = (locale: string | undefined): Langui => { export const getLangui = (locale: string): Langui => {
const websiteInterfaces = readLocalData<LocalDataGetWebsiteInterfacesQuery>("websiteInterfaces"); const websiteInterfaces = readLocalData<LocalDataGetWebsiteInterfacesQuery>("websiteInterfaces");
return processLangui(websiteInterfaces, locale); return processLangui(websiteInterfaces, locale);
}; };
export const getTypesTranslations = (): TypesTranslations => {
const typesTranslations = readLocalData<LocalDataGetTypesTranslationsQuery>("typesTranslations");
return processTypesTranslations(typesTranslations);
};

View File

@ -1,23 +0,0 @@
fragment recorderChip on Recorder {
username
anonymize
anonymous_code
pronouns
bio(filters: { language: { code: { eq: $language_code } } }) {
bio
}
languages(pagination: { limit: -1 }) {
data {
attributes {
code
}
}
}
avatar {
data {
attributes {
...uploadImage
}
}
}
}

View File

@ -14,9 +14,8 @@ fragment relatedContentPreview on Content {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
short slug
} }
} }
} }
@ -24,9 +23,6 @@ fragment relatedContentPreview on Content {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }

View File

@ -17,10 +17,9 @@ export const getPostStaticProps =
(slug: string): GetStaticProps => (slug: string): GetStaticProps =>
async (context) => { async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format, formatCategory } = getFormat(context.locale);
const post = await sdk.getPost({ const post = await sdk.getPost({
slug: slug, slug: slug,
language_code: context.locale ?? "en",
}); });
if (!post.posts?.data[0]?.attributes?.translations || !context.locale || !context.locales) { if (!post.posts?.data[0]?.attributes?.translations || !context.locale || !context.locales) {
@ -40,7 +39,7 @@ export const getPostStaticProps =
[format("category", { count: Infinity })]: filterHasAttributes( [format("category", { count: Infinity })]: filterHasAttributes(
post.posts.data[0].attributes.categories?.data, post.posts.data[0].attributes.categories?.data,
["attributes"] ["attributes"]
).map((category) => category.attributes.short), ).map((category) => formatCategory(category.attributes.slug)),
}); });
const thumbnail = const thumbnail =

View File

@ -1,4 +1,4 @@
query getChronicle($slug: String, $language_code: String) { query getChronicle($slug: String) {
chronicles(filters: { slug: { eq: $slug } }) { chronicles(filters: { slug: { eq: $slug } }) {
data { data {
attributes { attributes {
@ -53,21 +53,21 @@ query getChronicle($slug: String, $language_code: String) {
authors { authors {
data { data {
attributes { attributes {
...recorderChip username
} }
} }
} }
translators { translators {
data { data {
attributes { attributes {
...recorderChip username
} }
} }
} }
proofreaders { proofreaders {
data { data {
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -80,10 +80,8 @@ query getChronicle($slug: String, $language_code: String) {
slug slug
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -91,9 +89,6 @@ query getChronicle($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -123,7 +118,7 @@ query getChronicle($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -131,7 +126,7 @@ query getChronicle($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -139,7 +134,7 @@ query getChronicle($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }

View File

@ -1,4 +1,4 @@
query getContentText($slug: String, $language_code: String) { query getContentText($slug: String) {
contents(filters: { slug: { eq: $slug } }) { contents(filters: { slug: { eq: $slug } }) {
data { data {
id id
@ -6,10 +6,8 @@ query getContentText($slug: String, $language_code: String) {
slug slug
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -17,9 +15,6 @@ query getContentText($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -53,10 +48,8 @@ query getContentText($slug: String, $language_code: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -67,19 +60,15 @@ query getContentText($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
} }
... on ComponentMetadataGame { ... on ComponentMetadataGame {
platforms(pagination: { limit: -1 }) { platform {
data { data {
id
attributes { attributes {
short slug
} }
} }
} }
@ -89,9 +78,6 @@ query getContentText($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -101,9 +87,6 @@ query getContentText($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -113,9 +96,6 @@ query getContentText($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -123,9 +103,6 @@ query getContentText($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -163,7 +140,7 @@ query getContentText($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -171,7 +148,7 @@ query getContentText($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -179,7 +156,7 @@ query getContentText($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -198,7 +175,7 @@ query getContentText($slug: String, $language_code: String) {
subbers(pagination: { limit: -1 }) { subbers(pagination: { limit: -1 }) {
data { data {
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -216,7 +193,7 @@ query getContentText($slug: String, $language_code: String) {
dubbers(pagination: { limit: -1 }) { dubbers(pagination: { limit: -1 }) {
data { data {
attributes { attributes {
...recorderChip username
} }
} }
} }

View File

@ -1,4 +1,4 @@
query getContentsFolder($slug: String, $language_code: String) { query getContentsFolder($slug: String) {
contentsFolders(filters: { slug: { eq: $slug } }) { contentsFolders(filters: { slug: { eq: $slug } }) {
data { data {
attributes { attributes {
@ -22,10 +22,8 @@ query getContentsFolder($slug: String, $language_code: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -33,9 +31,6 @@ query getContentsFolder($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
query getLibraryItem($slug: String, $language_code: String) { query getLibraryItem($slug: String) {
libraryItems(filters: { slug: { eq: $slug } }) { libraryItems(filters: { slug: { eq: $slug } }) {
data { data {
id id
@ -33,10 +33,8 @@ query getLibraryItem($slug: String, $language_code: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -58,9 +56,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -81,19 +76,15 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
} }
... on ComponentMetadataGame { ... on ComponentMetadataGame {
platforms(pagination: { limit: -1 }) { platform {
data { data {
id
attributes { attributes {
short slug
} }
} }
} }
@ -127,9 +118,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -144,9 +132,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -154,9 +139,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -194,10 +176,8 @@ query getLibraryItem($slug: String, $language_code: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -208,19 +188,16 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
} }
... on ComponentMetadataGame { ... on ComponentMetadataGame {
platforms { platform {
data { data {
id id
attributes { attributes {
short slug
} }
} }
} }
@ -230,9 +207,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -242,9 +216,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -254,9 +225,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -264,9 +232,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -317,10 +282,8 @@ query getLibraryItem($slug: String, $language_code: String) {
slug slug
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -328,9 +291,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
query getLibraryItemScans($slug: String, $language_code: String) { query getLibraryItemScans($slug: String) {
libraryItems(filters: { slug: { eq: $slug } }) { libraryItems(filters: { slug: { eq: $slug } }) {
data { data {
id id
@ -27,7 +27,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -35,7 +35,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -43,7 +43,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -156,10 +156,8 @@ query getLibraryItemScans($slug: String, $language_code: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -171,19 +169,16 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
} }
... on ComponentMetadataGame { ... on ComponentMetadataGame {
platforms { platform {
data { data {
id id
attributes { attributes {
short slug
} }
} }
} }
@ -193,9 +188,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -205,9 +197,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -217,9 +206,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -227,9 +213,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -290,7 +273,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -298,7 +281,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -306,7 +289,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }

View File

@ -1,4 +1,4 @@
query getPost($slug: String, $language_code: String) { query getPost($slug: String) {
posts(filters: { slug: { eq: $slug } }) { posts(filters: { slug: { eq: $slug } }) {
data { data {
id id
@ -12,16 +12,14 @@ query getPost($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }

View File

@ -23,9 +23,8 @@ query getVideo($uid: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
short slug
} }
} }
} }

View File

@ -1,4 +1,4 @@
query getWeapon($slug: String, $language_code: String) { query getWeapon($slug: String) {
weaponStories(filters: { slug: { eq: $slug } }) { weaponStories(filters: { slug: { eq: $slug } }) {
data { data {
attributes { attributes {
@ -7,10 +7,8 @@ query getWeapon($slug: String, $language_code: String) {
id id
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
@ -57,16 +55,6 @@ fragment sharedWeaponFragment on WeaponStory {
id id
attributes { attributes {
slug slug
translations(filters: { language: { code: { eq: $language_code } } }) {
name
language {
data {
attributes {
code
}
}
}
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
query getWikiPage($slug: String, $language_code: String) { query getWikiPage($slug: String) {
wikiPages(filters: { slug: { eq: $slug } }) { wikiPages(filters: { slug: { eq: $slug } }) {
data { data {
id id
@ -13,21 +13,15 @@ query getWikiPage($slug: String, $language_code: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }
tags { tags {
data { data {
id
attributes { attributes {
slug slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
} }
} }
} }
@ -58,7 +52,7 @@ query getWikiPage($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -66,7 +60,7 @@ query getWikiPage($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -74,7 +68,7 @@ query getWikiPage($slug: String, $language_code: String) {
data { data {
id id
attributes { attributes {
...recorderChip username
} }
} }
} }
@ -90,10 +84,8 @@ query getWikiPage($slug: String, $language_code: String) {
} }
categories(pagination: { limit: -1 }) { categories(pagination: { limit: -1 }) {
data { data {
id
attributes { attributes {
name slug
short
} }
} }
} }

View File

@ -0,0 +1,36 @@
query localDataGetRecorders {
recorders(pagination: { limit: -1 }) {
data {
attributes {
username
anonymize
anonymous_code
pronouns
bio(pagination: { limit: -1 }) {
bio
language {
data {
attributes {
code
}
}
}
}
languages(pagination: { limit: -1 }) {
data {
attributes {
code
}
}
}
avatar {
data {
attributes {
...uploadImage
}
}
}
}
}
}
}

View File

@ -0,0 +1,183 @@
query localDataGetTypesTranslations {
metadataTypes(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
audioSubtypes(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
videoSubtypes(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
textualSubtypes(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
groupSubtypes(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
gamePlatforms(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
short
}
}
}
}
contentTypes(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
wikiPagesTags(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
weaponStoryTypes(pagination: { limit: -1 }) {
data {
attributes {
slug
translations {
language {
data {
attributes {
code
}
}
}
name
}
}
}
}
categories(pagination: { limit: -1 }) {
data {
attributes {
slug
titles {
language {
data {
attributes {
code
}
}
}
title
short
}
}
}
}
}

View File

@ -114,11 +114,11 @@ query localDataGetWebsiteInterfaces {
previous_content previous_content
followup_content followup_content
videos videos
view_on view_on_x
channel channel
subscribers subscribers
description description
available_at available_at_x
want_it want_it
have_it have_it
source source

View File

@ -62,135 +62,6 @@ export const prettyInlineTitle = (
return result; return result;
}; };
export const prettyItemSubType = (
metadata:
| {
__typename: "ComponentMetadataAudio";
subtype?: {
data?: {
attributes?: {
slug: string;
titles?:
| ({
title: string;
} | null)[]
| null;
} | null;
} | null;
} | null;
}
| {
__typename: "ComponentMetadataBooks";
subtype?: {
data?: {
attributes?: {
slug: string;
titles?:
| ({
title: string;
} | null)[]
| null;
} | null;
} | null;
} | null;
}
| {
__typename: "ComponentMetadataGame";
platforms?: {
data: {
id?: string | null;
attributes?: {
short: string;
} | null;
}[];
} | null;
}
| {
__typename: "ComponentMetadataGroup";
subtype?: {
data?: {
attributes?: {
slug: string;
titles?:
| ({
title: string;
} | null)[]
| null;
} | null;
} | null;
} | null;
subitems_type?: {
data?: {
attributes?: {
slug: string;
titles?:
| ({
title: string;
} | null)[]
| null;
} | null;
} | null;
} | null;
}
| {
__typename: "ComponentMetadataVideo";
subtype?: {
data?: {
attributes?: {
slug: string;
titles?:
| ({
title: string;
} | null)[]
| null;
} | null;
} | null;
} | null;
}
| { __typename: "ComponentMetadataOther" }
| { __typename: "Error" }
| null
): string => {
if (metadata) {
switch (metadata.__typename) {
case "ComponentMetadataAudio":
case "ComponentMetadataBooks":
case "ComponentMetadataVideo":
return metadata.subtype?.data?.attributes?.titles &&
metadata.subtype.data.attributes.titles.length > 0 &&
metadata.subtype.data.attributes.titles[0]
? metadata.subtype.data.attributes.titles[0].title
: prettySlug(metadata.subtype?.data?.attributes?.slug);
case "ComponentMetadataGame":
return metadata.platforms?.data &&
metadata.platforms.data.length > 0 &&
metadata.platforms.data[0]?.attributes
? metadata.platforms.data[0].attributes.short
: "";
case "ComponentMetadataGroup": {
const firstPart =
metadata.subtype?.data?.attributes?.titles &&
metadata.subtype.data.attributes.titles.length > 0 &&
metadata.subtype.data.attributes.titles[0]
? metadata.subtype.data.attributes.titles[0].title
: prettySlug(metadata.subtype?.data?.attributes?.slug);
const secondPart =
metadata.subitems_type?.data?.attributes?.titles &&
metadata.subitems_type.data.attributes.titles.length > 0 &&
metadata.subitems_type.data.attributes.titles[0]
? metadata.subitems_type.data.attributes.titles[0].title
: prettySlug(metadata.subitems_type?.data?.attributes?.slug);
return `${secondPart} ${firstPart}`;
}
default:
return "";
}
}
return "";
};
/* eslint-enable id-denylist */
export const prettyShortenNumber = (number: number): string => { export const prettyShortenNumber = (number: number): string => {
if (number > 1_000_000) { if (number > 1_000_000) {
return `${(number / 1_000_000).toLocaleString(undefined, { return `${(number / 1_000_000).toLocaleString(undefined, {

View File

@ -2,7 +2,9 @@ import { IntlMessageFormat } from "intl-messageformat";
import { LibraryItemMetadataDynamicZone } from "graphql/generated"; import { LibraryItemMetadataDynamicZone } from "graphql/generated";
import { ICUParams } from "graphql/icuParams"; import { ICUParams } from "graphql/icuParams";
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { getLangui } from "graphql/fetchLocalData"; import { getLangui, getTypesTranslations } from "graphql/fetchLocalData";
import { prettySlug } from "helpers/formatters";
import { LibraryItemMetadata } from "types/types";
type WordingKey = keyof ICUParams; type WordingKey = keyof ICUParams;
type LibraryItemType = Exclude<LibraryItemMetadataDynamicZone["__typename"], undefined>; type LibraryItemType = Exclude<LibraryItemMetadataDynamicZone["__typename"], undefined>;
@ -29,18 +31,24 @@ const componentSetsTextsetStatusToWording: Record<
}; };
export const getFormat = ( export const getFormat = (
locale: string | undefined locale: string | undefined = "en"
): { ): {
format: <K extends WordingKey>( format: <K extends WordingKey>(
key: K, key: K,
...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]] ...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]]
) => string; ) => string;
formatLibraryItemType: (metadata: { __typename: LibraryItemType }) => string; formatLibraryItemType: (metadata: LibraryItemMetadata) => string;
formatLibraryItemSubType: (metadata: LibraryItemMetadata) => string;
formatStatusLabel: (status: ContentStatus) => string; formatStatusLabel: (status: ContentStatus) => string;
formatStatusDescription: (status: ContentStatus) => string; formatStatusDescription: (status: ContentStatus) => string;
formatCategory: (slug: string, type?: "default" | "full") => string;
formatContentType: (slug: string) => string;
formatWikiTag: (slug: string) => string;
formatWeaponType: (slug: string) => string;
} => { } => {
const langui = getLangui(locale); const langui = getLangui(locale);
const fallbackLangui = getLangui("en"); const fallbackLangui = getLangui("en");
const typesTranslations = getTypesTranslations();
const format = ( const format = (
key: WordingKey, key: WordingKey,
@ -65,8 +73,90 @@ export const getFormat = (
return key; return key;
}; };
const formatLibraryItemType = (metadata: { __typename: LibraryItemType }): string => const formatLibraryItemType = (metadata: LibraryItemMetadata): string =>
format(componentMetadataToWording[metadata.__typename]); metadata ? format(componentMetadataToWording[metadata.__typename]) : format("other");
const formatLibraryItemSubType = (metadata: LibraryItemMetadata): string => {
switch (metadata?.__typename) {
case "ComponentMetadataAudio": {
const slug = metadata.subtype?.data?.attributes?.slug;
const subtype = typesTranslations.audioSubtypes.find(
(type) => type.attributes?.slug === slug
);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataBooks": {
const slug = metadata.subtype?.data?.attributes?.slug;
const subtype = typesTranslations.textualSubtypes.find(
(type) => type.attributes?.slug === slug
);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataVideo": {
const slug = metadata.subtype?.data?.attributes?.slug;
const subtype = typesTranslations.videoSubtypes.find(
(type) => type.attributes?.slug === slug
);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataGame": {
const slug = metadata.platform?.data?.attributes?.slug;
const subtype = typesTranslations.gamePlatforms.find(
(type) => type.attributes?.slug === slug
);
console.log(slug);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataGroup": {
const subItemType = (() => {
const subitemTypeSlug = metadata.subitems_type?.data?.attributes?.slug;
const subItemTypeTranslations = typesTranslations.metadataTypes.find(
(type) => type.attributes?.slug === subitemTypeSlug
);
const findTranslation = (givenLocale: string) =>
subItemTypeTranslations?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
})();
const groupType = (() => {
const groupTypeSlug = metadata.subtype?.data?.attributes?.slug;
const groupTypeTranslations = typesTranslations.groupSubtypes.find(
(type) => type.attributes?.slug === groupTypeSlug
);
const findTranslation = (givenLocale: string) =>
groupTypeTranslations?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
})();
return `${groupType} - ${subItemType}`;
}
default:
return format("other");
}
};
const formatStatusLabel = (status: ContentStatus): string => const formatStatusLabel = (status: ContentStatus): string =>
format(componentSetsTextsetStatusToWording[status].label); format(componentSetsTextsetStatusToWording[status].label);
@ -74,10 +164,65 @@ export const getFormat = (
const formatStatusDescription = (status: ContentStatus): string => const formatStatusDescription = (status: ContentStatus): string =>
format(componentSetsTextsetStatusToWording[status].description); format(componentSetsTextsetStatusToWording[status].description);
const formatCategory = (slug: string, type: "default" | "full" = "default"): string => {
const category = typesTranslations.categories.find((cat) => cat.attributes?.slug === slug);
if (!category) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = category.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return type === "default" ? localeTranslation?.title : localeTranslation?.short;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
};
const formatContentType = (slug: string): string => {
const contentType = typesTranslations.contentTypes.find(
(type) => type.attributes?.slug === slug
);
if (!contentType) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = contentType.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return localeTranslation?.title;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
};
const formatWikiTag = (slug: string): string => {
const wikiTag = typesTranslations.wikiPagesTags.find((cat) => cat.attributes?.slug === slug);
if (!wikiTag) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = wikiTag.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return localeTranslation?.title;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
};
const formatWeaponType = (slug: string): string => {
const weaponType = typesTranslations.weaponTypes.find((type) => type.attributes?.slug === slug);
if (!weaponType) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = weaponType.attributes?.translations?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return localeTranslation?.name;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
};
return { return {
format, format,
formatLibraryItemType, formatLibraryItemType,
formatLibraryItemSubType,
formatStatusLabel, formatStatusLabel,
formatStatusDescription, formatStatusDescription,
formatCategory,
formatContentType,
formatWikiTag,
formatWeaponType,
}; };
}; };

View File

@ -1,6 +1,8 @@
import { import {
LocalDataGetCurrenciesQuery, LocalDataGetCurrenciesQuery,
LocalDataGetLanguagesQuery, LocalDataGetLanguagesQuery,
LocalDataGetRecordersQuery,
LocalDataGetTypesTranslationsQuery,
LocalDataGetWebsiteInterfacesQuery, LocalDataGetWebsiteInterfacesQuery,
} from "graphql/generated"; } from "graphql/generated";
@ -45,3 +47,58 @@ export const processLanguages = (languages: LocalDataGetLanguagesQuery | undefin
} }
return languages?.languages?.data ?? []; return languages?.languages?.data ?? [];
}; };
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export type Recorders = NonNullable<LocalDataGetRecordersQuery["recorders"]>["data"];
export const processRecorders = (recorders: LocalDataGetRecordersQuery | undefined): Recorders =>
recorders?.recorders?.data ?? [];
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export type TypesTranslations = {
audioSubtypes: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["audioSubtypes"]
>["data"];
gamePlatforms: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["gamePlatforms"]
>["data"];
groupSubtypes: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["groupSubtypes"]
>["data"];
metadataTypes: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["metadataTypes"]
>["data"];
textualSubtypes: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["textualSubtypes"]
>["data"];
videoSubtypes: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["videoSubtypes"]
>["data"];
contentTypes: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["contentTypes"]
>["data"];
wikiPagesTags: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["wikiPagesTags"]
>["data"];
weaponTypes: NonNullable<
NonNullable<LocalDataGetTypesTranslationsQuery>["weaponStoryTypes"]
>["data"];
categories: NonNullable<NonNullable<LocalDataGetTypesTranslationsQuery>["categories"]>["data"];
};
export const processTypesTranslations = (
data: LocalDataGetTypesTranslationsQuery | undefined
): TypesTranslations => ({
audioSubtypes: data?.audioSubtypes?.data ?? [],
categories: data?.categories?.data ?? [],
contentTypes: data?.contentTypes?.data ?? [],
gamePlatforms: data?.gamePlatforms?.data ?? [],
groupSubtypes: data?.groupSubtypes?.data ?? [],
metadataTypes: data?.metadataTypes?.data ?? [],
textualSubtypes: data?.textualSubtypes?.data ?? [],
videoSubtypes: data?.videoSubtypes?.data ?? [],
weaponTypes: data?.weaponStoryTypes?.data ?? [],
wikiPagesTags: data?.wikiPagesTags?.data ?? [],
});

View File

@ -1,11 +1,14 @@
import { IntlMessageFormat } from "intl-messageformat"; import { IntlMessageFormat } from "intl-messageformat";
import { useCallback } from "react"; import { useCallback } from "react";
import { useRouter } from "next/router";
import { atoms } from "contexts/atoms"; import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms"; import { useAtomGetter } from "helpers/atoms";
import { LibraryItemMetadataDynamicZone } from "graphql/generated"; import { LibraryItemMetadataDynamicZone } from "graphql/generated";
import { ICUParams } from "graphql/icuParams"; import { ICUParams } from "graphql/icuParams";
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { getLogger } from "helpers/logger"; import { getLogger } from "helpers/logger";
import { prettySlug } from "helpers/formatters";
import { LibraryItemMetadata } from "types/types";
const logger = getLogger("🗺️ [I18n]"); const logger = getLogger("🗺️ [I18n]");
@ -58,12 +61,19 @@ export const useFormat = (): {
key: K, key: K,
...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]] ...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]]
) => string; ) => string;
formatLibraryItemType: (metadata: { __typename: LibraryItemType }) => string; formatLibraryItemType: (metadata: LibraryItemMetadata) => string;
formatLibraryItemSubType: (metadata: LibraryItemMetadata) => string;
formatStatusLabel: (status: ContentStatus) => string; formatStatusLabel: (status: ContentStatus) => string;
formatStatusDescription: (status: ContentStatus) => string; formatStatusDescription: (status: ContentStatus) => string;
formatCategory: (slug: string, type?: "default" | "full") => string;
formatContentType: (slug: string) => string;
formatWikiTag: (slug: string) => string;
formatWeaponType: (slug: string) => string;
} => { } => {
const langui = useAtomGetter(atoms.localData.langui); const langui = useAtomGetter(atoms.localData.langui);
const fallbackLangui = useAtomGetter(atoms.localData.fallbackLangui); const fallbackLangui = useAtomGetter(atoms.localData.fallbackLangui);
const typesTranslations = useAtomGetter(atoms.localData.typesTranslations);
const { locale = "en" } = useRouter();
const format = useCallback( const format = useCallback(
( (
@ -96,12 +106,6 @@ Falling back to en translation.`
[langui, fallbackLangui] [langui, fallbackLangui]
); );
const formatLibraryItemType = useCallback(
(metadata: { __typename: LibraryItemType }): string =>
format(componentMetadataToWording[metadata.__typename]),
[format]
);
const formatStatusLabel = useCallback( const formatStatusLabel = useCallback(
(status: ContentStatus): string => format(componentSetsTextsetStatusToWording[status].label), (status: ContentStatus): string => format(componentSetsTextsetStatusToWording[status].label),
[format] [format]
@ -113,10 +117,178 @@ Falling back to en translation.`
[format] [format]
); );
const formatLibraryItemType = useCallback(
(metadata: LibraryItemMetadata): string =>
metadata ? format(componentMetadataToWording[metadata.__typename]) : format("other"),
[format]
);
const formatLibraryItemSubType = useCallback(
(metadata: LibraryItemMetadata): string => {
switch (metadata?.__typename) {
case "ComponentMetadataAudio": {
const slug = metadata.subtype?.data?.attributes?.slug;
const subtype = typesTranslations.audioSubtypes.find(
(type) => type.attributes?.slug === slug
);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataBooks": {
const slug = metadata.subtype?.data?.attributes?.slug;
const subtype = typesTranslations.textualSubtypes.find(
(type) => type.attributes?.slug === slug
);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataVideo": {
const slug = metadata.subtype?.data?.attributes?.slug;
const subtype = typesTranslations.videoSubtypes.find(
(type) => type.attributes?.slug === slug
);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataGame": {
const slug = metadata.platform?.data?.attributes?.slug;
const subtype = typesTranslations.gamePlatforms.find(
(type) => type.attributes?.slug === slug
);
const findTranslation = (givenLocale: string) =>
subtype?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
}
case "ComponentMetadataGroup": {
const subItemType = (() => {
const subitemTypeSlug = metadata.subitems_type?.data?.attributes?.slug;
const subItemTypeTranslations = typesTranslations.metadataTypes.find(
(type) => type.attributes?.slug === subitemTypeSlug
);
const findTranslation = (givenLocale: string) =>
subItemTypeTranslations?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
})();
const groupType = (() => {
const groupTypeSlug = metadata.subtype?.data?.attributes?.slug;
const groupTypeTranslations = typesTranslations.groupSubtypes.find(
(type) => type.attributes?.slug === groupTypeSlug
);
const findTranslation = (givenLocale: string) =>
groupTypeTranslations?.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
)?.title;
return findTranslation(locale) ?? findTranslation("en") ?? format("other");
})();
return `${groupType} - ${subItemType}`;
}
default:
return format("other");
}
},
[
format,
locale,
typesTranslations.audioSubtypes,
typesTranslations.gamePlatforms,
typesTranslations.groupSubtypes,
typesTranslations.metadataTypes,
typesTranslations.textualSubtypes,
typesTranslations.videoSubtypes,
]
);
const formatCategory = useCallback(
(slug: string, type: "default" | "full" = "default"): string => {
const category = typesTranslations.categories.find((cat) => cat.attributes?.slug === slug);
if (!category) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = category.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return type === "full" ? localeTranslation?.title : localeTranslation?.short;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
},
[locale, typesTranslations.categories]
);
const formatContentType = useCallback(
(slug: string): string => {
const contentType = typesTranslations.contentTypes.find(
(type) => type.attributes?.slug === slug
);
if (!contentType) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = contentType.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return localeTranslation?.title;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
},
[locale, typesTranslations.contentTypes]
);
const formatWikiTag = useCallback(
(slug: string): string => {
const wikiTag = typesTranslations.wikiPagesTags.find((cat) => cat.attributes?.slug === slug);
if (!wikiTag) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = wikiTag.attributes?.titles?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return localeTranslation?.title;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
},
[locale, typesTranslations.wikiPagesTags]
);
const formatWeaponType = useCallback(
(slug: string): string => {
const weaponType = typesTranslations.weaponTypes.find(
(type) => type.attributes?.slug === slug
);
if (!weaponType) return prettySlug(slug);
const findTranslation = (givenLocale: string): string | null | undefined => {
const localeTranslation = weaponType.attributes?.translations?.find(
(translation) => translation?.language?.data?.attributes?.code === givenLocale
);
return localeTranslation?.name;
};
return findTranslation(locale) ?? findTranslation("en") ?? prettySlug(slug);
},
[locale, typesTranslations.weaponTypes]
);
return { return {
format, format,
formatLibraryItemType, formatLibraryItemType,
formatLibraryItemSubType,
formatStatusLabel, formatStatusLabel,
formatStatusDescription, formatStatusDescription,
formatCategory,
formatContentType,
formatWikiTag,
formatWeaponType,
}; };
}; };

View File

@ -2,7 +2,13 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { i18n } from "../../../next.config"; import { i18n } from "../../../next.config";
import { cartesianProduct } from "helpers/others"; import { cartesianProduct } from "helpers/others";
import { filterHasAttributes } from "helpers/asserts"; import { filterHasAttributes } from "helpers/asserts";
import { fetchLocalData } from "graphql/fetchLocalData"; import {
fetchCurrencies,
fetchLanguages,
fetchRecorders,
fetchTypesTranslations,
fetchWebsiteInterfaces,
} from "graphql/fetchLocalData";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
type CRUDEvents = "entry.create" | "entry.delete" | "entry.update"; type CRUDEvents = "entry.create" | "entry.delete" | "entry.update";
@ -20,21 +26,32 @@ type StrapiRelationalFieldEntry = {
type RequestProps = type RequestProps =
| CustomRequest | CustomRequest
| StrapiAudioSubType
| StrapiCategory
| StrapiChronicle | StrapiChronicle
| StrapiChronicleChapter | StrapiChronicleChapter
| StrapiChronology | StrapiChronology
| StrapiContent | StrapiContent
| StrapiContentFolder | StrapiContentFolder
| StrapiContentType
| StrapiCurrency | StrapiCurrency
| StrapiGamePlatform
| StrapiGroupSubtypes
| StrapiLanguage | StrapiLanguage
| StrapiLibraryItem | StrapiLibraryItem
| StrapiMetadataType
| StrapiPostContent | StrapiPostContent
| StrapiRangedContent | StrapiRangedContent
| StrapiRecorder
| StrapiTextualSubtypes
| StrapiVideo | StrapiVideo
| StrapiVideoSubType
| StrapiWeaponGroup | StrapiWeaponGroup
| StrapiWeaponStory | StrapiWeaponStory
| StrapiWeaponStoryType
| StrapiWebsiteInterface | StrapiWebsiteInterface
| StrapiWiki; | StrapiWiki
| StrapiWikiPagesTag;
interface CustomRequest { interface CustomRequest {
model: "custom"; model: "custom";
@ -57,7 +74,6 @@ interface StrapiWeaponGroup extends StrapiEvent {
} }
interface StrapiRangedContent extends StrapiEvent { interface StrapiRangedContent extends StrapiEvent {
event: CRUDEvents;
model: "ranged-content"; model: "ranged-content";
entry: { entry: {
id: string; id: string;
@ -78,7 +94,6 @@ interface StrapiContent extends StrapiEvent {
} }
interface StrapiPostContent extends StrapiEvent { interface StrapiPostContent extends StrapiEvent {
event: CRUDEvents;
model: "post"; model: "post";
entry: { entry: {
slug: string; slug: string;
@ -86,31 +101,62 @@ interface StrapiPostContent extends StrapiEvent {
} }
interface StrapiWebsiteInterface extends StrapiEvent { interface StrapiWebsiteInterface extends StrapiEvent {
event: CRUDEvents;
model: "website-interface"; model: "website-interface";
entry: { }
slug: string;
}; interface StrapiRecorder extends StrapiEvent {
model: "recorder";
}
interface StrapiMetadataType extends StrapiEvent {
model: "metadata-type";
}
interface StrapiAudioSubType extends StrapiEvent {
model: "audio-subtype";
}
interface StrapiVideoSubType extends StrapiEvent {
model: "video-subtype";
}
interface StrapiTextualSubtypes extends StrapiEvent {
model: "textual-subtype";
}
interface StrapiGroupSubtypes extends StrapiEvent {
model: "group-subtype";
}
interface StrapiGamePlatform extends StrapiEvent {
model: "game-platform";
}
interface StrapiContentType extends StrapiEvent {
model: "content-type";
}
interface StrapiWikiPagesTag extends StrapiEvent {
model: "wiki-pages-tag";
}
interface StrapiWeaponStoryType extends StrapiEvent {
model: "weapon-story-type";
}
interface StrapiCategory extends StrapiEvent {
model: "category";
} }
interface StrapiLanguage extends StrapiEvent { interface StrapiLanguage extends StrapiEvent {
event: CRUDEvents;
model: "language"; model: "language";
entry: {
slug: string;
};
} }
interface StrapiCurrency extends StrapiEvent { interface StrapiCurrency extends StrapiEvent {
event: CRUDEvents;
model: "currency"; model: "currency";
entry: {
slug: string;
};
} }
interface StrapiLibraryItem extends StrapiEvent { interface StrapiLibraryItem extends StrapiEvent {
event: CRUDEvents;
model: "library-item"; model: "library-item";
entry: { entry: {
slug: string; slug: string;
@ -121,7 +167,6 @@ interface StrapiLibraryItem extends StrapiEvent {
} }
interface StrapiContentFolder extends StrapiEvent { interface StrapiContentFolder extends StrapiEvent {
event: CRUDEvents;
model: "contents-folder"; model: "contents-folder";
entry: { entry: {
slug: string; slug: string;
@ -132,12 +177,10 @@ interface StrapiContentFolder extends StrapiEvent {
} }
interface StrapiChronology extends StrapiEvent { interface StrapiChronology extends StrapiEvent {
event: CRUDEvents;
model: "chronology-era" | "chronology-item"; model: "chronology-era" | "chronology-item";
} }
interface StrapiWiki extends StrapiEvent { interface StrapiWiki extends StrapiEvent {
event: CRUDEvents;
model: "wiki-page"; model: "wiki-page";
entry: { entry: {
slug: string; slug: string;
@ -145,7 +188,6 @@ interface StrapiWiki extends StrapiEvent {
} }
interface StrapiChronicle extends StrapiEvent { interface StrapiChronicle extends StrapiEvent {
event: CRUDEvents;
model: "chronicle"; model: "chronicle";
entry: { entry: {
slug: string; slug: string;
@ -153,7 +195,6 @@ interface StrapiChronicle extends StrapiEvent {
} }
interface StrapiChronicleChapter extends StrapiEvent { interface StrapiChronicleChapter extends StrapiEvent {
event: CRUDEvents;
model: "chronicles-chapter"; model: "chronicles-chapter";
entry: { entry: {
chronicles: StrapiRelationalFieldEntry[]; chronicles: StrapiRelationalFieldEntry[];
@ -161,7 +202,6 @@ interface StrapiChronicleChapter extends StrapiEvent {
} }
interface StrapiVideo extends StrapiEvent { interface StrapiVideo extends StrapiEvent {
event: CRUDEvents;
model: "video"; model: "video";
entry: { entry: {
uid: string; uid: string;
@ -322,13 +362,6 @@ const Revalidate = async (
break; break;
} }
case "website-interface":
case "language":
case "currency": {
await fetchLocalData();
break;
}
case "video": { case "video": {
if (body.entry.uid) { if (body.entry.uid) {
paths.push(`/archives/videos/v/${body.entry.uid}`); paths.push(`/archives/videos/v/${body.entry.uid}`);
@ -351,6 +384,38 @@ const Revalidate = async (
break; break;
} }
case "recorder": {
await fetchRecorders();
break;
}
case "audio-subtype":
case "textual-subtype":
case "video-subtype":
case "group-subtype":
case "game-platform":
case "metadata-type":
case "content-type":
case "weapon-story-type":
case "category":
case "wiki-pages-tag": {
await fetchTypesTranslations();
break;
}
case "website-interface": {
await fetchWebsiteInterfaces();
break;
}
case "language": {
await fetchLanguages();
break;
}
case "currency": {
await fetchCurrencies();
break;
}
case "custom": { case "custom": {
paths.push(`${body.path}`); paths.push(`${body.path}`);
break; break;

View File

@ -34,7 +34,7 @@ interface Props extends AppLayoutRequired {
} }
const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => { const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => {
const { format } = useFormat(); const { format, formatContentType, formatCategory } = useFormat();
useScrollTopOnChange(Ids.ContentPanel, [chronicle.slug]); useScrollTopOnChange(Ids.ContentPanel, [chronicle.slug]);
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({ const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
@ -111,8 +111,14 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element =
<ContentLanguageSwitcher {...ContentLanguageSwitcherProps} /> <ContentLanguageSwitcher {...ContentLanguageSwitcherProps} />
) : undefined ) : undefined
} }
categories={primaryContent?.categories} categories={filterHasAttributes(primaryContent?.categories?.data, [
type={primaryContent?.type} "attributes",
]).map((category) => formatCategory(category.attributes.slug))}
type={
primaryContent?.type?.data?.attributes
? formatContentType(primaryContent.type.data.attributes.slug)
: undefined
}
description={selectedContentTranslation.description} description={selectedContentTranslation.description}
thumbnail={primaryContent?.thumbnail?.data?.attributes} thumbnail={primaryContent?.thumbnail?.data?.attributes}
/>, />,
@ -146,13 +152,10 @@ export default Chronicle;
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format, formatCategory, formatContentType } = getFormat(context.locale);
const slug = const slug =
context.params && isDefined(context.params.slug) ? context.params.slug.toString() : ""; context.params && isDefined(context.params.slug) ? context.params.slug.toString() : "";
const chronicle = await sdk.getChronicle({ const chronicle = await sdk.getChronicle({ slug: slug });
language_code: context.locale ?? "en",
slug: slug,
});
const chronicles = await sdk.getChroniclesChapters(); const chronicles = await sdk.getChroniclesChapters();
if ( if (
!chronicle.chronicles?.data[0]?.attributes?.translations || !chronicle.chronicles?.data[0]?.attributes?.translations ||
@ -178,13 +181,18 @@ export const getStaticProps: GetStaticProps = async (context) => {
description: getDescription(selectedContentTranslation.description, { description: getDescription(selectedContentTranslation.description, {
[format("type", { count: Infinity })]: [ [format("type", { count: Infinity })]: [
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.type?.data chronicle.chronicles.data[0].attributes.contents.data[0].attributes.type?.data
?.attributes?.titles?.[0]?.title, ?.attributes
? formatContentType(
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.type.data
.attributes.slug
)
: undefined,
], ],
[format("category", { count: Infinity })]: filterHasAttributes( [format("category", { count: Infinity })]: filterHasAttributes(
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.categories chronicle.chronicles.data[0].attributes.contents.data[0].attributes.categories
?.data, ?.data,
["attributes"] ["attributes"]
).map((category) => category.attributes.short), ).map((category) => formatCategory(category.attributes.slug)),
}), }),
}; };
} }

View File

@ -10,7 +10,7 @@ import { SubPanel } from "components/Containers/SubPanel";
import { PreviewCard, TranslatedPreviewCard } from "components/PreviewCard"; import { PreviewCard, TranslatedPreviewCard } from "components/PreviewCard";
import { ThumbnailHeader } from "components/ThumbnailHeader"; import { ThumbnailHeader } from "components/ThumbnailHeader";
import { getReadySdk } from "graphql/sdk"; import { getReadySdk } from "graphql/sdk";
import { prettyInlineTitle, prettyItemSubType, prettySlug } from "helpers/formatters"; import { prettyInlineTitle, prettySlug } from "helpers/formatters";
import { isUntangibleGroupItem } from "helpers/libraryItem"; import { isUntangibleGroupItem } from "helpers/libraryItem";
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { ContentWithTranslations } from "types/types"; import { ContentWithTranslations } from "types/types";
@ -47,7 +47,7 @@ interface Props extends AppLayoutRequired {
const Content = ({ content, ...otherProps }: Props): JSX.Element => { const Content = ({ content, ...otherProps }: Props): JSX.Element => {
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
const { format } = useFormat(); const { format, formatCategory, formatLibraryItemSubType, formatContentType } = useFormat();
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({ const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
items: content.translations, items: content.translations,
@ -196,12 +196,12 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
libraryItem.attributes.metadata && libraryItem.attributes.metadata &&
libraryItem.attributes.metadata.length > 0 && libraryItem.attributes.metadata.length > 0 &&
libraryItem.attributes.metadata[0] libraryItem.attributes.metadata[0]
? [prettyItemSubType(libraryItem.attributes.metadata[0])] ? [formatLibraryItemSubType(libraryItem.attributes.metadata[0])]
: [] : []
} }
bottomChips={filterHasAttributes(libraryItem.attributes.categories?.data, [ bottomChips={filterHasAttributes(libraryItem.attributes.categories?.data, [
"attributes", "attributes",
]).map((category) => category.attributes.short)} ]).map((category) => formatCategory(category.attributes.slug))}
metadata={{ metadata={{
releaseDate: libraryItem.attributes.release_date, releaseDate: libraryItem.attributes.release_date,
price: libraryItem.attributes.price, price: libraryItem.attributes.price,
@ -250,8 +250,14 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
title={selectedTranslation?.title} title={selectedTranslation?.title}
subtitle={selectedTranslation?.subtitle} subtitle={selectedTranslation?.subtitle}
description={selectedTranslation?.description} description={selectedTranslation?.description}
type={content.type} categories={filterHasAttributes(content.categories?.data, ["attributes"]).map(
categories={content.categories} (category) => formatCategory(category.attributes.slug)
)}
type={
content.type?.data?.attributes
? formatContentType(content.type.data.attributes.slug)
: undefined
}
languageSwitcher={ languageSwitcher={
languageSwitcherProps.locales.size > 1 ? ( languageSwitcherProps.locales.size > 1 ? (
<LanguageSwitcher {...languageSwitcherProps} /> <LanguageSwitcher {...languageSwitcherProps} />
@ -330,12 +336,9 @@ export default Content;
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format, formatCategory, formatContentType } = getFormat(context.locale);
const slug = context.params?.slug ? context.params.slug.toString() : ""; const slug = context.params?.slug ? context.params.slug.toString() : "";
const content = await sdk.getContentText({ const content = await sdk.getContentText({ slug: slug });
slug: slug,
language_code: context.locale ?? "en",
});
if (!content.contents?.data[0]?.attributes?.translations) { if (!content.contents?.data[0]?.attributes?.translations) {
return { notFound: true }; return { notFound: true };
@ -361,12 +364,14 @@ export const getStaticProps: GetStaticProps = async (context) => {
), ),
description: getDescription(rawDescription, { description: getDescription(rawDescription, {
[format("type", { count: Infinity })]: [ [format("type", { count: Infinity })]: [
content.contents.data[0].attributes.type?.data?.attributes?.titles?.[0]?.title, content.contents.data[0].attributes.type?.data?.attributes
? formatContentType(content.contents.data[0].attributes.type.data.attributes.slug)
: undefined,
], ],
[format("category", { count: Infinity })]: filterHasAttributes( [format("category", { count: Infinity })]: filterHasAttributes(
content.contents.data[0].attributes.categories?.data, content.contents.data[0].attributes.categories?.data,
["attributes"] ["attributes"]
).map((category) => category.attributes.short), ).map((category) => formatCategory(category.attributes.slug)),
}), }),
audio: audio:
selectedTranslation.language?.data?.attributes?.code && selectedTranslation.audio_set selectedTranslation.language?.data?.attributes?.code && selectedTranslation.audio_set
@ -470,6 +475,7 @@ const RelatedContentPreview = ({
categories, categories,
type, type,
}: RelatedContentPreviewFragment) => { }: RelatedContentPreviewFragment) => {
const { formatCategory, formatContentType } = useFormat();
const isContentPanelAtLeastXl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastXl); const isContentPanelAtLeastXl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastXl);
return ( return (
@ -486,16 +492,10 @@ const RelatedContentPreview = ({
)} )}
fallback={{ title: slug }} fallback={{ title: slug }}
thumbnail={thumbnail?.data?.attributes} thumbnail={thumbnail?.data?.attributes}
topChips={ topChips={type?.data?.attributes ? [formatContentType(type.data.attributes.slug)] : undefined}
type?.data?.attributes bottomChips={filterHasAttributes(categories?.data, ["attributes"]).map((category) =>
? [ formatCategory(category.attributes.slug)
type.data.attributes.titles?.[0] )}
? type.data.attributes.titles[0]?.title
: prettySlug(type.data.attributes.slug),
]
: undefined
}
bottomChips={categories?.data.map((category) => category.attributes?.short ?? "")}
keepInfoVisible keepInfoVisible
/> />
); );

View File

@ -59,7 +59,7 @@ interface Props extends AppLayoutRequired {}
const Contents = (props: Props): JSX.Element => { const Contents = (props: Props): JSX.Element => {
const hoverable = useDeviceSupportsHover(); const hoverable = useDeviceSupportsHover();
const { format } = useFormat(); const { format, formatCategory, formatContentType } = useFormat();
const router = useTypedRouter(queryParamSchema); const router = useTypedRouter(queryParamSchema);
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
@ -225,15 +225,11 @@ const Contents = (props: Props): JSX.Element => {
thumbnailForceAspectRatio thumbnailForceAspectRatio
topChips={ topChips={
item.type?.data?.attributes item.type?.data?.attributes
? [ ? [formatContentType(item.type.data.attributes.slug)]
item.type.data.attributes.titles?.[0]
? item.type.data.attributes.titles[0]?.title
: prettySlug(item.type.data.attributes.slug),
]
: undefined : undefined
} }
bottomChips={item.categories?.data.map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes?.short ?? "" (category) => formatCategory(category.attributes.slug)
)} )}
keepInfoVisible={keepInfoVisible} keepInfoVisible={keepInfoVisible}
/> />

View File

@ -35,7 +35,7 @@ interface Props extends AppLayoutRequired {
} }
const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.Element => { const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.Element => {
const { format } = useFormat(); const { format, formatCategory, formatContentType } = useFormat();
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl); const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
@ -158,16 +158,12 @@ const ContentsFolder = ({ openGraph, folder, path, ...otherProps }: Props): JSX.
thumbnailForceAspectRatio thumbnailForceAspectRatio
topChips={ topChips={
item.attributes.type?.data?.attributes item.attributes.type?.data?.attributes
? [ ? [formatContentType(item.attributes.type.data.attributes.slug)]
item.attributes.type.data.attributes.titles?.[0]
? item.attributes.type.data.attributes.titles[0]?.title
: prettySlug(item.attributes.type.data.attributes.slug),
]
: undefined : undefined
} }
bottomChips={item.attributes.categories?.data.map( bottomChips={filterHasAttributes(item.attributes.categories?.data, [
(category) => category.attributes?.short ?? "" "attributes",
)} ]).map((category) => formatCategory(category.attributes.slug))}
keepInfoVisible keepInfoVisible
/> />
))} ))}
@ -201,10 +197,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format } = getFormat(context.locale);
const slug = context.params?.slug ? context.params.slug.toString() : ""; const slug = context.params?.slug ? context.params.slug.toString() : "";
const contentsFolder = await sdk.getContentsFolder({ const contentsFolder = await sdk.getContentsFolder({ slug: slug });
slug: slug,
language_code: context.locale ?? "en",
});
if (!contentsFolder.contentsFolders?.data[0]?.attributes) { if (!contentsFolder.contentsFolders?.data[0]?.attributes) {
return { notFound: true }; return { notFound: true };
} }

View File

@ -64,10 +64,7 @@ export default Home;
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format } = getFormat(context.locale);
const post = await sdk.getPost({ const post = await sdk.getPost({ slug: "home" });
slug: "home",
language_code: context.locale ?? "en",
});
if (post.posts?.data && post.posts.data.length > 0) { if (post.posts?.data && post.posts.data.length > 0) {
const props: PostStaticProps = { const props: PostStaticProps = {
post: post.posts.data[0]?.attributes as PostWithTranslations, post: post.posts.data[0]?.attributes as PostWithTranslations,

View File

@ -23,7 +23,6 @@ import { getReadySdk } from "graphql/sdk";
import { import {
prettyDate, prettyDate,
prettyInlineTitle, prettyInlineTitle,
prettyItemSubType,
prettyPrice, prettyPrice,
prettySlug, prettySlug,
prettyURL, prettyURL,
@ -91,7 +90,13 @@ const LibrarySlug = ({
}: Props): JSX.Element => { }: Props): JSX.Element => {
const currency = useAtomGetter(atoms.settings.currency); const currency = useAtomGetter(atoms.settings.currency);
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled); const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const { format, formatLibraryItemType } = useFormat(); const {
format,
formatLibraryItemType,
formatCategory,
formatContentType,
formatLibraryItemSubType,
} = useFormat();
const currencies = useAtomGetter(atoms.localData.currencies); const currencies = useAtomGetter(atoms.localData.currencies);
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl); const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
@ -307,7 +312,7 @@ const LibrarySlug = ({
<div className="grid grid-flow-col gap-1"> <div className="grid grid-flow-col gap-1">
<Chip text={formatLibraryItemType(item.metadata[0])} /> <Chip text={formatLibraryItemType(item.metadata[0])} />
{""} {""}
<Chip text={prettyItemSubType(item.metadata[0])} /> <Chip text={formatLibraryItemSubType(item.metadata[0])} />
</div> </div>
</div> </div>
)} )}
@ -347,7 +352,10 @@ const LibrarySlug = ({
</h3> </h3>
<div className="flex flex-row flex-wrap place-content-center gap-2"> <div className="flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(item.categories.data, ["attributes"]).map((category) => ( {filterHasAttributes(item.categories.data, ["attributes"]).map((category) => (
<Chip key={category.id} text={category.attributes.name} /> <Chip
key={category.attributes.slug}
text={formatCategory(category.attributes.slug, "full")}
/>
))} ))}
</div> </div>
</div> </div>
@ -506,12 +514,12 @@ const LibrarySlug = ({
subitem.attributes.metadata && subitem.attributes.metadata &&
subitem.attributes.metadata.length > 0 && subitem.attributes.metadata.length > 0 &&
subitem.attributes.metadata[0] subitem.attributes.metadata[0]
? [prettyItemSubType(subitem.attributes.metadata[0])] ? [formatLibraryItemSubType(subitem.attributes.metadata[0])]
: [] : []
} }
bottomChips={subitem.attributes.categories?.data.map( bottomChips={filterHasAttributes(subitem.attributes.categories?.data, [
(category) => category.attributes?.short ?? "" "attributes",
)} ]).map((category) => formatCategory(category.attributes.slug))}
metadata={{ metadata={{
releaseDate: subitem.attributes.release_date, releaseDate: subitem.attributes.release_date,
price: subitem.attributes.price, price: subitem.attributes.price,
@ -573,14 +581,14 @@ const LibrarySlug = ({
categories: filterHasAttributes( categories: filterHasAttributes(
rangedContent.attributes.content.data.attributes.categories?.data, rangedContent.attributes.content.data.attributes.categories?.data,
["attributes"] ["attributes"]
).map((category) => category.attributes.short), ).map((category) => formatCategory(category.attributes.slug)),
type: type: rangedContent.attributes.content.data.attributes.type?.data
rangedContent.attributes.content.data.attributes.type?.data ?.attributes
?.attributes?.titles?.[0]?.title ?? ? formatContentType(
prettySlug( rangedContent.attributes.content.data.attributes.type.data
rangedContent.attributes.content.data.attributes.type?.data .attributes.slug
?.attributes?.slug )
), : undefined,
slug: rangedContent.attributes.content.data.attributes.slug, slug: rangedContent.attributes.content.data.attributes.slug,
} }
: undefined : undefined
@ -619,10 +627,9 @@ export default LibrarySlug;
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format, formatCategory, formatLibraryItemSubType } = getFormat(context.locale);
const item = await sdk.getLibraryItem({ const item = await sdk.getLibraryItem({
slug: context.params && isDefined(context.params.slug) ? context.params.slug.toString() : "", slug: context.params && isDefined(context.params.slug) ? context.params.slug.toString() : "",
language_code: context.locale ?? "en",
}); });
if (!item.libraryItems?.data[0]?.attributes) return { notFound: true }; if (!item.libraryItems?.data[0]?.attributes) return { notFound: true };
sortRangedContent(item.libraryItems.data[0].attributes.contents); sortRangedContent(item.libraryItems.data[0].attributes.contents);
@ -634,10 +641,10 @@ export const getStaticProps: GetStaticProps = async (context) => {
{ {
[format("category", { count: Infinity })]: filterHasAttributes( [format("category", { count: Infinity })]: filterHasAttributes(
item.libraryItems.data[0].attributes.categories?.data, item.libraryItems.data[0].attributes.categories?.data,
["attributes.short"] ["attributes"]
).map((category) => category.attributes.short), ).map((category) => formatCategory(category.attributes.slug)),
[format("type", { count: Infinity })]: item.libraryItems.data[0].attributes.metadata?.[0] [format("type", { count: Infinity })]: item.libraryItems.data[0].attributes.metadata?.[0]
? [prettyItemSubType(item.libraryItems.data[0].attributes.metadata[0])] ? [formatLibraryItemSubType(item.libraryItems.data[0].attributes.metadata[0])]
: [], : [],
[format("release_date")]: [ [format("release_date")]: [
item.libraryItems.data[0].attributes.release_date item.libraryItems.data[0].attributes.release_date

View File

@ -593,7 +593,6 @@ export const getStaticProps: GetStaticProps = async (context) => {
const { format } = getFormat(context.locale); const { format } = getFormat(context.locale);
const item = await sdk.getLibraryItemScans({ const item = await sdk.getLibraryItemScans({
slug: context.params && isDefined(context.params.slug) ? context.params.slug.toString() : "", slug: context.params && isDefined(context.params.slug) ? context.params.slug.toString() : "",
language_code: context.locale ?? "en",
}); });
if (!item.libraryItems?.data[0]?.attributes || !item.libraryItems.data[0]?.id) if (!item.libraryItems?.data[0]?.attributes || !item.libraryItems.data[0]?.id)
return { notFound: true }; return { notFound: true };
@ -891,7 +890,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
{filterHasAttributes(selectedScan.scanners.data, ["id", "attributes"]).map( {filterHasAttributes(selectedScan.scanners.data, ["id", "attributes"]).map(
(scanner) => ( (scanner) => (
<Fragment key={scanner.id}> <Fragment key={scanner.id}>
<RecorderChip recorder={scanner.attributes} /> <RecorderChip username={scanner.attributes.username} />
</Fragment> </Fragment>
) )
)} )}
@ -906,7 +905,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
{filterHasAttributes(selectedScan.cleaners.data, ["id", "attributes"]).map( {filterHasAttributes(selectedScan.cleaners.data, ["id", "attributes"]).map(
(cleaner) => ( (cleaner) => (
<Fragment key={cleaner.id}> <Fragment key={cleaner.id}>
<RecorderChip recorder={cleaner.attributes} /> <RecorderChip username={cleaner.attributes.username} />
</Fragment> </Fragment>
) )
)} )}
@ -921,7 +920,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
{filterHasAttributes(selectedScan.typesetters.data, ["id", "attributes"]).map( {filterHasAttributes(selectedScan.typesetters.data, ["id", "attributes"]).map(
(typesetter) => ( (typesetter) => (
<Fragment key={typesetter.id}> <Fragment key={typesetter.id}>
<RecorderChip recorder={typesetter.attributes} /> <RecorderChip username={typesetter.attributes.username} />
</Fragment> </Fragment>
) )
)} )}

View File

@ -27,7 +27,6 @@ import {
import { MeiliIndices, MeiliLibraryItem } from "shared/meilisearch-graphql-typings/meiliTypes"; import { MeiliIndices, MeiliLibraryItem } from "shared/meilisearch-graphql-typings/meiliTypes";
import { useTypedRouter } from "hooks/useTypedRouter"; import { useTypedRouter } from "hooks/useTypedRouter";
import { TranslatedPreviewCard } from "components/PreviewCard"; import { TranslatedPreviewCard } from "components/PreviewCard";
import { prettyItemSubType } from "helpers/formatters";
import { isUntangibleGroupItem } from "helpers/libraryItem"; import { isUntangibleGroupItem } from "helpers/libraryItem";
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus"; import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
@ -70,7 +69,7 @@ interface Props extends AppLayoutRequired {}
const Library = (props: Props): JSX.Element => { const Library = (props: Props): JSX.Element => {
const hoverable = useDeviceSupportsHover(); const hoverable = useDeviceSupportsHover();
const { format } = useFormat(); const { format, formatCategory, formatLibraryItemSubType } = useFormat();
const { libraryItemUserStatus } = useLibraryItemUserStatus(); const { libraryItemUserStatus } = useLibraryItemUserStatus();
const sortingMethods = useMemo( const sortingMethods = useMemo(
@ -414,11 +413,11 @@ const Library = (props: Props): JSX.Element => {
keepInfoVisible={keepInfoVisible} keepInfoVisible={keepInfoVisible}
topChips={ topChips={
item.metadata && item.metadata.length > 0 && item.metadata[0] item.metadata && item.metadata.length > 0 && item.metadata[0]
? [prettyItemSubType(item.metadata[0])] ? [formatLibraryItemSubType(item.metadata[0])]
: [] : undefined
} }
bottomChips={item.categories?.data.map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes?.short ?? "" (category) => formatCategory(category.attributes.slug)
)} )}
metadata={{ metadata={{
releaseDate: item.release_date, releaseDate: item.release_date,

View File

@ -56,7 +56,7 @@ const queryParamSchema = z.object({
interface Props extends AppLayoutRequired {} interface Props extends AppLayoutRequired {}
const News = ({ ...otherProps }: Props): JSX.Element => { const News = ({ ...otherProps }: Props): JSX.Element => {
const { format } = useFormat(); const { format, formatCategory } = useFormat();
const hoverable = useDeviceSupportsHover(); const hoverable = useDeviceSupportsHover();
const router = useTypedRouter(queryParamSchema); const router = useTypedRouter(queryParamSchema);
@ -180,8 +180,8 @@ const News = ({ ...otherProps }: Props): JSX.Element => {
thumbnailAspectRatio="3/2" thumbnailAspectRatio="3/2"
thumbnailForceAspectRatio thumbnailForceAspectRatio
keepInfoVisible={keepInfoVisible} keepInfoVisible={keepInfoVisible}
bottomChips={item.categories?.data.map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes?.short ?? "" (category) => formatCategory(category.attributes.slug)
)} )}
metadata={{ metadata={{
releaseDate: item.date, releaseDate: item.date,

View File

@ -38,7 +38,7 @@ interface Props extends AppLayoutRequired {
} }
const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => { const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
const { format } = useFormat(); const { format, formatCategory, formatWikiTag } = useFormat();
const router = useRouter(); const router = useRouter();
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode); const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
@ -126,7 +126,10 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
<div className="flex flex-row flex-wrap place-content-center gap-2"> <div className="flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(page.categories.data, ["attributes"]).map((category) => ( {filterHasAttributes(page.categories.data, ["attributes"]).map((category) => (
<Chip key={category.id} text={category.attributes.name} /> <Chip
key={category.attributes.slug}
text={formatCategory(category.attributes.slug, "full")}
/>
))} ))}
</div> </div>
</> </>
@ -137,12 +140,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
<p className="font-headers text-xl font-bold">{format("tags")}</p> <p className="font-headers text-xl font-bold">{format("tags")}</p>
<div className="flex flex-row flex-wrap place-content-center gap-2"> <div className="flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(page.tags.data, ["attributes"]).map((tag) => ( {filterHasAttributes(page.tags.data, ["attributes"]).map((tag) => (
<Chip <Chip key={tag.attributes.slug} text={formatWikiTag(tag.attributes.slug)} />
key={tag.id}
text={
tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug)
}
/>
))} ))}
</div> </div>
</> </>
@ -180,7 +178,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
}))} }))}
index={index + 1} index={index + 1}
categories={filterHasAttributes(definition.categories?.data, ["attributes"]).map( categories={filterHasAttributes(definition.categories?.data, ["attributes"]).map(
(category) => category.attributes.short (category) => formatCategory(category.attributes.slug)
)} )}
/> />
</div> </div>
@ -250,24 +248,21 @@ export default WikiPage;
export const getStaticProps: GetStaticProps = async (context) => { export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format, formatCategory, formatWikiTag } = getFormat(context.locale);
const slug = const slug =
context.params && isDefined(context.params.slug) ? context.params.slug.toString() : ""; context.params && isDefined(context.params.slug) ? context.params.slug.toString() : "";
const page = await sdk.getWikiPage({ const page = await sdk.getWikiPage({ slug: slug });
language_code: context.locale ?? "en",
slug: slug,
});
if (!page.wikiPages?.data[0]?.attributes?.translations) return { notFound: true }; if (!page.wikiPages?.data[0]?.attributes?.translations) return { notFound: true };
const { title, description } = (() => { const { title, description } = (() => {
const chipsGroups = { const chipsGroups = {
[format("tags")]: filterHasAttributes(page.wikiPages.data[0].attributes.tags?.data, [ [format("tags")]: filterHasAttributes(page.wikiPages.data[0].attributes.tags?.data, [
"attributes", "attributes",
]).map((tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug)), ]).map((tag) => formatWikiTag(tag.attributes.slug)),
[format("category", { count: Infinity })]: filterHasAttributes( [format("category", { count: Infinity })]: filterHasAttributes(
page.wikiPages.data[0].attributes.categories?.data, page.wikiPages.data[0].attributes.categories?.data,
["attributes"] ["attributes"]
).map((category) => category.attributes.short), ).map((category) => formatCategory(category.attributes.slug)),
}; };
if (context.locale && context.locales) { if (context.locale && context.locales) {

View File

@ -51,10 +51,10 @@ const queryParamSchema = z.object({
interface Props extends AppLayoutRequired {} interface Props extends AppLayoutRequired {}
const Wiki = (props: Props): JSX.Element => { const Wiki = (props: Props): JSX.Element => {
const { format, formatCategory, formatWikiTag } = useFormat();
const hoverable = useDeviceSupportsHover(); const hoverable = useDeviceSupportsHover();
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened); const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]); const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]);
const { format } = useFormat();
const router = useTypedRouter(queryParamSchema); const router = useTypedRouter(queryParamSchema);
const [query, setQuery] = useState(router.query.query ?? DEFAULT_FILTERS_STATE.query); const [query, setQuery] = useState(router.query.query ?? DEFAULT_FILTERS_STATE.query);
@ -201,11 +201,11 @@ const Wiki = (props: Props): JSX.Element => {
thumbnailRounded thumbnailRounded
thumbnailForceAspectRatio thumbnailForceAspectRatio
keepInfoVisible keepInfoVisible
topChips={filterHasAttributes(item.tags?.data, ["attributes"]).map( topChips={filterHasAttributes(item.tags?.data, ["attributes"]).map((tag) =>
(tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug) formatWikiTag(tag.attributes.slug)
)} )}
bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map( bottomChips={filterHasAttributes(item.categories?.data, ["attributes"]).map(
(category) => category.attributes.short (category) => formatCategory(category.attributes.slug)
)} )}
/> />
))} ))}

View File

@ -64,7 +64,7 @@ const WeaponPreview = ({ weapon }: WeaponPreviewProps): JSX.Element => (
); );
const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX.Element => { const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX.Element => {
const { format } = useFormat(); const { format, formatCategory, formatWeaponType } = useFormat();
const intersectionIds = useMemo( const intersectionIds = useMemo(
() => filterDefined(weapon.stories).map(({ id }) => `story-${id}`), () => filterDefined(weapon.stories).map(({ id }) => `story-${id}`),
@ -91,8 +91,10 @@ const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX
key={index} key={index}
url={`#${id}`} url={`#${id}`}
title={`Story ${index + 1}`} title={`Story ${index + 1}`}
subtitle={weapon.stories?.[index]?.categories?.data subtitle={filterHasAttributes(weapon.stories?.[index]?.categories?.data, [
.map((category) => category.attributes?.name) "attributes",
])
.map((category) => formatCategory(category.attributes.slug))
.join("・")} .join("・")}
active={currentIntersection === index} active={currentIntersection === index}
border border
@ -133,6 +135,11 @@ const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX
<ThumbnailHeader <ThumbnailHeader
title={primaryName} title={primaryName}
subtitle={aliases.join("・")} subtitle={aliases.join("・")}
type={
weapon.type?.data?.attributes
? formatWeaponType(weapon.type.data.attributes.slug)
: undefined
}
thumbnail={weapon.thumbnail?.data?.attributes} thumbnail={weapon.thumbnail?.data?.attributes}
/> />
@ -166,10 +173,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk(); const sdk = getReadySdk();
const { format } = getFormat(context.locale); const { format } = getFormat(context.locale);
const slug = context.params?.slug ? context.params.slug.toString() : ""; const slug = context.params?.slug ? context.params.slug.toString() : "";
const weaponResp = await sdk.getWeapon({ const weaponResp = await sdk.getWeapon({ slug: slug });
slug: slug,
language_code: context.locale ?? "en",
});
const weapon = weaponResp.weaponStories?.data[0]?.attributes; const weapon = weaponResp.weaponStories?.data[0]?.attributes;
@ -227,7 +231,7 @@ interface WeaponStoryProps {
} }
const WeaponStory = ({ story, storyNumber, id }: WeaponStoryProps): JSX.Element => { const WeaponStory = ({ story, storyNumber, id }: WeaponStoryProps): JSX.Element => {
const { format } = useFormat(); const { format, formatCategory } = useFormat();
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({ const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
items: story.translations, items: story.translations,
languageExtractor: useCallback( languageExtractor: useCallback(
@ -241,14 +245,18 @@ const WeaponStory = ({ story, storyNumber, id }: WeaponStoryProps): JSX.Element
return ( return (
<InsetBox id={id} className="formatted"> <InsetBox id={id} className="formatted">
<div className="flex place-content-center place-items-center gap-4">
<h2 className="!mb-4 !mt-4">{format("story_x", { x: storyNumber })}</h2> <h2 className="!mb-4 !mt-4">{format("story_x", { x: storyNumber })}</h2>
{languageSwitcherProps.locales.size > 1 && <LanguageSwitcher {...languageSwitcherProps} />} {languageSwitcherProps.locales.size > 1 && <LanguageSwitcher {...languageSwitcherProps} />}
</div>
{story.categories && story.categories.data.length > 0 && ( {story.categories && story.categories.data.length > 0 && (
<div className="mb-12 flex flex-row flex-wrap place-content-center gap-2"> <div className="mb-12 flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(story.categories.data, ["attributes.name"]).map((category) => ( {filterHasAttributes(story.categories.data, ["attributes"]).map((category) => (
<Chip key={category.id} text={category.attributes.name} /> <Chip
key={category.attributes.slug}
text={formatCategory(category.attributes.slug, "full")}
/>
))} ))}
</div> </div>
)} )}

View File

@ -54,7 +54,7 @@ const queryParamSchema = z.object({
interface Props extends AppLayoutRequired {} interface Props extends AppLayoutRequired {}
const Weapons = (props: Props): JSX.Element => { const Weapons = (props: Props): JSX.Element => {
const { format } = useFormat(); const { format, formatCategory, formatWeaponType } = useFormat();
const hoverable = useDeviceSupportsHover(); const hoverable = useDeviceSupportsHover();
const router = useTypedRouter(queryParamSchema); const router = useTypedRouter(queryParamSchema);
@ -190,11 +190,11 @@ const Weapons = (props: Props): JSX.Element => {
keepInfoVisible={keepInfoVisible} keepInfoVisible={keepInfoVisible}
topChips={ topChips={
item.type?.data?.attributes?.slug item.type?.data?.attributes?.slug
? [prettySlug(item.type.data.attributes.slug)] ? [formatWeaponType(item.type.data.attributes.slug)]
: undefined : undefined
} }
bottomChips={filterHasAttributes(item.categories, ["attributes.short"]).map( bottomChips={filterHasAttributes(item.categories, ["attributes"]).map((category) =>
(category) => category.attributes.short formatCategory(category.attributes.slug)
)} )}
/> />
))} ))}
@ -203,11 +203,7 @@ const Weapons = (props: Props): JSX.Element => {
</ContentPanel> </ContentPanel>
); );
return ( return <AppLayout contentPanel={contentPanel} subPanel={subPanel} {...props} />;
<>
<AppLayout contentPanel={contentPanel} subPanel={subPanel} {...props} />
</>
);
}; };
export default Weapons; export default Weapons;

File diff suppressed because it is too large Load Diff

View File

@ -73,3 +73,39 @@ export enum LibraryItemUserStatus {
Want = 1, Want = 1,
Have = 2, Have = 2,
} }
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
type LibraryItemMetadataSubType = {
data?: {
attributes?: {
slug: string;
} | null;
} | null;
} | null;
export type LibraryItemMetadata =
| {
__typename: "ComponentMetadataAudio";
subtype?: LibraryItemMetadataSubType;
}
| {
__typename: "ComponentMetadataBooks";
subtype?: LibraryItemMetadataSubType;
}
| {
__typename: "ComponentMetadataGame";
platform?: LibraryItemMetadataSubType;
}
| {
__typename: "ComponentMetadataGroup";
subtype?: LibraryItemMetadataSubType;
subitems_type?: LibraryItemMetadataSubType;
}
| {
__typename: "ComponentMetadataVideo";
subtype?: LibraryItemMetadataSubType;
}
| { __typename: "ComponentMetadataOther" }
| { __typename: "Error" }
| null;