Added testing and display recorders info

This commit is contained in:
DrMint 2022-02-27 08:17:58 +01:00
parent 1e85f99d55
commit a085a8b95c
25 changed files with 914 additions and 162 deletions

4
.gitignore vendored
View File

@ -1,5 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
/testing_logs/*
# dependencies # dependencies
/node_modules /node_modules
/.pnp /.pnp
@ -35,3 +37,5 @@ yarn-error.log*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
!.gitkeep

32
package-lock.json generated
View File

@ -4,6 +4,7 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "accords-library.com",
"dependencies": { "dependencies": {
"@fontsource/material-icons": "^4.5.2", "@fontsource/material-icons": "^4.5.2",
"@fontsource/material-icons-rounded": "^4.5.2", "@fontsource/material-icons-rounded": "^4.5.2",
@ -21,6 +22,7 @@
"@tailwindcss/typography": "^0.5.2", "@tailwindcss/typography": "^0.5.2",
"@types/node": "17.0.18", "@types/node": "17.0.18",
"@types/react": "17.0.39", "@types/react": "17.0.39",
"@types/react-dom": "^17.0.11",
"eslint": "8.9.0", "eslint": "8.9.0",
"eslint-config-next": "12.1.0", "eslint-config-next": "12.1.0",
"tailwindcss": "^3.0.23", "tailwindcss": "^3.0.23",
@ -496,6 +498,15 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-dom": {
"version": "17.0.11",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz",
"integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/scheduler": { "node_modules/@types/scheduler": {
"version": "0.16.2", "version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
@ -976,7 +987,6 @@
"dependencies": { "dependencies": {
"anymatch": "~3.1.2", "anymatch": "~3.1.2",
"braces": "~3.0.2", "braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2", "glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
@ -2510,17 +2520,6 @@
"integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==", "integrity": "sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==",
"dependencies": { "dependencies": {
"@next/env": "12.1.0", "@next/env": "12.1.0",
"@next/swc-android-arm64": "12.1.0",
"@next/swc-darwin-arm64": "12.1.0",
"@next/swc-darwin-x64": "12.1.0",
"@next/swc-linux-arm-gnueabihf": "12.1.0",
"@next/swc-linux-arm64-gnu": "12.1.0",
"@next/swc-linux-arm64-musl": "12.1.0",
"@next/swc-linux-x64-gnu": "12.1.0",
"@next/swc-linux-x64-musl": "12.1.0",
"@next/swc-win32-arm64-msvc": "12.1.0",
"@next/swc-win32-ia32-msvc": "12.1.0",
"@next/swc-win32-x64-msvc": "12.1.0",
"caniuse-lite": "^1.0.30001283", "caniuse-lite": "^1.0.30001283",
"postcss": "8.4.5", "postcss": "8.4.5",
"styled-jsx": "5.0.0", "styled-jsx": "5.0.0",
@ -3986,6 +3985,15 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"@types/react-dom": {
"version": "17.0.11",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz",
"integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/scheduler": { "@types/scheduler": {
"version": "0.16.2", "version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",

View File

@ -5,8 +5,7 @@
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint"
"text": "NODE_ENV=test next build"
}, },
"dependencies": { "dependencies": {
"@fontsource/material-icons": "^4.5.2", "@fontsource/material-icons": "^4.5.2",
@ -25,6 +24,7 @@
"@tailwindcss/typography": "^0.5.2", "@tailwindcss/typography": "^0.5.2",
"@types/node": "17.0.18", "@types/node": "17.0.18",
"@types/react": "17.0.39", "@types/react": "17.0.39",
"@types/react-dom": "^17.0.11",
"eslint": "8.9.0", "eslint": "8.9.0",
"eslint-config-next": "12.1.0", "eslint-config-next": "12.1.0",
"tailwindcss": "^3.0.23", "tailwindcss": "^3.0.23",

View File

@ -1,11 +1,10 @@
{ {
"name": "Accord's Library",
"short_name": "Accord's Lib",
"start_url": ".",
"display": "standalone",
"background_color": "#FFEDD8", "background_color": "#FFEDD8",
"theme_color": "#FFEDD8", "categories": ["books", "education", "entertainment", "news", "games"],
"description": "Accord's Library aims at gathering and archiving all of Yoko Taros work. Yoko Taro is a Japanese video game director and scenario writer.", "description": "Accord's Library aims at gathering and archiving all of Yoko Taros work. Yoko Taro is a Japanese video game director and scenario writer.",
"dir": "auto",
"display": "fullscreen",
"icons": [ "icons": [
{ {
"src": "/android-chrome-192x192.png", "src": "/android-chrome-192x192.png",
@ -17,5 +16,44 @@
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
} }
] ],
"name": "Accord's Library",
"short_name": "Accord's Lib",
"start_url": ".",
"shortcuts": [
{
"name": "Library",
"url": "/library",
"description": "Browse all physical and digital media"
},
{
"name": "Contents",
"url": "/contents",
"description": "Explore all content and filter by type or category"
},
{
"name": "Wiki",
"url": "/wiki",
"description": "An encyclopedia for everything related to DrakeNieR"
},
{
"name": "Chronicles",
"url": "/chronicles",
"description": "Experience all events and content in chronological order"
},
{
"name": "News",
"url": "/news",
"description": "All the latest info"
},
{
"name": "Gallery",
"url": "/gallery",
"description": "Thousands of offcial artworks"
}
],
"theme_color": "#FFEDD8"
} }

View File

@ -1 +1 @@
npx next build npx next build

2
run_accords_testing.sh Executable file
View File

@ -0,0 +1,2 @@
NODE_ENV=test
npx next build |& tee ./testing_logs/$(date +"%Y-%m-%d---%H-%M-%S").log

View File

@ -7,7 +7,7 @@ import Head from "next/head";
import { useSwipeable } from "react-swipeable"; import { useSwipeable } from "react-swipeable";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Button from "components/Button"; import Button from "components/Button";
import { getOgImage, prettyLanguage } from "queries/helpers"; import { getOgImage, OgImage, prettyLanguage } from "queries/helpers";
import { useMediaCoarse, useMediaMobile } from "hooks/useMediaQuery"; import { useMediaCoarse, useMediaMobile } from "hooks/useMediaQuery";
import ReactTooltip from "react-tooltip"; import ReactTooltip from "react-tooltip";
import { useAppLayout } from "contexts/AppLayoutContext"; import { useAppLayout } from "contexts/AppLayoutContext";
@ -22,10 +22,10 @@ type AppLayoutProps = {
navTitle: string; navTitle: string;
thumbnail?: StrapiImage; thumbnail?: StrapiImage;
description?: string; description?: string;
extra?: React.ReactNode;
}; };
export default function AppLayout(props: AppLayoutProps): JSX.Element { export default function AppLayout(props: AppLayoutProps): JSX.Element {
const titlePrefix = "Accords Library";
const router = useRouter(); const router = useRouter();
const isMobile = useMediaMobile(); const isMobile = useMediaMobile();
const isCoarse = useMediaCoarse(); const isCoarse = useMediaCoarse();
@ -75,9 +75,21 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
const turnSubIntoContent = props.subPanel && !props.contentPanel; const turnSubIntoContent = props.subPanel && !props.contentPanel;
const ogImage = getOgImage(ImageQuality.Og, props.thumbnail); const titlePrefix = "Accords Library";
const metaImage: OgImage = props.thumbnail
? getOgImage(ImageQuality.Og, props.thumbnail)
: {
image: "/default_og.jpg",
width: 1200,
height: 630,
alt: "Accord's Library Logo",
};
const ogTitle = props.title ? props.title : props.navTitle; const ogTitle = props.title ? props.title : props.navTitle;
const metaDescription = props.description
? props.description
: "Accord's Library aims at gathering and archiving all of Yoko Taros work. Yoko Taro is a Japanese video game director and scenario writer.";
return ( return (
<div className={appLayout.darkMode ? "set-theme-dark" : "set-theme-light"}> <div className={appLayout.darkMode ? "set-theme-dark" : "set-theme-light"}>
<div <div
@ -94,36 +106,26 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
{props.description && ( {props.description && (
<> <>
<meta name="description" content={props.description} /> <meta name="description" content={metaDescription} />
<meta <meta name="twitter:description" content={metaDescription}></meta>
name="twitter:description"
content={props.description}
></meta>
</> </>
)} )}
{ogImage && ( <meta property="og:image" content={metaImage.image}></meta>
<> <meta property="og:image:secure_url" content={metaImage.image}></meta>
<meta property="og:image" content={ogImage.image}></meta> <meta
<meta property="og:image:width"
property="og:image:secure_url" content={metaImage.width.toString()}
content={ogImage.image} ></meta>
></meta> <meta
<meta property="og:image:height"
property="og:image:width" content={metaImage.height.toString()}
content={ogImage.width.toString()} ></meta>
></meta> <meta property="og:image:alt" content={metaImage.alt}></meta>
<meta <meta property="og:image:type" content="image/jpeg"></meta>
property="og:image:height" <meta name="twitter:card" content="summary_large_image"></meta>
content={ogImage.height.toString()}
></meta>
<meta property="og:image:alt" content={ogImage.alt}></meta>
<meta property="og:image:type" content="image/jpeg"></meta>
<meta name="twitter:card" content="summary_large_image"></meta>
<meta name="twitter:image" content={ogImage.image}></meta> <meta name="twitter:image" content={metaImage.image}></meta>
</>
)}
</Head> </Head>
{/* Navbar */} {/* Navbar */}
@ -263,6 +265,8 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
className="drop-shadow-shade-xl !opacity-100 !bg-light !rounded-lg after:!border-r-light text-left !text-black" className="drop-shadow-shade-xl !opacity-100 !bg-light !rounded-lg after:!border-r-light text-left !text-black"
/> />
</div> </div>
{props.extra}
</div> </div>
); );
} }

View File

@ -1,12 +1,25 @@
type ChipProps = { type ChipProps = {
className?: string; className?: string;
children: React.ReactChild | React.ReactChild[]; children: React.ReactChild | React.ReactChild[];
"data-tip"?: string;
"data-for"?: string;
"data-html"?: boolean;
"data-multiline"?: boolean;
}; };
export default function Chip(props: ChipProps): JSX.Element { export default function Chip(props: ChipProps): JSX.Element {
return ( return (
<div <div
className={`grid place-content-center place-items-center text-xs pb-[0.14rem] px-1.5 border-[1px] rounded-full opacity-70 ${props.className}`} className={`grid place-content-center place-items-center text-xs pb-[0.14rem] px-1.5 border-[1px] rounded-full opacity-70 transition-[color,_opacity,border-color] ${
props.className
} ${
props["data-tip"] &&
"hover:text-dark hover:border-dark hover:opacity-100"
}`}
data-tip={props["data-tip"]}
data-for={props["data-for"]}
data-html={props["data-html"]}
data-multiline={props["data-multiline"]}
> >
{props.children} {props.children}
</div> </div>

View File

@ -87,7 +87,7 @@ export default function ChronologyItemComponent(
<div className="place-items-start place-content-start grid grid-flow-col gap-2"> <div className="place-items-start place-content-start grid grid-flow-col gap-2">
{translation.status !== {translation.status !==
Enum_Componenttranslationschronologyitem_Status.Done && ( Enum_Componenttranslationschronologyitem_Status.Done && (
<div <Chip
data-tip={ data-tip={
translation.status === translation.status ===
Enum_Componenttranslationschronologyitem_Status.Incomplete Enum_Componenttranslationschronologyitem_Status.Incomplete
@ -102,8 +102,8 @@ export default function ChronologyItemComponent(
} }
data-for={"ChronologyTooltip"} data-for={"ChronologyTooltip"}
> >
<Chip>{translation.status}</Chip> {translation.status}
</div> </Chip>
)} )}
{translation.title ? <h3>{translation.title}</h3> : ""} {translation.title ? <h3>{translation.title}</h3> : ""}
</div> </div>

View File

@ -5,6 +5,7 @@ import {
import { prettySlug } from "queries/helpers"; import { prettySlug } from "queries/helpers";
import Button from "components/Button"; import Button from "components/Button";
import Img, { ImageQuality } from "components/Img"; import Img, { ImageQuality } from "components/Img";
import InsetBox from "components/InsetBox";
export type ThumbnailHeaderProps = { export type ThumbnailHeaderProps = {
content: { content: {
@ -25,7 +26,7 @@ export default function ThumbnailHeader(
return ( return (
<> <>
<div className="grid place-items-center gap-12 mb-12"> <div className="grid place-items-center gap-12 mb-12">
<div className="drop-shadow-shade-lg"> <div className="drop-shadow-shade-lg">
{content.thumbnail.data ? ( {content.thumbnail.data ? (
<Img <Img
@ -76,6 +77,9 @@ export default function ThumbnailHeader(
"" ""
)} )}
</div> </div>
{content.titles.length > 0 && content.titles[0].description && (
<InsetBox className="mt-8">{content.titles[0].description}</InsetBox>
)}
</> </>
); );
} }

View File

@ -56,6 +56,7 @@ type ImgProps = {
layout?: ImageProps["layout"]; layout?: ImageProps["layout"];
objectFit?: ImageProps["objectFit"]; objectFit?: ImageProps["objectFit"];
priority?: ImageProps["priority"]; priority?: ImageProps["priority"];
rawImg?: boolean;
}; };
export default function Img(props: ImgProps): JSX.Element { export default function Img(props: ImgProps): JSX.Element {
@ -64,20 +65,37 @@ export default function Img(props: ImgProps): JSX.Element {
props.image.height, props.image.height,
props.quality ? props.quality : ImageQuality.Small props.quality ? props.quality : ImageQuality.Small
); );
return (
<Image if (props.rawImg) {
className={props.className} return (
src={getAssetURL( // eslint-disable-next-line @next/next/no-img-element
props.image.url, <img
props.quality ? props.quality : ImageQuality.Small className={props.className}
)} src={getAssetURL(
alt={props.alt ? props.alt : props.image.alternativeText} props.image.url,
width={props.layout === "fill" ? undefined : imgSize.width} props.quality ? props.quality : ImageQuality.Small
height={props.layout === "fill" ? undefined : imgSize.height} )}
layout={props.layout} alt={props.alt ? props.alt : props.image.alternativeText}
objectFit={props.objectFit} width={imgSize.width}
priority={props.priority} height={imgSize.height}
unoptimized />
/> );
); } else {
return (
<Image
className={props.className}
src={getAssetURL(
props.image.url,
props.quality ? props.quality : ImageQuality.Small
)}
alt={props.alt ? props.alt : props.image.alternativeText}
width={props.layout === "fill" ? undefined : imgSize.width}
height={props.layout === "fill" ? undefined : imgSize.height}
layout={props.layout}
objectFit={props.objectFit}
priority={props.priority}
unoptimized
/>
);
}
} }

View File

@ -1,6 +1,7 @@
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Link from "next/link"; import Link from "next/link";
import { MouseEventHandler } from "react"; import { MouseEventHandler } from "react";
import ReactDOMServer from "react-dom/server";
type NavOptionProps = { type NavOptionProps = {
url: string; url: string;
@ -29,16 +30,14 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
onClick={props.onClick} onClick={props.onClick}
data-html data-html
data-multiline data-multiline
data-tip={` data-tip={ReactDOMServer.renderToStaticMarkup(
<div class="px-4 py-3"> <div className="px-4 py-3">
<h3 class="text-2xl">${props.title}</h3> <h3 className="text-2xl">{props.title}</h3>
${ {props.subtitle && (
props.subtitle <p className="max-w-[10rem]">{props.subtitle}</p>
? `<p class="max-w-[10rem]">${props.subtitle}</p>` )}
: ""
}
</div> </div>
`} )}
data-for={props.tooltipId} data-for={props.tooltipId}
className={`grid grid-flow-col grid-cols-[auto] auto-cols-fr justify-center ${ className={`grid grid-flow-col grid-cols-[auto] auto-cols-fr justify-center ${
props.icon ? "text-left" : "text-center" props.icon ? "text-left" : "text-center"

View File

@ -183,6 +183,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
)} )}
</p> </p>
<a <a
aria-label="Read more about the license we use for this website"
className="transition-[filter] colorize-black hover:colorize-dark" className="transition-[filter] colorize-black hover:colorize-dark"
href="https://creativecommons.org/licenses/by-sa/4.0/" href="https://creativecommons.org/licenses/by-sa/4.0/"
> >
@ -201,12 +202,14 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
</p> </p>
<div className="mt-12 mb-4 grid h-4 grid-flow-col place-content-center gap-8"> <div className="mt-12 mb-4 grid h-4 grid-flow-col place-content-center gap-8">
<a <a
aria-label="Browse our GitHub repository, which include this website source code"
className="transition-colors [mask:url('/icons/github-brands.svg')] ![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center] w-10 aspect-square bg-black hover:bg-dark" className="transition-colors [mask:url('/icons/github-brands.svg')] ![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center] w-10 aspect-square bg-black hover:bg-dark"
href="https://github.com/Accords-Library" href="https://github.com/Accords-Library"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
></a> ></a>
<a <a
aria-label="Join our Discord server!"
className="transition-colors [mask:url('/icons/discord-brands.svg')] ![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center] w-10 aspect-square bg-black hover:bg-dark" className="transition-colors [mask:url('/icons/discord-brands.svg')] ![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center] w-10 aspect-square bg-black hover:bg-dark"
href="/discord" href="/discord"
target="_blank" target="_blank"

View File

@ -0,0 +1,60 @@
import Chip from "components/Chip";
import { GetContentTextQuery } from "graphql/operations-types";
import Img, { ImageQuality } from "./Img";
import ReactDOMServer from "react-dom/server";
type RecorderChipProps = {
className?: string;
recorder: GetContentTextQuery["contents"]["data"][number]["attributes"]["text_set"][number]["transcribers"]["data"][number];
};
export default function RecorderChip(props: RecorderChipProps): JSX.Element {
const recorder = props.recorder;
return (
<Chip
key={recorder.id}
data-tip={ReactDOMServer.renderToStaticMarkup(
<div className="p-2 py-5 grid gap-2">
<div className="grid grid-flow-col gap-2 place-items-center place-content-start">
{recorder.attributes.avatar.data && (
<Img
className="w-8 rounded-full"
image={recorder.attributes.avatar.data.attributes}
quality={ImageQuality.Small}
rawImg
/>
)}
<h3 className="text-xl">{recorder.attributes.username}</h3>
</div>
{recorder.attributes.languages.data.length > 0 && (
<div className="flex flex-row flex-wrap gap-1">
<p>Languages:</p>
{recorder.attributes.languages.data.map((language) => (
<Chip key={language.attributes.code}>
{language.attributes.code.toUpperCase()}
</Chip>
))}
</div>
)}
{recorder.attributes.pronouns && (
<div className="flex flex-row flex-wrap gap-1">
<p>Pronouns:</p>
<Chip>{recorder.attributes.pronouns}</Chip>
</div>
)}
<p>
{recorder.attributes.bio.length > 0 &&
recorder.attributes.bio[0].bio}
</p>
</div>
)}
data-for={"RecordersTooltip"}
data-multiline
data-html
>
{recorder.attributes.anonymize
? `Recorder#${recorder.attributes.anonymous_code}`
: recorder.attributes.username}
</Chip>
);
}

View File

@ -73,7 +73,7 @@ query getWebsiteInterface($language_code: String) {
} }
query getEras($language_code: String) { query getEras($language_code: String) {
chronologyEras { chronologyEras(sort: "starting_year") {
data { data {
id id
attributes { attributes {
@ -730,6 +730,7 @@ query getContent($slug: String, $language_code: String) {
pre_title pre_title
title title
subtitle subtitle
description
} }
categories { categories {
data { data {
@ -817,6 +818,7 @@ query getContentText($slug: String, $language_code: String) {
pre_title pre_title
title title
subtitle subtitle
description
} }
categories { categories {
data { data {
@ -875,16 +877,28 @@ query getContentText($slug: String, $language_code: String) {
source_language { source_language {
data { data {
attributes { attributes {
name code
} }
} }
} }
transcribers { transcribers {
data { data {
id
attributes { attributes {
username username
anonymize anonymize
anonymous_code anonymous_code
pronouns
bio(filters: { language: { code: { eq: $language_code } } }) {
bio
}
languages {
data {
attributes {
code
}
}
}
avatar { avatar {
data { data {
attributes { attributes {
@ -902,10 +916,22 @@ query getContentText($slug: String, $language_code: String) {
} }
translators { translators {
data { data {
id
attributes { attributes {
username username
anonymize anonymize
anonymous_code anonymous_code
pronouns
bio(filters: { language: { code: { eq: $language_code } } }) {
bio
}
languages {
data {
attributes {
code
}
}
}
avatar { avatar {
data { data {
attributes { attributes {
@ -923,10 +949,22 @@ query getContentText($slug: String, $language_code: String) {
} }
proofreaders { proofreaders {
data { data {
id
attributes { attributes {
username username
anonymize anonymize
anonymous_code anonymous_code
pronouns
bio(filters: { language: { code: { eq: $language_code } } }) {
bio
}
languages {
data {
attributes {
code
}
}
}
avatar { avatar {
data { data {
attributes { attributes {

View File

@ -996,6 +996,7 @@ export type GetContentQuery = {
pre_title: string; pre_title: string;
title: string; title: string;
subtitle: string; subtitle: string;
description: string;
}>; }>;
categories: { categories: {
__typename: "CategoryRelationResponseCollection"; __typename: "CategoryRelationResponseCollection";
@ -1116,6 +1117,7 @@ export type GetContentTextQuery = {
pre_title: string; pre_title: string;
title: string; title: string;
subtitle: string; subtitle: string;
description: string;
}>; }>;
categories: { categories: {
__typename: "CategoryRelationResponseCollection"; __typename: "CategoryRelationResponseCollection";
@ -1194,18 +1196,34 @@ export type GetContentTextQuery = {
__typename: "LanguageEntityResponse"; __typename: "LanguageEntityResponse";
data: { data: {
__typename: "LanguageEntity"; __typename: "LanguageEntity";
attributes: { __typename: "Language"; name: string }; attributes: { __typename: "Language"; code: string };
}; };
}; };
transcribers: { transcribers: {
__typename: "RecorderRelationResponseCollection"; __typename: "RecorderRelationResponseCollection";
data: Array<{ data: Array<{
__typename: "RecorderEntity"; __typename: "RecorderEntity";
id: string;
attributes: { attributes: {
__typename: "Recorder"; __typename: "Recorder";
username: string; username: string;
anonymize: boolean; anonymize: boolean;
anonymous_code: string; anonymous_code: string;
pronouns: string;
bio: Array<{
__typename: "ComponentTranslationsBio";
bio: string;
}>;
languages: {
__typename: "LanguageRelationResponseCollection";
data: Array<{
__typename: "LanguageEntity";
attributes: {
__typename: "Language";
code: string;
};
}>;
};
avatar: { avatar: {
__typename: "UploadFileEntityResponse"; __typename: "UploadFileEntityResponse";
data: { data: {
@ -1228,11 +1246,27 @@ export type GetContentTextQuery = {
__typename: "RecorderRelationResponseCollection"; __typename: "RecorderRelationResponseCollection";
data: Array<{ data: Array<{
__typename: "RecorderEntity"; __typename: "RecorderEntity";
id: string;
attributes: { attributes: {
__typename: "Recorder"; __typename: "Recorder";
username: string; username: string;
anonymize: boolean; anonymize: boolean;
anonymous_code: string; anonymous_code: string;
pronouns: string;
bio: Array<{
__typename: "ComponentTranslationsBio";
bio: string;
}>;
languages: {
__typename: "LanguageRelationResponseCollection";
data: Array<{
__typename: "LanguageEntity";
attributes: {
__typename: "Language";
code: string;
};
}>;
};
avatar: { avatar: {
__typename: "UploadFileEntityResponse"; __typename: "UploadFileEntityResponse";
data: { data: {
@ -1255,11 +1289,27 @@ export type GetContentTextQuery = {
__typename: "RecorderRelationResponseCollection"; __typename: "RecorderRelationResponseCollection";
data: Array<{ data: Array<{
__typename: "RecorderEntity"; __typename: "RecorderEntity";
id: string;
attributes: { attributes: {
__typename: "Recorder"; __typename: "Recorder";
username: string; username: string;
anonymize: boolean; anonymize: boolean;
anonymous_code: string; anonymous_code: string;
pronouns: string;
bio: Array<{
__typename: "ComponentTranslationsBio";
bio: string;
}>;
languages: {
__typename: "LanguageRelationResponseCollection";
data: Array<{
__typename: "LanguageEntity";
attributes: {
__typename: "Language";
code: string;
};
}>;
};
avatar: { avatar: {
__typename: "UploadFileEntityResponse"; __typename: "UploadFileEntityResponse";
data: { data: {

View File

@ -398,6 +398,23 @@ type ComponentCollectionsComponentLibraryObiBelt {
inside_full: UploadFileEntityResponse inside_full: UploadFileEntityResponse
} }
input ComponentCollectionsComponentTitlesFiltersInput {
title: StringFilterInput
and: [ComponentCollectionsComponentTitlesFiltersInput]
or: [ComponentCollectionsComponentTitlesFiltersInput]
not: ComponentCollectionsComponentTitlesFiltersInput
}
input ComponentCollectionsComponentTitlesInput {
id: ID
title: String
}
type ComponentCollectionsComponentTitles {
id: ID!
title: String!
}
input ComponentCollectionsComponentWeaponStoryFiltersInput { input ComponentCollectionsComponentWeaponStoryFiltersInput {
source: SourceFiltersInput source: SourceFiltersInput
categories: CategoryFiltersInput categories: CategoryFiltersInput
@ -428,6 +445,17 @@ type ComponentCollectionsComponentWeaponStory {
): CategoryRelationResponseCollection ): CategoryRelationResponseCollection
} }
type ComponentCollectionsComponentWikiDefinition {
id: ID!
definition: String
categories(
filters: CategoryFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): CategoryRelationResponseCollection
source: SourceEntityResponse
}
type ComponentMetadataAudio { type ComponentMetadataAudio {
id: ID! id: ID!
subtype: AudioSubtypeEntityResponse subtype: AudioSubtypeEntityResponse
@ -771,6 +799,69 @@ type ComponentSetsVideoSet {
notes: String notes: String
} }
enum ENUM_COMPONENTSETSWIKISET_STATUS {
Incomplete
Draft
Review
Done
}
input ComponentSetsWikiSetFiltersInput {
language: LanguageFiltersInput
status: StringFilterInput
summary: StringFilterInput
body: StringFilterInput
source_language: LanguageFiltersInput
authors: RecorderFiltersInput
translators: RecorderFiltersInput
proofreaders: RecorderFiltersInput
and: [ComponentSetsWikiSetFiltersInput]
or: [ComponentSetsWikiSetFiltersInput]
not: ComponentSetsWikiSetFiltersInput
}
input ComponentSetsWikiSetInput {
id: ID
language: ID
status: ENUM_COMPONENTSETSWIKISET_STATUS
titles: [ComponentCollectionsComponentTitlesInput]
summary: String
body: String
source_language: ID
authors: [ID]
translators: [ID]
proofreaders: [ID]
}
type ComponentSetsWikiSet {
id: ID!
language: LanguageEntityResponse
status: ENUM_COMPONENTSETSWIKISET_STATUS!
titles(
filters: ComponentCollectionsComponentTitlesFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): [ComponentCollectionsComponentTitles]
summary: String
body: String
source_language: LanguageEntityResponse
authors(
filters: RecorderFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): RecorderRelationResponseCollection
translators(
filters: RecorderFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): RecorderRelationResponseCollection
proofreaders(
filters: RecorderFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): RecorderRelationResponseCollection
}
type ComponentSourceUrlSource { type ComponentSourceUrlSource {
id: ID! id: ID!
title: String title: String
@ -794,9 +885,30 @@ type ComponentTranslationsAudioSets {
credits: ComponentBasicsCredits! credits: ComponentBasicsCredits!
} }
input ComponentTranslationsBioFiltersInput {
language: LanguageFiltersInput
bio: StringFilterInput
and: [ComponentTranslationsBioFiltersInput]
or: [ComponentTranslationsBioFiltersInput]
not: ComponentTranslationsBioFiltersInput
}
input ComponentTranslationsBioInput {
id: ID
language: ID
bio: String
}
type ComponentTranslationsBio {
id: ID!
language: LanguageEntityResponse
bio: String
}
input ComponentTranslationsChronologyEraFiltersInput { input ComponentTranslationsChronologyEraFiltersInput {
title: StringFilterInput title: StringFilterInput
language: LanguageFiltersInput language: LanguageFiltersInput
description: StringFilterInput
and: [ComponentTranslationsChronologyEraFiltersInput] and: [ComponentTranslationsChronologyEraFiltersInput]
or: [ComponentTranslationsChronologyEraFiltersInput] or: [ComponentTranslationsChronologyEraFiltersInput]
not: ComponentTranslationsChronologyEraFiltersInput not: ComponentTranslationsChronologyEraFiltersInput
@ -806,12 +918,14 @@ input ComponentTranslationsChronologyEraInput {
id: ID id: ID
title: String title: String
language: ID language: ID
description: String
} }
type ComponentTranslationsChronologyEra { type ComponentTranslationsChronologyEra {
id: ID! id: ID!
title: String title: String!
language: LanguageEntityResponse language: LanguageEntityResponse
description: String
} }
enum ENUM_COMPONENTTRANSLATIONSCHRONOLOGYITEM_STATUS { enum ENUM_COMPONENTTRANSLATIONSCHRONOLOGYITEM_STATUS {
@ -1017,6 +1131,7 @@ input ComponentTranslationsTitleFiltersInput {
title: StringFilterInput title: StringFilterInput
subtitle: StringFilterInput subtitle: StringFilterInput
pre_title: StringFilterInput pre_title: StringFilterInput
description: StringFilterInput
and: [ComponentTranslationsTitleFiltersInput] and: [ComponentTranslationsTitleFiltersInput]
or: [ComponentTranslationsTitleFiltersInput] or: [ComponentTranslationsTitleFiltersInput]
not: ComponentTranslationsTitleFiltersInput not: ComponentTranslationsTitleFiltersInput
@ -1028,6 +1143,7 @@ input ComponentTranslationsTitleInput {
title: String title: String
subtitle: String subtitle: String
pre_title: String pre_title: String
description: String
} }
type ComponentTranslationsTitle { type ComponentTranslationsTitle {
@ -1036,6 +1152,7 @@ type ComponentTranslationsTitle {
title: String! title: String!
subtitle: String subtitle: String
pre_title: String pre_title: String
description: String
} }
enum ENUM_COMPONENTTRANSLATIONSVIDEOSETS_STATUS { enum ENUM_COMPONENTTRANSLATIONSVIDEOSETS_STATUS {
@ -2051,6 +2168,7 @@ input RecorderFiltersInput {
anonymize: BooleanFilterInput anonymize: BooleanFilterInput
anonymous_code: StringFilterInput anonymous_code: StringFilterInput
languages: LanguageFiltersInput languages: LanguageFiltersInput
pronouns: StringFilterInput
createdAt: DateTimeFilterInput createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput updatedAt: DateTimeFilterInput
and: [RecorderFiltersInput] and: [RecorderFiltersInput]
@ -2064,6 +2182,8 @@ input RecorderInput {
anonymous_code: String anonymous_code: String
avatar: ID avatar: ID
languages: [ID] languages: [ID]
pronouns: String
bio: [ComponentTranslationsBioInput]
} }
type Recorder { type Recorder {
@ -2076,6 +2196,12 @@ type Recorder {
pagination: PaginationArg = {} pagination: PaginationArg = {}
sort: [String] = [] sort: [String] = []
): LanguageRelationResponseCollection ): LanguageRelationResponseCollection
pronouns: String
bio(
filters: ComponentTranslationsBioFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): [ComponentTranslationsBio]
createdAt: DateTime createdAt: DateTime
updatedAt: DateTime updatedAt: DateTime
} }
@ -2359,12 +2485,7 @@ input WebsiteInterfaceFiltersInput {
language: LanguageFiltersInput language: LanguageFiltersInput
main_library: StringFilterInput main_library: StringFilterInput
main_library_description: StringFilterInput main_library_description: StringFilterInput
main_hub: StringFilterInput
main_hub_description: StringFilterInput
main_chronology: StringFilterInput
main_chronology_description: StringFilterInput
main_news: StringFilterInput main_news: StringFilterInput
main_data: StringFilterInput
main_merch: StringFilterInput main_merch: StringFilterInput
main_gallery: StringFilterInput main_gallery: StringFilterInput
main_archives: StringFilterInput main_archives: StringFilterInput
@ -2394,13 +2515,6 @@ input WebsiteInterfaceFiltersInput {
global_price: StringFilterInput global_price: StringFilterInput
library_item_physical_size: StringFilterInput library_item_physical_size: StringFilterInput
library_item_type_information: StringFilterInput library_item_type_information: StringFilterInput
chronology_description: StringFilterInput
chronology_timelines: StringFilterInput
chronology_timelines_description: StringFilterInput
chronology_overview: StringFilterInput
chronology_overview_description: StringFilterInput
chronology_walkthrough: StringFilterInput
chronology_walkthrough_description: StringFilterInput
library_item_front_matter: StringFilterInput library_item_front_matter: StringFilterInput
library_item_back_matter: StringFilterInput library_item_back_matter: StringFilterInput
library_item_type_textual: StringFilterInput library_item_type_textual: StringFilterInput
@ -2419,6 +2533,21 @@ input WebsiteInterfaceFiltersInput {
global_hardcover: StringFilterInput global_hardcover: StringFilterInput
global_left_to_right: StringFilterInput global_left_to_right: StringFilterInput
global_right_to_left: StringFilterInput global_right_to_left: StringFilterInput
main_wiki: StringFilterInput
main_wiki_description: StringFilterInput
main_chronicles: StringFilterInput
main_chronicles_description: StringFilterInput
library_items: StringFilterInput
library_items_description: StringFilterInput
library_content: StringFilterInput
library_content_description: StringFilterInput
wiki_description: StringFilterInput
news_description: StringFilterInput
chronicles_description: StringFilterInput
gallery_description: StringFilterInput
archives_description: StringFilterInput
about_us_description: StringFilterInput
merch_description: StringFilterInput
createdAt: DateTimeFilterInput createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput updatedAt: DateTimeFilterInput
and: [WebsiteInterfaceFiltersInput] and: [WebsiteInterfaceFiltersInput]
@ -2430,12 +2559,7 @@ input WebsiteInterfaceInput {
language: ID language: ID
main_library: String main_library: String
main_library_description: String main_library_description: String
main_hub: String
main_hub_description: String
main_chronology: String
main_chronology_description: String
main_news: String main_news: String
main_data: String
main_merch: String main_merch: String
main_gallery: String main_gallery: String
main_archives: String main_archives: String
@ -2465,13 +2589,6 @@ input WebsiteInterfaceInput {
global_price: String global_price: String
library_item_physical_size: String library_item_physical_size: String
library_item_type_information: String library_item_type_information: String
chronology_description: String
chronology_timelines: String
chronology_timelines_description: String
chronology_overview: String
chronology_overview_description: String
chronology_walkthrough: String
chronology_walkthrough_description: String
library_item_front_matter: String library_item_front_matter: String
library_item_back_matter: String library_item_back_matter: String
library_item_type_textual: String library_item_type_textual: String
@ -2490,18 +2607,28 @@ input WebsiteInterfaceInput {
global_hardcover: String global_hardcover: String
global_left_to_right: String global_left_to_right: String
global_right_to_left: String global_right_to_left: String
main_wiki: String
main_wiki_description: String
main_chronicles: String
main_chronicles_description: String
library_items: String
library_items_description: String
library_content: String
library_content_description: String
wiki_description: String
news_description: String
chronicles_description: String
gallery_description: String
archives_description: String
about_us_description: String
merch_description: String
} }
type WebsiteInterface { type WebsiteInterface {
language: LanguageEntityResponse language: LanguageEntityResponse
main_library: String main_library: String
main_library_description: String main_library_description: String
main_hub: String
main_hub_description: String
main_chronology: String
main_chronology_description: String
main_news: String main_news: String
main_data: String
main_merch: String main_merch: String
main_gallery: String main_gallery: String
main_archives: String main_archives: String
@ -2531,13 +2658,6 @@ type WebsiteInterface {
global_price: String global_price: String
library_item_physical_size: String library_item_physical_size: String
library_item_type_information: String library_item_type_information: String
chronology_description: String
chronology_timelines: String
chronology_timelines_description: String
chronology_overview: String
chronology_overview_description: String
chronology_walkthrough: String
chronology_walkthrough_description: String
library_item_front_matter: String library_item_front_matter: String
library_item_back_matter: String library_item_back_matter: String
library_item_type_textual: String library_item_type_textual: String
@ -2556,6 +2676,21 @@ type WebsiteInterface {
global_hardcover: String global_hardcover: String
global_left_to_right: String global_left_to_right: String
global_right_to_left: String global_right_to_left: String
main_wiki: String
main_wiki_description: String
main_chronicles: String
main_chronicles_description: String
library_items: String
library_items_description: String
library_content: String
library_content_description: String
wiki_description: String
news_description: String
chronicles_description: String
gallery_description: String
archives_description: String
about_us_description: String
merch_description: String
createdAt: DateTime createdAt: DateTime
updatedAt: DateTime updatedAt: DateTime
} }
@ -2574,6 +2709,91 @@ type WebsiteInterfaceEntityResponseCollection {
meta: ResponseCollectionMeta! meta: ResponseCollectionMeta!
} }
input WikiPageFiltersInput {
id: IDFilterInput
slug: StringFilterInput
type: WikiPageTypeFiltersInput
createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput
and: [WikiPageFiltersInput]
or: [WikiPageFiltersInput]
not: WikiPageFiltersInput
}
input WikiPageInput {
slug: String
thumbnail: ID
wiki_set: [ComponentSetsWikiSetInput]
type: ID
}
type WikiPage {
slug: String!
thumbnail: UploadFileEntityResponse
wiki_set(
filters: ComponentSetsWikiSetFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): [ComponentSetsWikiSet]
type: WikiPageTypeEntityResponse
createdAt: DateTime
updatedAt: DateTime
}
type WikiPageEntity {
id: ID
attributes: WikiPage
}
type WikiPageEntityResponse {
data: WikiPageEntity
}
type WikiPageEntityResponseCollection {
data: [WikiPageEntity!]!
meta: ResponseCollectionMeta!
}
input WikiPageTypeFiltersInput {
id: IDFilterInput
slug: StringFilterInput
createdAt: DateTimeFilterInput
updatedAt: DateTimeFilterInput
and: [WikiPageTypeFiltersInput]
or: [WikiPageTypeFiltersInput]
not: WikiPageTypeFiltersInput
}
input WikiPageTypeInput {
slug: String
titles: [ComponentTranslationsSimpleTitleInput]
}
type WikiPageType {
slug: String!
titles(
filters: ComponentTranslationsSimpleTitleFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): [ComponentTranslationsSimpleTitle]
createdAt: DateTime
updatedAt: DateTime
}
type WikiPageTypeEntity {
id: ID
attributes: WikiPageType
}
type WikiPageTypeEntityResponse {
data: WikiPageTypeEntity
}
type WikiPageTypeEntityResponseCollection {
data: [WikiPageTypeEntity!]!
meta: ResponseCollectionMeta!
}
union GenericMorph = union GenericMorph =
ComponentBasicsCredits ComponentBasicsCredits
| ComponentBasicsDatepicker | ComponentBasicsDatepicker
@ -2585,7 +2805,9 @@ union GenericMorph =
| ComponentCollectionsComponentLibraryDustJacket | ComponentCollectionsComponentLibraryDustJacket
| ComponentCollectionsComponentLibraryImages | ComponentCollectionsComponentLibraryImages
| ComponentCollectionsComponentLibraryObiBelt | ComponentCollectionsComponentLibraryObiBelt
| ComponentCollectionsComponentTitles
| ComponentCollectionsComponentWeaponStory | ComponentCollectionsComponentWeaponStory
| ComponentCollectionsComponentWikiDefinition
| ComponentMetadataAudio | ComponentMetadataAudio
| ComponentMetadataBooks | ComponentMetadataBooks
| ComponentMetadataGame | ComponentMetadataGame
@ -2606,8 +2828,10 @@ union GenericMorph =
| ComponentSetsScanSet | ComponentSetsScanSet
| ComponentSetsTextSet | ComponentSetsTextSet
| ComponentSetsVideoSet | ComponentSetsVideoSet
| ComponentSetsWikiSet
| ComponentSourceUrlSource | ComponentSourceUrlSource
| ComponentTranslationsAudioSets | ComponentTranslationsAudioSets
| ComponentTranslationsBio
| ComponentTranslationsChronologyEra | ComponentTranslationsChronologyEra
| ComponentTranslationsChronologyItem | ComponentTranslationsChronologyItem
| ComponentTranslationsGlossaryDefinition | ComponentTranslationsGlossaryDefinition
@ -2648,6 +2872,8 @@ union GenericMorph =
| WeaponStoryGroup | WeaponStoryGroup
| WeaponStoryType | WeaponStoryType
| WebsiteInterface | WebsiteInterface
| WikiPage
| WikiPageType
input FileInfoInput { input FileInfoInput {
name: String name: String
@ -2814,6 +3040,18 @@ type Query {
pagination: PaginationArg = {} pagination: PaginationArg = {}
sort: [String] = [] sort: [String] = []
): WebsiteInterfaceEntityResponseCollection ): WebsiteInterfaceEntityResponseCollection
wikiPage(id: ID): WikiPageEntityResponse
wikiPages(
filters: WikiPageFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): WikiPageEntityResponseCollection
wikiPageType(id: ID): WikiPageTypeEntityResponse
wikiPageTypes(
filters: WikiPageTypeFiltersInput
pagination: PaginationArg = {}
sort: [String] = []
): WikiPageTypeEntityResponseCollection
} }
type Mutation { type Mutation {
@ -2939,6 +3177,15 @@ type Mutation {
data: WebsiteInterfaceInput! data: WebsiteInterfaceInput!
): WebsiteInterfaceEntityResponse ): WebsiteInterfaceEntityResponse
deleteWebsiteInterface(id: ID!): WebsiteInterfaceEntityResponse deleteWebsiteInterface(id: ID!): WebsiteInterfaceEntityResponse
createWikiPage(data: WikiPageInput!): WikiPageEntityResponse
updateWikiPage(id: ID!, data: WikiPageInput!): WikiPageEntityResponse
deleteWikiPage(id: ID!): WikiPageEntityResponse
createWikiPageType(data: WikiPageTypeInput!): WikiPageTypeEntityResponse
updateWikiPageType(
id: ID!
data: WikiPageTypeInput!
): WikiPageTypeEntityResponse
deleteWikiPageType(id: ID!): WikiPageTypeEntityResponse
upload( upload(
refId: ID refId: ID
ref: String ref: String

View File

@ -39,7 +39,16 @@ class MyDocument extends Document {
<meta name="application-name" content="Accord's Library" /> <meta name="application-name" content="Accord's Library" />
<meta name="msapplication-TileColor" content="#feecd6" /> <meta name="msapplication-TileColor" content="#feecd6" />
<meta name="msapplication-TileImage" content="/mstile-144x144.png" /> <meta name="msapplication-TileImage" content="/mstile-144x144.png" />
<meta name="theme-color" content="#feecd6" /> <meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="#feecd6"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#26221e"
/>
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta <meta

View File

@ -81,6 +81,9 @@ export default function ContentIndex(props: ContentIndexProps): JSX.Element {
langui={langui} langui={langui}
contentPanel={contentPanel} contentPanel={contentPanel}
subPanel={subPanel} subPanel={subPanel}
description={
content.titles.length > 0 ? content.titles[0].description : undefined
}
/> />
); );
} }

View File

@ -5,6 +5,7 @@ import {
getWebsiteInterface, getWebsiteInterface,
} from "graphql/operations"; } from "graphql/operations";
import { import {
Enum_Componentsetstextset_Status,
GetContentTextQuery, GetContentTextQuery,
GetWebsiteInterfaceQuery, GetWebsiteInterfaceQuery,
} from "graphql/operations-types"; } from "graphql/operations-types";
@ -15,7 +16,18 @@ import ReturnButton from "components/PanelComponents/ReturnButton";
import ThumbnailHeader from "components/Content/ThumbnailHeader"; import ThumbnailHeader from "components/Content/ThumbnailHeader";
import AppLayout from "components/AppLayout"; import AppLayout from "components/AppLayout";
import Markdawn from "components/Markdown/Markdawn"; import Markdawn from "components/Markdown/Markdawn";
import { prettyinlineTitle, prettySlug } from "queries/helpers"; import {
prettyinlineTitle,
prettyLanguage,
prettySlug,
prettyTestError,
prettyTestWarning,
} from "queries/helpers";
import Button from "components/Button";
import { useRouter } from "next/router";
import Chip from "components/Chip";
import ReactTooltip from "react-tooltip";
import RecorderChip from "components/RecorderChip";
type ContentReadProps = { type ContentReadProps = {
content: GetContentTextQuery; content: GetContentTextQuery;
@ -25,6 +37,10 @@ type ContentReadProps = {
export default function ContentRead(props: ContentReadProps): JSX.Element { export default function ContentRead(props: ContentReadProps): JSX.Element {
const content = props.content.contents.data[0].attributes; const content = props.content.contents.data[0].attributes;
const langui = props.langui.websiteInterfaces.data[0].attributes; const langui = props.langui.websiteInterfaces.data[0].attributes;
const router = useRouter();
useTesting(props.content);
const subPanel = ( const subPanel = (
<SubPanel> <SubPanel>
<ReturnButton <ReturnButton
@ -32,6 +48,93 @@ export default function ContentRead(props: ContentReadProps): JSX.Element {
title={"Content"} title={"Content"}
langui={langui} langui={langui}
/> />
<HorizontalLine />
{content.text_set.length > 0 ? (
<div className="grid gap-5">
<h2 className="text-xl">
{content.text_set[0].source_language.data.attributes.code ===
router.locale
? "This content is a transcript"
: "This content is a fan-translation"}
</h2>
{content.text_set[0].source_language.data.attributes.code !==
router.locale && (
<div className="grid place-items-center gap-2">
<p className="font-headers">Source language:</p>
<Button
href={router.asPath}
locale={
content.text_set[0].source_language.data.attributes.code
}
>
{prettyLanguage(
content.text_set[0].source_language.data.attributes.code
)}
</Button>
</div>
)}
<div className="grid grid-flow-col place-items-center place-content-center gap-2">
<p className="font-headers">Status:</p>
<Chip
data-tip={
content.text_set[0].status ===
Enum_Componentsetstextset_Status.Incomplete
? "This entry is only partially translated/transcribed."
: content.text_set[0].status ===
Enum_Componentsetstextset_Status.Draft
? "This entry is just a draft. It usually means that this is a work-in-progress. Translation/transcription might be poor and/or computer-generated."
: content.text_set[0].status ===
Enum_Componentsetstextset_Status.Review
? "This entry has not yet being proofread. The content should still be accurate."
: "This entry has been checked and proofread. If you notice any translation errors or typos, please contact us so we can fix it!"
}
data-for={"StatusTooltip"}
>
{content.text_set[0].status}
</Chip>
</div>
{content.text_set[0].transcribers.data.length > 0 && (
<div>
<p className="font-headers">Transcribers:</p>
<div className="grid place-items-center place-content-center gap-2">
{content.text_set[0].transcribers.data.map((recorder) => (
<RecorderChip key={recorder.id} recorder={recorder} />
))}
</div>
</div>
)}
{content.text_set[0].translators.data.length > 0 && (
<div>
<p className="font-headers">Translators:</p>
<div className="grid place-items-center place-content-center gap-2">
{content.text_set[0].translators.data.map((recorder) => (
<RecorderChip key={recorder.id} recorder={recorder} />
))}
</div>
</div>
)}
{content.text_set[0].proofreaders.data.length > 0 && (
<div>
<p className="font-headers">Proofreaders:</p>
<div className="grid place-items-center place-content-center gap-2">
{content.text_set[0].proofreaders.data.map((recorder) => (
<RecorderChip key={recorder.id} recorder={recorder} />
))}
</div>
</div>
)}
</div>
) : (
""
)}
</SubPanel> </SubPanel>
); );
const contentPanel = ( const contentPanel = (
@ -50,18 +153,49 @@ export default function ContentRead(props: ContentReadProps): JSX.Element {
</ContentPanel> </ContentPanel>
); );
const extra = (
<>
<ReactTooltip
id="StatusTooltip"
place="top"
type="light"
effect="solid"
delayShow={50}
clickable={true}
className="drop-shadow-shade-xl !opacity-100 !bg-light !rounded-lg desktop:after:!border-t-light text-left !text-black max-w-xs"
/>
<ReactTooltip
id="RecordersTooltip"
place="top"
type="light"
effect="solid"
delayShow={100}
delayUpdate={100}
delayHide={100}
clickable={true}
className="drop-shadow-shade-xl !opacity-100 !bg-light !rounded-lg desktop:after:!border-t-light text-left !text-black max-w-[22rem]"
/>
</>
);
return ( return (
<AppLayout <AppLayout
navTitle="Contents" navTitle="Contents"
title={ title={
content.titles.length > 0 content.titles.length > 0
? prettyinlineTitle(content.titles[0].pre_title, content.titles[0].title, content.titles[0].subtitle) ? prettyinlineTitle(
content.titles[0].pre_title,
content.titles[0].title,
content.titles[0].subtitle
)
: prettySlug(content.slug) : prettySlug(content.slug)
} }
thumbnail={content.thumbnail.data.attributes} thumbnail={content.thumbnail.data.attributes}
langui={langui} langui={langui}
contentPanel={contentPanel} contentPanel={contentPanel}
subPanel={subPanel} subPanel={subPanel}
extra={extra}
/> />
); );
} }
@ -109,3 +243,72 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
fallback: false, fallback: false,
}; };
}; };
export function useTesting(content: GetContentTextQuery) {
const router = useRouter();
const contentAtr = content.contents.data[0].attributes;
if (contentAtr.categories.data.length === 0) {
prettyTestError(router, "Missing categories", ["content"]);
}
if (contentAtr.ranged_contents.data.length === 0) {
prettyTestWarning(router, "Unconnected to any source", ["content"]);
}
if (contentAtr.text_set.length === 0) {
prettyTestWarning(router, "Has no textset, nor audioset, nor videoset", [
"content",
]);
}
if (contentAtr.text_set.length > 1) {
console.warn(
prettyTestError(router, "More than one textset for this language", [
"content",
"text_set",
])
);
}
if (contentAtr.text_set.length === 1) {
const textset = contentAtr.text_set[0];
if (!textset.text) {
prettyTestError(router, "Missing text", ["content", "text_set"]);
}
if (!textset.source_language.data) {
prettyTestError(router, "Missing source language", [
"content",
"text_set",
]);
}
if (textset.source_language.data.attributes.code === router.locale) {
// This is a transcript
if (textset.transcribers.data.length === 0) {
prettyTestError(router, "Missing transcribers attribution", [
"content",
"text_set",
]);
}
if (textset.translators.data.length > 0) {
prettyTestError(router, "Transcripts shouldn't have translators", [
"content",
"text_set",
]);
}
} else {
// This is a translation
if (textset.translators.data.length === 0) {
prettyTestError(router, "Missing translators attribution", [
"content",
"text_set",
]);
}
if (textset.transcribers.data.length > 0) {
prettyTestError(router, "Translations shouldn't have transcribers", [
"content",
"text_set",
]);
}
}
}
}

View File

@ -16,7 +16,11 @@ import NavOption from "components/PanelComponents/NavOption";
import ReturnButton from "components/PanelComponents/ReturnButton"; import ReturnButton from "components/PanelComponents/ReturnButton";
import HorizontalLine from "components/HorizontalLine"; import HorizontalLine from "components/HorizontalLine";
import AppLayout from "components/AppLayout"; import AppLayout from "components/AppLayout";
import { prettySlug } from "queries/helpers"; import {
prettySlug,
prettyTestError,
prettyTestWarning,
} from "queries/helpers";
import InsetBox from "components/InsetBox"; import InsetBox from "components/InsetBox";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import ReactTooltip from "react-tooltip"; import ReactTooltip from "react-tooltip";
@ -165,41 +169,52 @@ export function useTesting(
const router = useRouter(); const router = useRouter();
chronologyEras.chronologyEras.data.map((era) => { chronologyEras.chronologyEras.data.map((era) => {
if (era.attributes.title.length === 0) { if (era.attributes.title.length === 0) {
console.warn( prettyTestError(
`${router.pathname} | ${router.locale} | chronologyEras | ${era.attributes.slug} | Missing translation for title and description, using slug instead` router,
"Missing translation for title and description, using slug instead",
["chronologyEras", era.attributes.slug]
); );
} else if (era.attributes.title.length > 1) { } else if (era.attributes.title.length > 1) {
console.warn( prettyTestError(router, "More than one title and description", [
`${router.pathname} | ${router.locale} | chronologyEras | ${era.attributes.slug} | More than one title and description` "chronologyEras",
); era.attributes.slug,
]);
} else { } else {
if (!era.attributes.title[0].title) if (!era.attributes.title[0].title)
console.warn( prettyTestError(router, "Missing title, using slug instead", [
`${router.pathname} | ${router.locale} | chronologyEras | ${era.attributes.slug} | Missing title, using slug instead` "chronologyEras",
); era.attributes.slug,
]);
if (!era.attributes.title[0].description) if (!era.attributes.title[0].description)
console.warn( prettyTestError(router, "Missing description", [
`${router.pathname} | ${router.locale} | chronologyEras | ${era.attributes.slug} | Missing description` "chronologyEras",
); era.attributes.slug,
]);
} }
}); });
chronologyItems.chronologyItems.data.map((item) => { chronologyItems.chronologyItems.data.map((item) => {
const date = `${item.attributes.year}/${item.attributes.month}/${item.attributes.day}`;
if (!(item.attributes.events.length > 0)) { if (!(item.attributes.events.length > 0)) {
console.warn( prettyTestError(router, "No events for this date", [
`${router.pathname} | ${router.locale} | chronologyItems | ${item.attributes.year}/${item.attributes.month}/${item.attributes.day} | No events for this date` "chronologyItems",
); date,
]);
} else { } else {
item.attributes.events.map((event) => { item.attributes.events.map((event) => {
if (!event.source.data) { if (!event.source.data) {
console.warn( prettyTestError(router, "No source for this event", [
`${router.pathname} | ${router.locale} | chronologyItems | ${item.attributes.year}/${item.attributes.month}/${item.attributes.day} | ${event.id} | No source for this event` "chronologyItems",
); date,
event.id,
]);
} }
if (!(event.translations.length > 0)) { if (!(event.translations.length > 0)) {
console.warn( prettyTestWarning(router, "No translation for this event", [
`${router.pathname} | ${router.locale} | chronologyItems | ${item.attributes.year}/${item.attributes.month}/${item.attributes.day} | ${event.id} | No translation for this event` "chronologyItems",
); date,
event.id,
]);
} }
}); });
} }

View File

@ -8,6 +8,7 @@ import {
GetWebsiteInterfaceQuery, GetWebsiteInterfaceQuery,
StrapiImage, StrapiImage,
} from "graphql/operations-types"; } from "graphql/operations-types";
import { NextRouter } from "next/router";
export function prettyDate( export function prettyDate(
datePicker: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["release_date"] datePicker: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["release_date"]
@ -117,6 +118,39 @@ export function prettyLanguage(code: string): string {
} }
} }
export function prettyTestWarning(
router: NextRouter,
message: string,
subCategory?: string[]
): void {
prettyTestWritter(TestingLevel.Warning, router, message, subCategory);
}
export function prettyTestError(
router: NextRouter,
message: string,
subCategory?: string[]
): void {
prettyTestWritter(TestingLevel.Error, router, message, subCategory);
}
enum TestingLevel {
Warning = "warn",
Error = "error",
}
function prettyTestWritter(
level: TestingLevel,
{ asPath, locale }: NextRouter,
message: string,
subCategory?: string[]
): void {
subCategory?.push("");
console.warn(
`${level} - ${asPath} | ${locale} | ${subCategory?.join(" | ")}${message}`
);
}
export function capitalizeString(string: string): string { export function capitalizeString(string: string): string {
function capitalizeWord(word: string): string { function capitalizeWord(word: string): string {
return word.charAt(0).toUpperCase() + word.substring(1); return word.charAt(0).toUpperCase() + word.substring(1);
@ -131,28 +165,23 @@ export function convertMmToInch(mm: number): string {
return (mm * 0.03937008).toPrecision(3); return (mm * 0.03937008).toPrecision(3);
} }
type OgImage = { export type OgImage = {
image: string; image: string;
width: number; width: number;
height: number; height: number;
alt: string; alt: string;
}; };
export function getOgImage( export function getOgImage(quality: ImageQuality, image: StrapiImage): OgImage {
quality: ImageQuality, const imgSize = getImgSizesByQuality(
image?: StrapiImage image.width,
): OgImage | undefined { image.height,
if (image) { quality ? quality : ImageQuality.Small
const imgSize = getImgSizesByQuality( );
image.width, return {
image.height, image: getAssetURL(image.url, quality),
quality ? quality : ImageQuality.Small width: imgSize.width,
); height: imgSize.height,
return { alt: image.alternativeText,
image: getAssetURL(image.url, quality), };
width: imgSize.width,
height: imgSize.height,
alt: image.alternativeText,
};
}
} }

View File

@ -77,6 +77,10 @@
@apply text-dark; @apply text-dark;
} }
.prose blockquote {
@apply border-l-dark
}
} }
@layer components { @layer components {

0
testing_logs/.gitkeep Normal file
View File

View File

@ -2,6 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"importHelpers": true,
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,