Fixed bugs + translated components
This commit is contained in:
		
							parent
							
								
									930da37d64
								
							
						
					
					
						commit
						0df66815c8
					
				| @ -51,7 +51,7 @@ interface Props { | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| const ScanSet = ({ | ||||
| export const ScanSet = ({ | ||||
|   openLightBox, | ||||
|   scanSet, | ||||
|   id, | ||||
| @ -138,7 +138,9 @@ const ScanSet = ({ | ||||
|                 /> | ||||
|               )} | ||||
| 
 | ||||
|             <LanguageSwitcher {...languageSwitcherProps} /> | ||||
|             {languageSwitcherProps.locales.size > 1 && ( | ||||
|               <LanguageSwitcher {...languageSwitcherProps} /> | ||||
|             )} | ||||
| 
 | ||||
|             <div className="grid place-content-center place-items-center"> | ||||
|               <p className="font-headers font-bold">{langui.status}:</p> | ||||
| @ -242,40 +244,3 @@ const ScanSet = ({ | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| interface TranslatedProps extends Omit<Props, "title"> { | ||||
|   translations: { | ||||
|     title: string; | ||||
|     language: string; | ||||
|   }[]; | ||||
|   fallbackTitle: TranslatedProps["translations"][number]["title"]; | ||||
|   languages: AppStaticProps["languages"]; | ||||
| } | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedScanSet = ({ | ||||
|   fallbackTitle, | ||||
|   translations = [{ title: fallbackTitle, language: "default" }], | ||||
|   languages, | ||||
|   ...otherProps | ||||
| }: TranslatedProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: useCallback( | ||||
|       (item: TranslatedProps["translations"][number]) => item.language, | ||||
|       [] | ||||
|     ), | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <ScanSet | ||||
|       title={selectedTranslation?.title ?? fallbackTitle} | ||||
|       languages={languages} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -1,11 +1,9 @@ | ||||
| import { useRouter } from "next/router"; | ||||
| import { MouseEventHandler, useCallback, useMemo } from "react"; | ||||
| import { MouseEventHandler, useMemo } from "react"; | ||||
| import { Ico, Icon } from "components/Ico"; | ||||
| import { ToolTip } from "components/ToolTip"; | ||||
| import { cJoin, cIf } from "helpers/className"; | ||||
| import { isDefinedAndNotEmpty } from "helpers/others"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| 
 | ||||
| /* | ||||
|  *                                        ╭─────────────╮ | ||||
| @ -90,45 +88,3 @@ export const NavOption = ({ | ||||
|     </ToolTip> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| interface TranslatedProps extends Omit<Props, "subtitle" | "title"> { | ||||
|   translations: { | ||||
|     title: string | null | undefined; | ||||
|     subtitle?: string | null | undefined; | ||||
|     language: string; | ||||
|   }[]; | ||||
|   fallbackTitle: TranslatedProps["translations"][number]["title"]; | ||||
|   fallbackSubtitle: TranslatedProps["translations"][number]["subtitle"]; | ||||
|   languages: AppStaticProps["languages"]; | ||||
| } | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedNavOption = ({ | ||||
|   fallbackTitle, | ||||
|   fallbackSubtitle, | ||||
|   translations = [ | ||||
|     { title: fallbackTitle, subtitle: fallbackSubtitle, language: "default" }, | ||||
|   ], | ||||
|   languages, | ||||
|   ...otherProps | ||||
| }: TranslatedProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: useCallback( | ||||
|       (item: TranslatedProps["translations"][number]) => item.language, | ||||
|       [] | ||||
|     ), | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <NavOption | ||||
|       title={selectedTranslation?.title ?? fallbackTitle} | ||||
|       subtitle={selectedTranslation?.subtitle ?? fallbackSubtitle} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import Link from "next/link"; | ||||
| import { useCallback, useMemo } from "react"; | ||||
| import { useMemo } from "react"; | ||||
| import { Chip } from "./Chip"; | ||||
| import { Ico, Icon } from "./Ico"; | ||||
| import { Img } from "./Img"; | ||||
| @ -16,10 +16,9 @@ import { | ||||
|   prettyDuration, | ||||
|   prettyPrice, | ||||
|   prettyShortenNumber, | ||||
|   prettySlug, | ||||
| } from "helpers/formatters"; | ||||
| import { ImageQuality } from "helpers/img"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| import { useMediaHoverable } from "hooks/useMediaQuery"; | ||||
| 
 | ||||
| /* | ||||
|  *                                        ╭─────────────╮ | ||||
| @ -78,6 +77,7 @@ export const PreviewCard = ({ | ||||
|   infoAppend, | ||||
| }: Props): JSX.Element => { | ||||
|   const { currency } = useAppLayout(); | ||||
|   const isHoverable = useMediaHoverable(); | ||||
| 
 | ||||
|   const metadataJSX = useMemo( | ||||
|     () => ( | ||||
| @ -247,7 +247,7 @@ export const PreviewCard = ({ | ||||
|           className={cJoin( | ||||
|             "z-20 grid gap-2 p-4 transition-opacity linearbg-obi", | ||||
|             cIf( | ||||
|               !keepInfoVisible, | ||||
|               !keepInfoVisible && isHoverable, | ||||
|               `-inset-x-0.5 bottom-2 opacity-0 [border-radius:10%_10%_10%_10%_/_1%_1%_3%_3%]
 | ||||
|               group-hover:opacity-100 hoverable:absolute hoverable:drop-shadow-shade-lg | ||||
|               notHoverable:rounded-b-md notHoverable:opacity-100`,
 | ||||
| @ -291,46 +291,3 @@ export const PreviewCard = ({ | ||||
|     </Link> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| interface TranslatedProps | ||||
|   extends Omit<Props, "description" | "pre_title" | "subtitle" | "title"> { | ||||
|   translations: { | ||||
|     pre_title?: string | null | undefined; | ||||
|     title: string | null | undefined; | ||||
|     subtitle?: string | null | undefined; | ||||
|     description?: string | null | undefined; | ||||
|     language: string | undefined; | ||||
|   }[]; | ||||
|   slug: string; | ||||
|   languages: AppStaticProps["languages"]; | ||||
| } | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedPreviewCard = ({ | ||||
|   slug, | ||||
|   translations = [{ title: slug, language: "default" }], | ||||
|   languages, | ||||
|   ...otherProps | ||||
| }: TranslatedProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: useCallback( | ||||
|       (item: TranslatedProps["translations"][number]) => item.language, | ||||
|       [] | ||||
|     ), | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <PreviewCard | ||||
|       pre_title={selectedTranslation?.pre_title} | ||||
|       title={selectedTranslation?.title ?? prettySlug(slug)} | ||||
|       subtitle={selectedTranslation?.subtitle} | ||||
|       description={selectedTranslation?.description} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -1,12 +1,8 @@ | ||||
| import Link from "next/link"; | ||||
| import { useCallback } from "react"; | ||||
| import { Chip } from "./Chip"; | ||||
| import { Img } from "./Img"; | ||||
| import { UploadImageFragment } from "graphql/generated"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { prettySlug } from "helpers/formatters"; | ||||
| import { ImageQuality } from "helpers/img"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| 
 | ||||
| /* | ||||
|  *                                        ╭─────────────╮ | ||||
| @ -26,7 +22,7 @@ interface Props { | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| const PreviewLine = ({ | ||||
| export const PreviewLine = ({ | ||||
|   href, | ||||
|   thumbnail, | ||||
|   pre_title, | ||||
| @ -76,45 +72,3 @@ const PreviewLine = ({ | ||||
|     </div> | ||||
|   </Link> | ||||
| ); | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| interface TranslatedProps | ||||
|   extends Omit<Props, "pre_title" | "subtitle" | "title"> { | ||||
|   translations: { | ||||
|     pre_title?: string | null | undefined; | ||||
|     title: string | null | undefined; | ||||
|     subtitle?: string | null | undefined; | ||||
|     language: string | undefined; | ||||
|   }[]; | ||||
| 
 | ||||
|   slug: string; | ||||
|   languages: AppStaticProps["languages"]; | ||||
| } | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedPreviewLine = ({ | ||||
|   slug, | ||||
|   translations = [{ title: slug, language: "default" }], | ||||
|   languages, | ||||
|   ...otherProps | ||||
| }: TranslatedProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: useCallback( | ||||
|       (item: TranslatedProps["translations"][number]) => item.language, | ||||
|       [] | ||||
|     ), | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <PreviewLine | ||||
|       pre_title={selectedTranslation?.pre_title} | ||||
|       title={selectedTranslation?.title ?? prettySlug(slug)} | ||||
|       subtitle={selectedTranslation?.subtitle} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -41,7 +41,7 @@ export const RecorderChip = ({ recorder, langui }: Props): JSX.Element => ( | ||||
|                 {filterHasAttributes(recorder.languages.data, [ | ||||
|                   "attributes", | ||||
|                 ] as const).map((language) => ( | ||||
|                   <Fragment key={language.attributes.code}> | ||||
|                   <Fragment key={language.__typename}> | ||||
|                     <Chip text={language.attributes.code.toUpperCase()} /> | ||||
|                   </Fragment> | ||||
|                 ))} | ||||
|  | ||||
							
								
								
									
										135
									
								
								src/components/Translated.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/components/Translated.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,135 @@ | ||||
| import { PreviewCard } from "./PreviewCard"; | ||||
| import { PreviewLine } from "./PreviewLine"; | ||||
| import { ScanSet } from "./Library/ScanSet"; | ||||
| import { NavOption } from "./PanelComponents/NavOption"; | ||||
| import { AppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| 
 | ||||
| type TranslatedProps<P, K extends keyof P> = Omit<P, K> & { | ||||
|   translations: (Pick<P, K> & { language: string })[]; | ||||
|   fallback: Pick<P, K>; | ||||
|   languages: AppStaticProps["languages"]; | ||||
| }; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| type TranslatedPreviewCardProps = TranslatedProps< | ||||
|   Parameters<typeof PreviewCard>[0], | ||||
|   "description" | "pre_title" | "subtitle" | "title" | ||||
| >; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedPreviewCard = ({ | ||||
|   translations, | ||||
|   languages, | ||||
|   fallback, | ||||
|   ...otherProps | ||||
| }: TranslatedPreviewCardProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: (item) => item.language, | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <PreviewCard | ||||
|       pre_title={selectedTranslation?.pre_title ?? fallback.pre_title} | ||||
|       title={selectedTranslation?.title ?? fallback.title} | ||||
|       subtitle={selectedTranslation?.subtitle ?? fallback.subtitle} | ||||
|       description={selectedTranslation?.description ?? fallback.description} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| type TranslatedPreviewLineProps = TranslatedProps< | ||||
|   Parameters<typeof PreviewLine>[0], | ||||
|   "pre_title" | "subtitle" | "title" | ||||
| >; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedPreviewLine = ({ | ||||
|   translations, | ||||
|   languages, | ||||
|   fallback, | ||||
|   ...otherProps | ||||
| }: TranslatedPreviewLineProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: (item) => item.language, | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <PreviewLine | ||||
|       pre_title={selectedTranslation?.pre_title ?? fallback.pre_title} | ||||
|       title={selectedTranslation?.title ?? fallback.title} | ||||
|       subtitle={selectedTranslation?.subtitle ?? fallback.subtitle} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| type TranslatedScanSetProps = TranslatedProps< | ||||
|   Parameters<typeof ScanSet>[0], | ||||
|   "title" | ||||
| >; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedScanSet = ({ | ||||
|   translations, | ||||
|   languages, | ||||
|   fallback, | ||||
|   ...otherProps | ||||
| }: TranslatedScanSetProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: (item) => item.language, | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <ScanSet | ||||
|       title={selectedTranslation?.title ?? fallback.title} | ||||
|       languages={languages} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| type TranslatedNavOptionProps = TranslatedProps< | ||||
|   Parameters<typeof NavOption>[0], | ||||
|   "subtitle" | "title" | ||||
| >; | ||||
| 
 | ||||
| // ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 | ||||
| 
 | ||||
| export const TranslatedNavOption = ({ | ||||
|   translations, | ||||
|   languages, | ||||
|   fallback, | ||||
|   ...otherProps | ||||
| }: TranslatedNavOptionProps): JSX.Element => { | ||||
|   const [selectedTranslation] = useSmartLanguage({ | ||||
|     items: translations, | ||||
|     languages: languages, | ||||
|     languageExtractor: (item) => item.language, | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <NavOption | ||||
|       title={selectedTranslation?.title ?? fallback.title} | ||||
|       subtitle={selectedTranslation?.subtitle ?? fallback.subtitle} | ||||
|       {...otherProps} | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| @ -75,22 +75,27 @@ export const filterHasAttributes = <T, P extends PathDot<T>>( | ||||
|   t: T[] | null | undefined, | ||||
|   paths: readonly P[] | ||||
| ): SelectiveNonNullable<T, typeof paths[number]>[] => | ||||
|   isUndefined(t) | ||||
|     ? [] | ||||
|     : (t.filter((item) => | ||||
|   isDefined(t) | ||||
|     ? (t.filter((item) => | ||||
|         hasAttributes(item, paths) | ||||
|       ) as unknown as SelectiveNonNullable<T, typeof paths[number]>[]); | ||||
|       ) as unknown as SelectiveNonNullable<T, typeof paths[number]>[]) | ||||
|     : []; | ||||
| 
 | ||||
| const hasAttributes = <T>(item: T, paths: readonly PathDot<T>[]): boolean => { | ||||
| const hasAttributes = <T>(item: T, paths: readonly PathDot<T>[]): boolean => | ||||
|   isDefined(item) && paths.every((path) => hasAttribute(item, path)); | ||||
| 
 | ||||
| const hasAttribute = <T>(item: T, path: string): boolean => { | ||||
|   if (isDefined(item)) { | ||||
|     return paths.every((path) => { | ||||
|       const attributeToCheck = (path as string).split(".")[0]; | ||||
|       return ( | ||||
|         isDefined(attributeToCheck) && | ||||
|         Object.keys(item).includes(attributeToCheck) && | ||||
|         isDefined(item[attributeToCheck as keyof T]) | ||||
|       ); | ||||
|     }); | ||||
|     const [head, ...rest] = path.split("."); | ||||
|     if (Object.keys(item).includes(head)) { | ||||
|       const attribute = head as keyof T; | ||||
|       if (isDefined(item[attribute])) { | ||||
|         if (rest.length > 0) { | ||||
|           return hasAttribute(item[attribute], rest.join(".")); | ||||
|         } | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| }; | ||||
|  | ||||
| @ -12,7 +12,6 @@ import { | ||||
| import { ContentPanel } from "components/Panels/ContentPanel"; | ||||
| import { SubPanel } from "components/Panels/SubPanel"; | ||||
| import { PreviewCard } from "components/PreviewCard"; | ||||
| import { TranslatedPreviewLine } from "components/PreviewLine"; | ||||
| import { RecorderChip } from "components/RecorderChip"; | ||||
| import { ThumbnailHeader } from "components/ThumbnailHeader"; | ||||
| import { ToolTip } from "components/ToolTip"; | ||||
| @ -27,7 +26,6 @@ import { | ||||
| } from "helpers/formatters"; | ||||
| import { isUntangibleGroupItem } from "helpers/libraryItem"; | ||||
| import { | ||||
|   filterDefined, | ||||
|   filterHasAttributes, | ||||
|   getStatusDescription, | ||||
|   isDefinedAndNotEmpty, | ||||
| @ -36,6 +34,7 @@ import { ContentWithTranslations } from "helpers/types"; | ||||
| import { useMediaMobile } from "hooks/useMediaQuery"; | ||||
| import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange"; | ||||
| import { useSmartLanguage } from "hooks/useSmartLanguage"; | ||||
| import { TranslatedPreviewLine } from "components/Translated"; | ||||
| 
 | ||||
| /* | ||||
|  *                                           ╭────────╮ | ||||
| @ -344,15 +343,18 @@ const Content = ({ | ||||
|               </h2> | ||||
|               <TranslatedPreviewLine | ||||
|                 href={`/contents/${previousContent.attributes.slug}`} | ||||
|                 translations={filterDefined( | ||||
|                   previousContent.attributes.translations | ||||
|                 translations={filterHasAttributes( | ||||
|                   previousContent.attributes.translations, | ||||
|                   ["language.data.attributes.code"] as const | ||||
|                 ).map((translation) => ({ | ||||
|                   pre_title: translation.pre_title, | ||||
|                   title: translation.title, | ||||
|                   subtitle: translation.subtitle, | ||||
|                   language: translation.language?.data?.attributes?.code, | ||||
|                   language: translation.language.data.attributes.code, | ||||
|                 }))} | ||||
|                 slug={previousContent.attributes.slug} | ||||
|                 fallback={{ | ||||
|                   title: prettySlug(previousContent.attributes.slug), | ||||
|                 }} | ||||
|                 languages={languages} | ||||
|                 thumbnail={ | ||||
|                   previousContent.attributes.thumbnail?.data?.attributes | ||||
| @ -397,15 +399,16 @@ const Content = ({ | ||||
|               </h2> | ||||
|               <TranslatedPreviewLine | ||||
|                 href={`/contents/${nextContent.attributes.slug}`} | ||||
|                 translations={filterDefined( | ||||
|                   nextContent.attributes.translations | ||||
|                 translations={filterHasAttributes( | ||||
|                   nextContent.attributes.translations, | ||||
|                   ["language.data.attributes.code"] as const | ||||
|                 ).map((translation) => ({ | ||||
|                   pre_title: translation.pre_title, | ||||
|                   title: translation.title, | ||||
|                   subtitle: translation.subtitle, | ||||
|                   language: translation.language?.data?.attributes?.code, | ||||
|                   language: translation.language.data.attributes.code, | ||||
|                 }))} | ||||
|                 slug={nextContent.attributes.slug} | ||||
|                 fallback={{ title: nextContent.attributes.slug }} | ||||
|                 languages={languages} | ||||
|                 thumbnail={nextContent.attributes.thumbnail?.data?.attributes} | ||||
|                 thumbnailAspectRatio="3/2" | ||||
|  | ||||
| @ -9,7 +9,6 @@ import { | ||||
|   ContentPanelWidthSizes, | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import { SubPanel } from "components/Panels/SubPanel"; | ||||
| import { TranslatedPreviewCard } from "components/PreviewCard"; | ||||
| import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| import { prettyinlineTitle, prettySlug } from "helpers/formatters"; | ||||
| @ -24,6 +23,7 @@ import { SmartList } from "components/SmartList"; | ||||
| import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder"; | ||||
| import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable"; | ||||
| import { useBoolean } from "hooks/useBoolean"; | ||||
| import { TranslatedPreviewCard } from "components/Translated"; | ||||
| 
 | ||||
| /* | ||||
|  *                                         ╭─────────────╮ | ||||
| @ -223,49 +223,41 @@ const Contents = ({ | ||||
|           items={filterHasAttributes(contents, ["attributes", "id"] as const)} | ||||
|           getItemId={(item) => item.id} | ||||
|           renderItem={({ item }) => ( | ||||
|             <> | ||||
|               {item.attributes.translations && ( | ||||
|                 <TranslatedPreviewCard | ||||
|                   href={`/contents/${item.attributes.slug}`} | ||||
|                   translations={item.attributes.translations.map( | ||||
|                     (translation) => ({ | ||||
|                       pre_title: translation?.pre_title, | ||||
|                       title: translation?.title, | ||||
|                       subtitle: translation?.subtitle, | ||||
|                       language: translation?.language?.data?.attributes?.code, | ||||
|                     }) | ||||
|                   )} | ||||
|                   slug={item.attributes.slug} | ||||
|                   languages={languages} | ||||
|                   thumbnail={item.attributes.thumbnail?.data?.attributes} | ||||
|                   thumbnailAspectRatio="3/2" | ||||
|                   thumbnailForceAspectRatio | ||||
|                   stackNumber={ | ||||
|                     effectiveCombineRelatedContent && | ||||
|                     item.attributes.group?.data?.attributes?.combine === true | ||||
|                       ? item.attributes.group.data.attributes.contents?.data | ||||
|                           .length | ||||
|                       : 0 | ||||
|                   } | ||||
|                   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 | ||||
|                               ), | ||||
|                         ] | ||||
|                       : undefined | ||||
|                   } | ||||
|                   bottomChips={item.attributes.categories?.data.map( | ||||
|                     (category) => category.attributes?.short ?? "" | ||||
|                   )} | ||||
|                   keepInfoVisible={keepInfoVisible} | ||||
|                 /> | ||||
|             <TranslatedPreviewCard | ||||
|               href={`/contents/${item.attributes.slug}`} | ||||
|               translations={filterHasAttributes(item.attributes.translations, [ | ||||
|                 "language.data.attributes.code", | ||||
|               ] as const).map((translation) => ({ | ||||
|                 pre_title: translation.pre_title, | ||||
|                 title: translation.title, | ||||
|                 subtitle: translation.subtitle, | ||||
|                 language: translation.language.data.attributes.code, | ||||
|               }))} | ||||
|               fallback={{ title: prettySlug(item.attributes.slug) }} | ||||
|               languages={languages} | ||||
|               thumbnail={item.attributes.thumbnail?.data?.attributes} | ||||
|               thumbnailAspectRatio="3/2" | ||||
|               thumbnailForceAspectRatio | ||||
|               stackNumber={ | ||||
|                 effectiveCombineRelatedContent && | ||||
|                 item.attributes.group?.data?.attributes?.combine === true | ||||
|                   ? item.attributes.group.data.attributes.contents?.data.length | ||||
|                   : 0 | ||||
|               } | ||||
|               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), | ||||
|                     ] | ||||
|                   : undefined | ||||
|               } | ||||
|               bottomChips={item.attributes.categories?.data.map( | ||||
|                 (category) => category.attributes?.short ?? "" | ||||
|               )} | ||||
|             </> | ||||
|               keepInfoVisible={keepInfoVisible} | ||||
|             /> | ||||
|           )} | ||||
|           renderWhenEmpty={() => ( | ||||
|             <ContentPlaceholder | ||||
|  | ||||
| @ -1,9 +1,7 @@ | ||||
| import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next"; | ||||
| import { Fragment, useMemo } from "react"; | ||||
| import { AppLayout } from "components/AppLayout"; | ||||
| import { TranslatedScanSet } from "components/Library/ScanSet"; | ||||
| import { ScanSetCover } from "components/Library/ScanSetCover"; | ||||
| import { TranslatedNavOption } from "components/PanelComponents/NavOption"; | ||||
| import { | ||||
|   ReturnButton, | ||||
|   ReturnButtonType, | ||||
| @ -31,6 +29,7 @@ import { isUntangibleGroupItem } from "helpers/libraryItem"; | ||||
| import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs"; | ||||
| import { PreviewCard } from "components/PreviewCard"; | ||||
| import { HorizontalLine } from "components/HorizontalLine"; | ||||
| import { TranslatedNavOption, TranslatedScanSet } from "components/Translated"; | ||||
| 
 | ||||
| /* | ||||
|  *                                           ╭────────╮ | ||||
| @ -69,34 +68,36 @@ const LibrarySlug = ({ | ||||
|           displayOn={ReturnButtonType.Desktop} | ||||
|         /> | ||||
| 
 | ||||
|         <div className="mobile:w-[80%]"> | ||||
|           <PreviewCard | ||||
|             href={`/library/${item.slug}`} | ||||
|             title={item.title} | ||||
|             subtitle={item.subtitle} | ||||
|             thumbnail={item.thumbnail?.data?.attributes} | ||||
|             thumbnailAspectRatio="21/29.7" | ||||
|             thumbnailRounded={false} | ||||
|             topChips={ | ||||
|               item.metadata && item.metadata.length > 0 && item.metadata[0] | ||||
|                 ? [prettyItemSubType(item.metadata[0])] | ||||
|                 : [] | ||||
|             } | ||||
|             bottomChips={filterHasAttributes(item.categories?.data, [ | ||||
|               "attributes", | ||||
|             ] as const).map((category) => category.attributes.short)} | ||||
|             metadata={{ | ||||
|               currencies: currencies, | ||||
|               release_date: item.release_date, | ||||
|               price: item.price, | ||||
|               position: "Bottom", | ||||
|             }} | ||||
|             infoAppend={ | ||||
|               !isUntangibleGroupItem(item.metadata?.[0]) && ( | ||||
|                 <PreviewCardCTAs id={itemId} langui={langui} /> | ||||
|               ) | ||||
|             } | ||||
|           /> | ||||
|         <div className="grid place-items-center"> | ||||
|           <div className="mobile:w-[80%]"> | ||||
|             <PreviewCard | ||||
|               href={`/library/${item.slug}`} | ||||
|               title={item.title} | ||||
|               subtitle={item.subtitle} | ||||
|               thumbnail={item.thumbnail?.data?.attributes} | ||||
|               thumbnailAspectRatio="21/29.7" | ||||
|               thumbnailRounded={false} | ||||
|               topChips={ | ||||
|                 item.metadata && item.metadata.length > 0 && item.metadata[0] | ||||
|                   ? [prettyItemSubType(item.metadata[0])] | ||||
|                   : [] | ||||
|               } | ||||
|               bottomChips={filterHasAttributes(item.categories?.data, [ | ||||
|                 "attributes", | ||||
|               ] as const).map((category) => category.attributes.short)} | ||||
|               metadata={{ | ||||
|                 currencies: currencies, | ||||
|                 release_date: item.release_date, | ||||
|                 price: item.price, | ||||
|                 position: "Bottom", | ||||
|               }} | ||||
|               infoAppend={ | ||||
|                 !isUntangibleGroupItem(item.metadata?.[0]) && ( | ||||
|                   <PreviewCardCTAs id={itemId} langui={langui} /> | ||||
|                 ) | ||||
|               } | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <HorizontalLine /> | ||||
| @ -132,18 +133,16 @@ const LibrarySlug = ({ | ||||
|                             `${content.attributes.range[0].ending_page}` | ||||
|                           : undefined, | ||||
|                     }))} | ||||
|                     fallbackTitle={prettySlug( | ||||
|                       content.attributes.slug, | ||||
|                       item.slug | ||||
|                     )} | ||||
|                     fallbackSubtitle={ | ||||
|                       content.attributes.range[0]?.__typename === | ||||
|                       "ComponentRangePageRange" | ||||
|                         ? `${content.attributes.range[0].starting_page}` + | ||||
|                           `→` + | ||||
|                           `${content.attributes.range[0].ending_page}` | ||||
|                         : undefined | ||||
|                     } | ||||
|                     fallback={{ | ||||
|                       title: prettySlug(content.attributes.slug, item.slug), | ||||
|                       subtitle: | ||||
|                         content.attributes.range[0]?.__typename === | ||||
|                         "ComponentRangePageRange" | ||||
|                           ? `${content.attributes.range[0].starting_page}` + | ||||
|                             `→` + | ||||
|                             `${content.attributes.range[0].ending_page}` | ||||
|                           : undefined, | ||||
|                     }} | ||||
|                     border | ||||
|                     languages={languages} | ||||
|                   /> | ||||
| @ -210,7 +209,9 @@ const LibrarySlug = ({ | ||||
|                     translation.subtitle | ||||
|                   ), | ||||
|                 }))} | ||||
|                 fallbackTitle={prettySlug(content.attributes.slug, item.slug)} | ||||
|                 fallback={{ | ||||
|                   title: prettySlug(content.attributes.slug, item.slug), | ||||
|                 }} | ||||
|                 languages={languages} | ||||
|                 langui={langui} | ||||
|                 content={content.attributes.content} | ||||
|  | ||||
| @ -8,7 +8,6 @@ import { | ||||
|   ContentPanelWidthSizes, | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import { SubPanel } from "components/Panels/SubPanel"; | ||||
| import { TranslatedPreviewCard } from "components/PreviewCard"; | ||||
| import { GetPostsPreviewQuery } from "graphql/generated"; | ||||
| import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps"; | ||||
| import { getReadySdk } from "graphql/sdk"; | ||||
| @ -18,9 +17,10 @@ import { WithLabel } from "components/Inputs/WithLabel"; | ||||
| import { TextInput } from "components/Inputs/TextInput"; | ||||
| import { Button } from "components/Inputs/Button"; | ||||
| import { useMediaHoverable } from "hooks/useMediaQuery"; | ||||
| import { filterDefined, filterHasAttributes } from "helpers/others"; | ||||
| import { filterHasAttributes } from "helpers/others"; | ||||
| import { SmartList } from "components/SmartList"; | ||||
| import { useBoolean } from "hooks/useBoolean"; | ||||
| import { TranslatedPreviewCard } from "components/Translated"; | ||||
| 
 | ||||
| /* | ||||
|  *                                         ╭─────────────╮ | ||||
| @ -113,15 +113,15 @@ const News = ({ | ||||
|           renderItem={({ item: post }) => ( | ||||
|             <TranslatedPreviewCard | ||||
|               href={`/news/${post.attributes.slug}`} | ||||
|               translations={filterDefined(post.attributes.translations).map( | ||||
|                 (translation) => ({ | ||||
|                   language: translation.language?.data?.attributes?.code, | ||||
|                   title: translation.title, | ||||
|                   description: translation.excerpt, | ||||
|                 }) | ||||
|               )} | ||||
|               translations={filterHasAttributes(post.attributes.translations, [ | ||||
|                 "language.data.attributes.code", | ||||
|               ] as const).map((translation) => ({ | ||||
|                 language: translation.language.data.attributes.code, | ||||
|                 title: translation.title, | ||||
|                 description: translation.excerpt, | ||||
|               }))} | ||||
|               fallback={{ title: prettySlug(post.attributes.slug) }} | ||||
|               languages={languages} | ||||
|               slug={post.attributes.slug} | ||||
|               thumbnail={post.attributes.thumbnail?.data?.attributes} | ||||
|               thumbnailAspectRatio="3/2" | ||||
|               thumbnailForceAspectRatio | ||||
|  | ||||
| @ -12,20 +12,20 @@ import { | ||||
|   ContentPanel, | ||||
|   ContentPanelWidthSizes, | ||||
| } from "components/Panels/ContentPanel"; | ||||
| import { TranslatedPreviewCard } from "components/PreviewCard"; | ||||
| import { HorizontalLine } from "components/HorizontalLine"; | ||||
| import { Button } from "components/Inputs/Button"; | ||||
| import { Switch } from "components/Inputs/Switch"; | ||||
| import { TextInput } from "components/Inputs/TextInput"; | ||||
| import { WithLabel } from "components/Inputs/WithLabel"; | ||||
| import { useMediaHoverable } from "hooks/useMediaQuery"; | ||||
| import { filterDefined, filterHasAttributes, isDefined } from "helpers/others"; | ||||
| import { filterDefined, filterHasAttributes } from "helpers/others"; | ||||
| import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder"; | ||||
| import { SmartList } from "components/SmartList"; | ||||
| import { Select } from "components/Inputs/Select"; | ||||
| import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable"; | ||||
| import { prettySlug } from "helpers/formatters"; | ||||
| import { useBoolean } from "hooks/useBoolean"; | ||||
| import { TranslatedPreviewCard } from "components/Translated"; | ||||
| 
 | ||||
| /* | ||||
|  *                                         ╭─────────────╮ | ||||
| @ -169,44 +169,40 @@ const Wiki = ({ | ||||
|           items={filterHasAttributes(pages, ["id", "attributes"] as const)} | ||||
|           getItemId={(item) => item.id} | ||||
|           renderItem={({ item }) => ( | ||||
|             <> | ||||
|               {isDefined(item.attributes.translations) && ( | ||||
|                 <TranslatedPreviewCard | ||||
|                   href={`/wiki/${item.attributes.slug}`} | ||||
|                   translations={item.attributes.translations.map( | ||||
|                     (translation) => ({ | ||||
|                       title: translation?.title, | ||||
|                       subtitle: | ||||
|                         translation?.aliases && translation.aliases.length > 0 | ||||
|                           ? translation.aliases | ||||
|                               .map((alias) => alias?.alias) | ||||
|                               .join("・") | ||||
|                           : undefined, | ||||
|                       description: translation?.summary, | ||||
|                       language: translation?.language?.data?.attributes?.code, | ||||
|                     }) | ||||
|                   )} | ||||
|                   thumbnail={item.attributes.thumbnail?.data?.attributes} | ||||
|                   thumbnailAspectRatio={"4/3"} | ||||
|                   thumbnailRounded | ||||
|                   thumbnailForceAspectRatio | ||||
|                   languages={languages} | ||||
|                   slug={item.attributes.slug} | ||||
|                   keepInfoVisible={keepInfoVisible} | ||||
|                   topChips={filterHasAttributes(item.attributes.tags?.data, [ | ||||
|                     "attributes", | ||||
|                   ] as const).map( | ||||
|                     (tag) => | ||||
|                       tag.attributes.titles?.[0]?.title ?? | ||||
|                       prettySlug(tag.attributes.slug) | ||||
|                   )} | ||||
|                   bottomChips={filterHasAttributes( | ||||
|                     item.attributes.categories?.data, | ||||
|                     ["attributes"] as const | ||||
|                   ).map((category) => category.attributes.short)} | ||||
|                 /> | ||||
|             <TranslatedPreviewCard | ||||
|               href={`/wiki/${item.attributes.slug}`} | ||||
|               translations={filterHasAttributes(item.attributes.translations, [ | ||||
|                 "language.data.attributes.code", | ||||
|               ] as const).map((translation) => ({ | ||||
|                 title: translation.title, | ||||
|                 subtitle: | ||||
|                   translation.aliases && translation.aliases.length > 0 | ||||
|                     ? translation.aliases | ||||
|                         .map((alias) => alias?.alias) | ||||
|                         .join("・") | ||||
|                     : undefined, | ||||
|                 description: translation.summary, | ||||
|                 language: translation.language.data.attributes.code, | ||||
|               }))} | ||||
|               fallback={{ title: prettySlug(item.attributes.slug) }} | ||||
|               thumbnail={item.attributes.thumbnail?.data?.attributes} | ||||
|               thumbnailAspectRatio={"4/3"} | ||||
|               thumbnailRounded | ||||
|               thumbnailForceAspectRatio | ||||
|               languages={languages} | ||||
|               keepInfoVisible={keepInfoVisible} | ||||
|               topChips={filterHasAttributes(item.attributes.tags?.data, [ | ||||
|                 "attributes", | ||||
|               ] as const).map( | ||||
|                 (tag) => | ||||
|                   tag.attributes.titles?.[0]?.title ?? | ||||
|                   prettySlug(tag.attributes.slug) | ||||
|               )} | ||||
|             </> | ||||
|               bottomChips={filterHasAttributes( | ||||
|                 item.attributes.categories?.data, | ||||
|                 ["attributes"] as const | ||||
|               ).map((category) => category.attributes.short)} | ||||
|             /> | ||||
|           )} | ||||
|           renderWhenEmpty={() => ( | ||||
|             <ContentPlaceholder | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint