Categories and recorders are now localdata
This commit is contained in:
		
							parent
							
								
									c3796b4fe8
								
							
						
					
					
						commit
						284bbd6272
					
				| @ -1 +1,2 @@ | ||||
| .next | ||||
| public/local-data/* | ||||
| @ -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}}]}} | ||||
| @ -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":"中文"}}]}} | ||||
							
								
								
									
										1
									
								
								public/local-data/recorders.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								public/local-data/recorders.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								public/local-data/typesTranslations.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								public/local-data/typesTranslations.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -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> | ||||
| ); | ||||
|  | ||||
| @ -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) | ||||
|                   )} | ||||
|                 /> | ||||
|               ))} | ||||
|  | ||||
| @ -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} /> | ||||
|  | ||||
| @ -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"> | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -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 }, | ||||
| }; | ||||
|  | ||||
| @ -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]); | ||||
| }; | ||||
|  | ||||
| @ -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); | ||||
| }; | ||||
|  | ||||
| @ -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 | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -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 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @ -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 = | ||||
|  | ||||
| @ -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 | ||||
|                       } | ||||
|                     } | ||||
|                   } | ||||
|  | ||||
| @ -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 | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|  | ||||
| @ -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 | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|  | ||||
| @ -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 | ||||
|                           } | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|  | ||||
| @ -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 | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|  | ||||
| @ -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 | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
| @ -23,9 +23,8 @@ query getVideo($uid: String) { | ||||
|         } | ||||
|         categories(pagination: { limit: -1 }) { | ||||
|           data { | ||||
|             id | ||||
|             attributes { | ||||
|               short | ||||
|               slug | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|  | ||||
| @ -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 | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @ -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 | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|  | ||||
| @ -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 | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -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 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -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 | ||||
|  | ||||
| @ -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, { | ||||
|  | ||||
| @ -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, | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| @ -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 ?? [], | ||||
| }); | ||||
|  | ||||
| @ -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, | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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)), | ||||
|             }), | ||||
|           }; | ||||
|         } | ||||
|  | ||||
| @ -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 | ||||
|     /> | ||||
|   ); | ||||
|  | ||||
| @ -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} | ||||
|             /> | ||||
|  | ||||
| @ -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 }; | ||||
|   } | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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> | ||||
|                     ) | ||||
|                   )} | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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) | ||||
|               )} | ||||
|             /> | ||||
|           ))} | ||||
|  | ||||
| @ -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> | ||||
|       )} | ||||
|  | ||||
| @ -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
											
										
									
								
							| @ -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; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint