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

View File

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

View File

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

View File

@ -3,10 +3,12 @@ import { Img } from "./Img";
import { Markdawn } from "./Markdown/Markdawn";
import { ToolTip } from "./ToolTip";
import { Chip } from "components/Chip";
import { RecorderChipFragment } from "graphql/generated";
import { ImageQuality } from "helpers/img";
import { filterHasAttributes } from "helpers/asserts";
import { filterHasAttributes, isUndefined } from "helpers/asserts";
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 {
className?: string;
recorder: RecorderChipFragment;
username: string;
}
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
export const RecorderChip = ({ recorder }: Props): JSX.Element => {
export const RecorderChip = ({ username }: Props): JSX.Element => {
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 (
<ToolTip
@ -55,7 +65,7 @@ export const RecorderChip = ({ recorder }: Props): JSX.Element => {
)}
</div>
</div>
{recorder.bio?.[0] && <Markdawn text={recorder.bio[0].bio ?? ""} />}
{selectedBioTranslation?.bio && <Markdawn text={selectedBioTranslation.bio} />}
</div>
}
placement="top">

View File

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

View File

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

View File

@ -7,10 +7,17 @@ import {
LocalDataGetWebsiteInterfacesQuery,
LocalDataGetCurrenciesQuery,
LocalDataGetLanguagesQuery,
LocalDataGetRecordersQuery,
} from "graphql/generated";
import { LocalDataFile } from "graphql/fetchLocalData";
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";
const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`;
@ -21,6 +28,8 @@ export const useLocalData = (): void => {
const setCurrencies = useAtomSetter(internalAtoms.localData.currencies);
const setLangui = useAtomSetter(internalAtoms.localData.langui);
const setFallbackLangui = useAtomSetter(internalAtoms.localData.fallbackLangui);
const setRecorders = useAtomSetter(internalAtoms.localData.recorders);
const setTypesTranslations = useAtomSetter(internalAtoms.localData.typesTranslations);
const { locale } = useRouter();
const { data: rawLanguages } = useFetch<LocalDataGetLanguagesQuery>(getFileName("languages"));
@ -28,6 +37,10 @@ export const useLocalData = (): void => {
const { data: rawLangui } = useFetch<LocalDataGetWebsiteInterfacesQuery>(
getFileName("websiteInterfaces")
);
const { data: rawRecorders } = useFetch<LocalDataGetRecordersQuery>(getFileName("recorders"));
const { data: rawTypesTranslations } = useFetch<LocalDataGetRecordersQuery>(
getFileName("typesTranslations")
);
useEffect(() => {
logger.log("Refresh languages");
@ -48,4 +61,14 @@ export const useLocalData = (): void => {
logger.log("Refresh fallback langui");
setFallbackLangui(processLangui(rawLangui, "en"));
}, [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 { config } from "dotenv";
import { getReadySdk } from "./sdk";
import { LocalDataGetWebsiteInterfacesQuery } from "./generated";
import { processLangui, Langui } from "helpers/localData";
import {
LocalDataGetTypesTranslationsQuery,
LocalDataGetWebsiteInterfacesQuery,
} from "./generated";
import {
processLangui,
Langui,
TypesTranslations,
processTypesTranslations,
} from "helpers/localData";
import { getLogger } from "helpers/logger";
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 logger = getLogger("💽 [Local Data]", "server");
const writeLocalData = (name: LocalDataFile, localData: unknown) => {
const writeLocalData = (name: LocalDataFile, localData: object) => {
const path = `${LOCAL_DATA_FOLDER}/${name}.json`;
writeFileSync(path, JSON.stringify(localData), { encoding: "utf-8" });
logger.log(`${name}.json has been written`);
@ -23,22 +31,58 @@ const readLocalData = <T>(name: LocalDataFile): T => {
return JSON.parse(readFileSync(path, { encoding: "utf8" }));
};
export const fetchLocalData = async (): Promise<void> => {
export const fetchWebsiteInterfaces = async (): Promise<void> => {
const sdk = getReadySdk();
writeLocalData("websiteInterfaces", await sdk.localDataGetWebsiteInterfaces());
};
export const fetchCurrencies = async (): Promise<void> => {
const sdk = getReadySdk();
writeLocalData("currencies", await sdk.localDataGetCurrencies());
};
export const fetchLanguages = async (): Promise<void> => {
const sdk = getReadySdk();
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") {
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");
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 }) {
data {
id
attributes {
short
slug
}
}
}
@ -24,9 +23,6 @@ fragment relatedContentPreview on Content {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}

View File

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

View File

@ -1,4 +1,4 @@
query getChronicle($slug: String, $language_code: String) {
query getChronicle($slug: String) {
chronicles(filters: { slug: { eq: $slug } }) {
data {
attributes {
@ -53,21 +53,21 @@ query getChronicle($slug: String, $language_code: String) {
authors {
data {
attributes {
...recorderChip
username
}
}
}
translators {
data {
attributes {
...recorderChip
username
}
}
}
proofreaders {
data {
attributes {
...recorderChip
username
}
}
}
@ -80,10 +80,8 @@ query getChronicle($slug: String, $language_code: String) {
slug
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -91,9 +89,6 @@ query getChronicle($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -123,7 +118,7 @@ query getChronicle($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -131,7 +126,7 @@ query getChronicle($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -139,7 +134,7 @@ query getChronicle($slug: String, $language_code: String) {
data {
id
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 } }) {
data {
id
@ -6,10 +6,8 @@ query getContentText($slug: String, $language_code: String) {
slug
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -17,9 +15,6 @@ query getContentText($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -53,10 +48,8 @@ query getContentText($slug: String, $language_code: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -67,19 +60,15 @@ query getContentText($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
}
... on ComponentMetadataGame {
platforms(pagination: { limit: -1 }) {
platform {
data {
id
attributes {
short
slug
}
}
}
@ -89,9 +78,6 @@ query getContentText($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -101,9 +87,6 @@ query getContentText($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -113,9 +96,6 @@ query getContentText($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -123,9 +103,6 @@ query getContentText($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -163,7 +140,7 @@ query getContentText($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -171,7 +148,7 @@ query getContentText($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -179,7 +156,7 @@ query getContentText($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -198,7 +175,7 @@ query getContentText($slug: String, $language_code: String) {
subbers(pagination: { limit: -1 }) {
data {
attributes {
...recorderChip
username
}
}
}
@ -216,7 +193,7 @@ query getContentText($slug: String, $language_code: String) {
dubbers(pagination: { limit: -1 }) {
data {
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 } }) {
data {
attributes {
@ -22,10 +22,8 @@ query getContentsFolder($slug: String, $language_code: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -33,9 +31,6 @@ query getContentsFolder($slug: String, $language_code: String) {
data {
attributes {
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 } }) {
data {
id
@ -33,10 +33,8 @@ query getLibraryItem($slug: String, $language_code: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -58,9 +56,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -81,19 +76,15 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
}
... on ComponentMetadataGame {
platforms(pagination: { limit: -1 }) {
platform {
data {
id
attributes {
short
slug
}
}
}
@ -127,9 +118,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -144,9 +132,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -154,9 +139,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -194,10 +176,8 @@ query getLibraryItem($slug: String, $language_code: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -208,19 +188,16 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
}
... on ComponentMetadataGame {
platforms {
platform {
data {
id
attributes {
short
slug
}
}
}
@ -230,9 +207,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -242,9 +216,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -254,9 +225,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -264,9 +232,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -317,10 +282,8 @@ query getLibraryItem($slug: String, $language_code: String) {
slug
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -328,9 +291,6 @@ query getLibraryItem($slug: String, $language_code: String) {
data {
attributes {
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 } }) {
data {
id
@ -27,7 +27,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -35,7 +35,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -43,7 +43,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -156,10 +156,8 @@ query getLibraryItemScans($slug: String, $language_code: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -171,19 +169,16 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
}
... on ComponentMetadataGame {
platforms {
platform {
data {
id
attributes {
short
slug
}
}
}
@ -193,9 +188,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -205,9 +197,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -217,9 +206,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -227,9 +213,6 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -290,7 +273,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -298,7 +281,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -306,7 +289,7 @@ query getLibraryItemScans($slug: String, $language_code: String) {
data {
id
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 } }) {
data {
id
@ -12,16 +12,14 @@ query getPost($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}

View File

@ -23,9 +23,8 @@ query getVideo($uid: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
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 } }) {
data {
attributes {
@ -7,10 +7,8 @@ query getWeapon($slug: String, $language_code: String) {
id
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
@ -57,16 +55,6 @@ fragment sharedWeaponFragment on WeaponStory {
id
attributes {
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 } }) {
data {
id
@ -13,21 +13,15 @@ query getWikiPage($slug: String, $language_code: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}
tags {
data {
id
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
title
}
}
}
}
@ -58,7 +52,7 @@ query getWikiPage($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -66,7 +60,7 @@ query getWikiPage($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -74,7 +68,7 @@ query getWikiPage($slug: String, $language_code: String) {
data {
id
attributes {
...recorderChip
username
}
}
}
@ -90,10 +84,8 @@ query getWikiPage($slug: String, $language_code: String) {
}
categories(pagination: { limit: -1 }) {
data {
id
attributes {
name
short
slug
}
}
}

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
followup_content
videos
view_on
view_on_x
channel
subscribers
description
available_at
available_at_x
want_it
have_it
source

View File

@ -62,135 +62,6 @@ export const prettyInlineTitle = (
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 => {
if (number > 1_000_000) {
return `${(number / 1_000_000).toLocaleString(undefined, {

View File

@ -2,7 +2,9 @@ import { IntlMessageFormat } from "intl-messageformat";
import { LibraryItemMetadataDynamicZone } from "graphql/generated";
import { ICUParams } from "graphql/icuParams";
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 LibraryItemType = Exclude<LibraryItemMetadataDynamicZone["__typename"], undefined>;
@ -29,18 +31,24 @@ const componentSetsTextsetStatusToWording: Record<
};
export const getFormat = (
locale: string | undefined
locale: string | undefined = "en"
): {
format: <K extends WordingKey>(
key: K,
...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]]
) => string;
formatLibraryItemType: (metadata: { __typename: LibraryItemType }) => string;
formatLibraryItemType: (metadata: LibraryItemMetadata) => string;
formatLibraryItemSubType: (metadata: LibraryItemMetadata) => string;
formatStatusLabel: (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 fallbackLangui = getLangui("en");
const typesTranslations = getTypesTranslations();
const format = (
key: WordingKey,
@ -65,8 +73,90 @@ export const getFormat = (
return key;
};
const formatLibraryItemType = (metadata: { __typename: LibraryItemType }): string =>
format(componentMetadataToWording[metadata.__typename]);
const formatLibraryItemType = (metadata: LibraryItemMetadata): string =>
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 =>
format(componentSetsTextsetStatusToWording[status].label);
@ -74,10 +164,65 @@ export const getFormat = (
const formatStatusDescription = (status: ContentStatus): string =>
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 {
format,
formatLibraryItemType,
formatLibraryItemSubType,
formatStatusLabel,
formatStatusDescription,
formatCategory,
formatContentType,
formatWikiTag,
formatWeaponType,
};
};

View File

@ -1,6 +1,8 @@
import {
LocalDataGetCurrenciesQuery,
LocalDataGetLanguagesQuery,
LocalDataGetRecordersQuery,
LocalDataGetTypesTranslationsQuery,
LocalDataGetWebsiteInterfacesQuery,
} from "graphql/generated";
@ -45,3 +47,58 @@ export const processLanguages = (languages: LocalDataGetLanguagesQuery | undefin
}
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 { useCallback } from "react";
import { useRouter } from "next/router";
import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms";
import { LibraryItemMetadataDynamicZone } from "graphql/generated";
import { ICUParams } from "graphql/icuParams";
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { getLogger } from "helpers/logger";
import { prettySlug } from "helpers/formatters";
import { LibraryItemMetadata } from "types/types";
const logger = getLogger("🗺️ [I18n]");
@ -58,12 +61,19 @@ export const useFormat = (): {
key: K,
...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]]
) => string;
formatLibraryItemType: (metadata: { __typename: LibraryItemType }) => string;
formatLibraryItemType: (metadata: LibraryItemMetadata) => string;
formatLibraryItemSubType: (metadata: LibraryItemMetadata) => string;
formatStatusLabel: (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 fallbackLangui = useAtomGetter(atoms.localData.fallbackLangui);
const typesTranslations = useAtomGetter(atoms.localData.typesTranslations);
const { locale = "en" } = useRouter();
const format = useCallback(
(
@ -96,12 +106,6 @@ Falling back to en translation.`
[langui, fallbackLangui]
);
const formatLibraryItemType = useCallback(
(metadata: { __typename: LibraryItemType }): string =>
format(componentMetadataToWording[metadata.__typename]),
[format]
);
const formatStatusLabel = useCallback(
(status: ContentStatus): string => format(componentSetsTextsetStatusToWording[status].label),
[format]
@ -113,10 +117,178 @@ Falling back to en translation.`
[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 {
format,
formatLibraryItemType,
formatLibraryItemSubType,
formatStatusLabel,
formatStatusDescription,
formatCategory,
formatContentType,
formatWikiTag,
formatWeaponType,
};
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,7 +23,6 @@ import { getReadySdk } from "graphql/sdk";
import {
prettyDate,
prettyInlineTitle,
prettyItemSubType,
prettyPrice,
prettySlug,
prettyURL,
@ -91,7 +90,13 @@ const LibrarySlug = ({
}: Props): JSX.Element => {
const currency = useAtomGetter(atoms.settings.currency);
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const { format, formatLibraryItemType } = useFormat();
const {
format,
formatLibraryItemType,
formatCategory,
formatContentType,
formatLibraryItemSubType,
} = useFormat();
const currencies = useAtomGetter(atoms.localData.currencies);
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
@ -307,7 +312,7 @@ const LibrarySlug = ({
<div className="grid grid-flow-col gap-1">
<Chip text={formatLibraryItemType(item.metadata[0])} />
{""}
<Chip text={prettyItemSubType(item.metadata[0])} />
<Chip text={formatLibraryItemSubType(item.metadata[0])} />
</div>
</div>
)}
@ -347,7 +352,10 @@ const LibrarySlug = ({
</h3>
<div className="flex flex-row flex-wrap place-content-center gap-2">
{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>
@ -506,12 +514,12 @@ const LibrarySlug = ({
subitem.attributes.metadata &&
subitem.attributes.metadata.length > 0 &&
subitem.attributes.metadata[0]
? [prettyItemSubType(subitem.attributes.metadata[0])]
? [formatLibraryItemSubType(subitem.attributes.metadata[0])]
: []
}
bottomChips={subitem.attributes.categories?.data.map(
(category) => category.attributes?.short ?? ""
)}
bottomChips={filterHasAttributes(subitem.attributes.categories?.data, [
"attributes",
]).map((category) => formatCategory(category.attributes.slug))}
metadata={{
releaseDate: subitem.attributes.release_date,
price: subitem.attributes.price,
@ -573,14 +581,14 @@ const LibrarySlug = ({
categories: filterHasAttributes(
rangedContent.attributes.content.data.attributes.categories?.data,
["attributes"]
).map((category) => category.attributes.short),
type:
rangedContent.attributes.content.data.attributes.type?.data
?.attributes?.titles?.[0]?.title ??
prettySlug(
rangedContent.attributes.content.data.attributes.type?.data
?.attributes?.slug
),
).map((category) => formatCategory(category.attributes.slug)),
type: rangedContent.attributes.content.data.attributes.type?.data
?.attributes
? formatContentType(
rangedContent.attributes.content.data.attributes.type.data
.attributes.slug
)
: undefined,
slug: rangedContent.attributes.content.data.attributes.slug,
}
: undefined
@ -619,10 +627,9 @@ export default LibrarySlug;
export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk();
const { format } = getFormat(context.locale);
const { format, formatCategory, formatLibraryItemSubType } = getFormat(context.locale);
const item = await sdk.getLibraryItem({
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 };
sortRangedContent(item.libraryItems.data[0].attributes.contents);
@ -634,10 +641,10 @@ export const getStaticProps: GetStaticProps = async (context) => {
{
[format("category", { count: Infinity })]: filterHasAttributes(
item.libraryItems.data[0].attributes.categories?.data,
["attributes.short"]
).map((category) => category.attributes.short),
["attributes"]
).map((category) => formatCategory(category.attributes.slug)),
[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")]: [
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 item = await sdk.getLibraryItemScans({
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)
return { notFound: true };
@ -891,7 +890,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
{filterHasAttributes(selectedScan.scanners.data, ["id", "attributes"]).map(
(scanner) => (
<Fragment key={scanner.id}>
<RecorderChip recorder={scanner.attributes} />
<RecorderChip username={scanner.attributes.username} />
</Fragment>
)
)}
@ -906,7 +905,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
{filterHasAttributes(selectedScan.cleaners.data, ["id", "attributes"]).map(
(cleaner) => (
<Fragment key={cleaner.id}>
<RecorderChip recorder={cleaner.attributes} />
<RecorderChip username={cleaner.attributes.username} />
</Fragment>
)
)}
@ -921,7 +920,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
{filterHasAttributes(selectedScan.typesetters.data, ["id", "attributes"]).map(
(typesetter) => (
<Fragment key={typesetter.id}>
<RecorderChip recorder={typesetter.attributes} />
<RecorderChip username={typesetter.attributes.username} />
</Fragment>
)
)}

View File

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

View File

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

View File

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

View File

@ -51,10 +51,10 @@ const queryParamSchema = z.object({
interface Props extends AppLayoutRequired {}
const Wiki = (props: Props): JSX.Element => {
const { format, formatCategory, formatWikiTag } = useFormat();
const hoverable = useDeviceSupportsHover();
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
const closeSubPanel = useCallback(() => setSubPanelOpened(false), [setSubPanelOpened]);
const { format } = useFormat();
const router = useTypedRouter(queryParamSchema);
const [query, setQuery] = useState(router.query.query ?? DEFAULT_FILTERS_STATE.query);
@ -201,11 +201,11 @@ const Wiki = (props: Props): JSX.Element => {
thumbnailRounded
thumbnailForceAspectRatio
keepInfoVisible
topChips={filterHasAttributes(item.tags?.data, ["attributes"]).map(
(tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug)
topChips={filterHasAttributes(item.tags?.data, ["attributes"]).map((tag) =>
formatWikiTag(tag.attributes.slug)
)}
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 { format } = useFormat();
const { format, formatCategory, formatWeaponType } = useFormat();
const intersectionIds = useMemo(
() => filterDefined(weapon.stories).map(({ id }) => `story-${id}`),
@ -91,8 +91,10 @@ const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX
key={index}
url={`#${id}`}
title={`Story ${index + 1}`}
subtitle={weapon.stories?.[index]?.categories?.data
.map((category) => category.attributes?.name)
subtitle={filterHasAttributes(weapon.stories?.[index]?.categories?.data, [
"attributes",
])
.map((category) => formatCategory(category.attributes.slug))
.join("・")}
active={currentIntersection === index}
border
@ -133,6 +135,11 @@ const WeaponPage = ({ weapon, primaryName, aliases, ...otherProps }: Props): JSX
<ThumbnailHeader
title={primaryName}
subtitle={aliases.join("・")}
type={
weapon.type?.data?.attributes
? formatWeaponType(weapon.type.data.attributes.slug)
: undefined
}
thumbnail={weapon.thumbnail?.data?.attributes}
/>
@ -166,10 +173,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk();
const { format } = getFormat(context.locale);
const slug = context.params?.slug ? context.params.slug.toString() : "";
const weaponResp = await sdk.getWeapon({
slug: slug,
language_code: context.locale ?? "en",
});
const weaponResp = await sdk.getWeapon({ slug: slug });
const weapon = weaponResp.weaponStories?.data[0]?.attributes;
@ -227,7 +231,7 @@ interface WeaponStoryProps {
}
const WeaponStory = ({ story, storyNumber, id }: WeaponStoryProps): JSX.Element => {
const { format } = useFormat();
const { format, formatCategory } = useFormat();
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
items: story.translations,
languageExtractor: useCallback(
@ -241,14 +245,18 @@ const WeaponStory = ({ story, storyNumber, id }: WeaponStoryProps): JSX.Element
return (
<InsetBox id={id} className="formatted">
<h2 className="!mb-4 !mt-4">{format("story_x", { x: storyNumber })}</h2>
{languageSwitcherProps.locales.size > 1 && <LanguageSwitcher {...languageSwitcherProps} />}
<div className="flex place-content-center place-items-center gap-4">
<h2 className="!mb-4 !mt-4">{format("story_x", { x: storyNumber })}</h2>
{languageSwitcherProps.locales.size > 1 && <LanguageSwitcher {...languageSwitcherProps} />}
</div>
{story.categories && story.categories.data.length > 0 && (
<div className="mb-12 flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(story.categories.data, ["attributes.name"]).map((category) => (
<Chip key={category.id} text={category.attributes.name} />
{filterHasAttributes(story.categories.data, ["attributes"]).map((category) => (
<Chip
key={category.attributes.slug}
text={formatCategory(category.attributes.slug, "full")}
/>
))}
</div>
)}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -73,3 +73,39 @@ export enum LibraryItemUserStatus {
Want = 1,
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;