Wordings can now use ICU format
This commit is contained in:
parent
77e25c9056
commit
be1ea95b71
|
@ -1,4 +1,5 @@
|
||||||
src/graphql/generated.ts
|
src/graphql/generated.ts
|
||||||
|
src/graphql/icuParams.ts
|
||||||
src/shared
|
src/shared
|
||||||
.eslintrc.js
|
.eslintrc.js
|
||||||
graphql-codegen.config.js
|
graphql-codegen.config.js
|
||||||
|
|
57
README.md
57
README.md
|
@ -1,18 +1,25 @@
|
||||||
# Accords-library.com
|
# Accords-library.com
|
||||||
|
|
||||||
|
|
||||||
[![Node.js CI](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml)
|
[![Node.js CI](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/Accords-Library/accords-library.com/actions/workflows/node.js.yml)
|
||||||
[![GitHub](https://img.shields.io/github/license/Accords-Library/accords-library.com?style=flat-square)](https://github.com/Accords-Library/accords-library.com/blob/main/LICENSE)
|
[![GitHub](https://img.shields.io/github/license/Accords-Library/accords-library.com?style=flat-square)](https://github.com/Accords-Library/accords-library.com/blob/main/LICENSE)
|
||||||
![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/Accords-Library/accords-library.com?style=flat-square)
|
![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/Accords-Library/accords-library.com?style=flat-square)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Accord’s Library is a fan-site that aims at gathering and archiving all of Yoko Taro’s work.
|
||||||
|
Yoko Taro is a Japanese video game director and scenario writer. He is best-known for his work on the NieR and Drakengard (Drag-on Dragoon) franchises.
|
||||||
|
|
||||||
|
|
||||||
## Technologies
|
## Technologies
|
||||||
|
|
||||||
#### [Back](https://github.com/Accords-Library/strapi.accords-library.com)
|
#### [Content Management System](https://github.com/Accords-Library/strapi.accords-library.com)
|
||||||
|
|
||||||
- CMS: Stapi
|
- CMS: [Stapi](https://strapi.io/)
|
||||||
- GraphQL endpoint
|
- GraphQL endpoint
|
||||||
- Multilanguage support
|
- Multilanguage support
|
||||||
- Markdown format for the rich text fields
|
- Markdown format for the rich text fields
|
||||||
- Use webhooks to notify the front-end and image processor of updates
|
- Use webhooks to notify the front-end, search engine, and image processor of updates
|
||||||
|
|
||||||
#### [Image Processor](https://github.com/Accords-Library/img.accords-library.com)
|
#### [Image Processor](https://github.com/Accords-Library/img.accords-library.com)
|
||||||
|
|
||||||
|
@ -22,51 +29,81 @@
|
||||||
- Large: 2048x2048, quality 80, .webp
|
- Large: 2048x2048, quality 80, .webp
|
||||||
- Og: 512x512, quality 60, .jpg
|
- Og: 512x512, quality 60, .jpg
|
||||||
|
|
||||||
|
#### [Search Engine](https://github.com/Accords-Library/search.accords-library.com)
|
||||||
|
|
||||||
|
- Search Engine: [Meilisearch](https://www.meilisearch.com/)
|
||||||
|
|
||||||
#### [Front](https://github.com/Accords-Library/accords-library.com) (this repository)
|
#### [Front](https://github.com/Accords-Library/accords-library.com) (this repository)
|
||||||
|
|
||||||
- Language: [TypeScript](https://www.typescriptlang.org/)
|
- Language: [TypeScript](https://www.typescriptlang.org/)
|
||||||
|
|
||||||
- Framework: [Next.js 13](https://nextjs.org/) (React 18)
|
- Framework: [Next.js 13](https://nextjs.org/) (React 18)
|
||||||
|
|
||||||
- Queries: [GraphQL Code Generator](https://www.graphql-code-generator.com/)
|
- Queries: [GraphQL Code Generator](https://www.graphql-code-generator.com/)
|
||||||
|
|
||||||
- Fetch the GraphQL schema from the GraphQL back-end endpoint
|
- Fetch the GraphQL schema from the GraphQL back-end endpoint
|
||||||
- Read the operations and fragments stored as graphql files in the `src/graphql` folder
|
- Read the operations and fragments stored as graphql files in the `src/graphql` folder
|
||||||
- Automatically generates a typesafe ready to use SDK using [graphql-request](https://www.npmjs.com/package/graphql-request) as the GraphQL client
|
- Automatically generates a typesafe ready to use SDK using [graphql-request](https://www.npmjs.com/package/graphql-request) as the GraphQL client
|
||||||
- Markdown: [markdown-to-jsx](https://www.npmjs.com/package/markdown-to-jsx)
|
|
||||||
- Support for arbitrary React Components and Component Props!
|
- Markdown
|
||||||
|
- Use [Marked](https://www.npmjs.com/package/marked) to convert markdown to HTML (which is then sanitized using [DOMPurify](https://www.npmjs.com/package/isomorphic-dompurify))
|
||||||
|
- Support for arbitrary React Components and Component Props using [markdown-to-jsx](https://www.npmjs.com/package/markdown-to-jsx)
|
||||||
- Autogenerated multi-level table of content and anchor links for the different headers
|
- Autogenerated multi-level table of content and anchor links for the different headers
|
||||||
|
|
||||||
- Styling: [Tailwind CSS](https://tailwindcss.com/)
|
- Styling: [Tailwind CSS](https://tailwindcss.com/)
|
||||||
|
|
||||||
- Support for [Material Symbols](https://fonts.google.com/icons)
|
- Support for [Material Symbols](https://fonts.google.com/icons)
|
||||||
- Support for creating any arbitrary theming mode by swapping CSS variables
|
- Support for creating any arbitrary theming mode by swapping CSS variables
|
||||||
- Support for Container Queries (media queries at the element level)
|
- Support for Container Queries (media queries at the element level)
|
||||||
- The website has a three-column layout, which turns into one-column + 2 toggleable side-menus if the screen is too narrow.
|
- The website has a three-column layout, which turns into one-column + 2 toggleable side-menus if the screen is too narrow.
|
||||||
- Check out our [Design System Showcase](https://accords-library.com/dev/showcase/design-system)
|
- Check out our [Design System Showcase](https://accords-library.com/dev/showcase/design-system)
|
||||||
|
|
||||||
- State Management: [Jōtai](https://jotai.org/)
|
- State Management: [Jōtai](https://jotai.org/)
|
||||||
|
|
||||||
- Jōtai is a small-weighted library for atomic state management
|
- Jōtai is a small-weighted library for atomic state management
|
||||||
- Persistent app state using LocalStorage and SessionStorage
|
- Persistent app state using LocalStorage and SessionStorage
|
||||||
|
|
||||||
- Accessibility
|
- Accessibility
|
||||||
|
|
||||||
- Gestures using [react-swipeable](https://www.npmjs.com/package/react-swipeable)
|
- Gestures using [react-swipeable](https://www.npmjs.com/package/react-swipeable)
|
||||||
- Keyboard hotkeys using [react-hotkeys-hook](https://www.npmjs.com/package/react-hotkeys-hook)
|
- Keyboard hotkeys using [react-hotkeys-hook](https://www.npmjs.com/package/react-hotkeys-hook)
|
||||||
- Support for light and dark mode with a manual switch and system's selected theme by default
|
- Support for light and dark mode with a manual switch and system's selected theme by default
|
||||||
- Fonts can be swaped to [OpenDyslexic](https://www.npmjs.com/package/@fontsource/opendyslexic)
|
- Fonts can be swaped to [OpenDyslexic](https://www.npmjs.com/package/@fontsource/opendyslexic)
|
||||||
|
|
||||||
- Multilingual
|
- Multilingual
|
||||||
|
|
||||||
- By default, use the browser's language as the main language
|
- By default, use the browser's language as the main language
|
||||||
- Fallback languages are used for content which are not available in the main language
|
- Fallback languages are used for content which are not available in the main language
|
||||||
- Main and fallback languages can be ordered manually by the user
|
- Main and fallback languages can be ordered manually by the user
|
||||||
- At the content level, the user can know which language is available
|
- At the content level, the user can know which language is available
|
||||||
- Furthermore, the user can temporary select another language then the one that was automatically selected
|
- Furthermore, the user can temporary select another language then the one that was automatically selected
|
||||||
|
|
||||||
|
- UI Localizations
|
||||||
|
|
||||||
|
- The translated wordings use [ICU Message Format](https://unicode-org.github.io/icu/userguide/format_parse/messages/) to include variables, plural, dates...
|
||||||
|
- Use a custom ICU Typescript transformation script to provide type safety when formatting ICU wordings
|
||||||
|
- Fallback to English if the working isn't available in one language
|
||||||
|
|
||||||
- SSG + ISR (Static Site Generation + Incremental Static Regeneration)
|
- SSG + ISR (Static Site Generation + Incremental Static Regeneration)
|
||||||
|
|
||||||
- The website is built before running in production
|
- The website is built before running in production
|
||||||
- Performances are great, and possibility to deploy the app on a CDN
|
- Performances are great, and possibility to deploy the app on a CDN
|
||||||
- On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted
|
- On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted
|
||||||
- UI localizations are downloaded separetely into the `public/local-data` to avoid fetching the same static props for every page.
|
- UI localizations are downloaded separetely into the `public/local-data` to avoid fetching the same static props for every page.
|
||||||
|
|
||||||
- SEO
|
- SEO
|
||||||
|
|
||||||
- Good defaults for the metadata and OpenGraph properties
|
- Good defaults for the metadata and OpenGraph properties
|
||||||
- Each page can provide a custom thumbnail, title, description to be used
|
- Each page can provide a custom thumbnail, title, description to be used
|
||||||
- Automatic generation of the sitemap using [next-sitemap](https://www.npmjs.com/package/next-sitemap)
|
- Automatic generation of the sitemap using [next-sitemap](https://www.npmjs.com/package/next-sitemap)
|
||||||
- Data quality testing
|
|
||||||
|
- Data Quality Testing
|
||||||
|
|
||||||
- Data from the CMS is subject to a battery of tests (about 20 warning types and 40 error types) at build time
|
- Data from the CMS is subject to a battery of tests (about 20 warning types and 40 error types) at build time
|
||||||
- Each warning/error comes with a front-end link to the incriminating element, as well as a link to the CMS to fix it
|
- Each warning/error comes with a front-end link to the incriminating element, as well as a link to the CMS to fix it
|
||||||
- Check for completeness, conformity, and integrity
|
- Check for completeness, conformity, and integrity
|
||||||
- Code quality and style
|
|
||||||
|
- Code Quality and Style
|
||||||
|
|
||||||
- React Strict Mode
|
- React Strict Mode
|
||||||
- [Eslint](https://www.npmjs.com/package/eslint) with [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import), [typescript-eslint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin)
|
- [Eslint](https://www.npmjs.com/package/eslint) with [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import), [typescript-eslint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin)
|
||||||
|
@ -76,6 +113,12 @@
|
||||||
- Other
|
- Other
|
||||||
- Custom book reader based on [Okuma-Reader](https://github.com/DrMint/Okuma-Reader)
|
- Custom book reader based on [Okuma-Reader](https://github.com/DrMint/Okuma-Reader)
|
||||||
- Custom lightbox using [react-zoom-pan-pinch](https://www.npmjs.com/package/react-zoom-pan-pinch)
|
- Custom lightbox using [react-zoom-pan-pinch](https://www.npmjs.com/package/react-zoom-pan-pinch)
|
||||||
|
- Handle query params using [Zod](https://zod.dev/)
|
||||||
|
- A special "Terminal" mode. Can you find it?
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
Check out our [Design System Showcase](https://accords-library.com/dev/showcase/design-system)!
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<minder version="1.14.0" parent-etag="565269084" etag="3671330291">
|
||||||
|
<theme name="dark" label="Dark" index="-1"/>
|
||||||
|
<styles>
|
||||||
|
<style level="0" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="rounded" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="10" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="1" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="2" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="3" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="4" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="5" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="6" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="7" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="8" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="9" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<style level="10" isset="true" branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true" connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
</styles>
|
||||||
|
<drawarea x="-706.6563110351558" y="37.12041219075536" scale="1.5"/>
|
||||||
|
<images/>
|
||||||
|
<nodes>
|
||||||
|
<node id="0" posx="619.05682373046852" posy="496.07335408528627" width="181" height="56" side="left" fold="false" treesize="56" layout="Horizontal" group="false">
|
||||||
|
<style branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true"/>
|
||||||
|
<nodename posx="638.05682373046852" posy="515.07335408528627" maxwidth="200">
|
||||||
|
<text data="accords-library.com"/>
|
||||||
|
</nodename>
|
||||||
|
<nodenote></nodenote>
|
||||||
|
</node>
|
||||||
|
<node id="1" posx="573.47979736328057" posy="193.53715006510404" width="228" height="56" side="right" fold="false" treesize="56" layout="Horizontal" group="false">
|
||||||
|
<style branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true"/>
|
||||||
|
<nodename posx="592.47979736328057" posy="212.53715006510404" maxwidth="200">
|
||||||
|
<text data="strapi.accords-library.com"/>
|
||||||
|
</nodename>
|
||||||
|
<nodenote></nodenote>
|
||||||
|
</node>
|
||||||
|
<node id="2" posx="479.06459554036411" posy="352.9875793457029" width="230" height="56" side="right" fold="false" treesize="56" layout="Horizontal" group="false">
|
||||||
|
<style branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true"/>
|
||||||
|
<nodename posx="498.06459554036411" posy="371.9875793457029" maxwidth="200">
|
||||||
|
<text data="watch.accords-library.com"/>
|
||||||
|
</nodename>
|
||||||
|
<nodenote></nodenote>
|
||||||
|
</node>
|
||||||
|
<node id="3" posx="930.07356770833326" posy="485.75804646809888" width="235" height="56" side="left" fold="false" treesize="56" layout="Horizontal" group="false">
|
||||||
|
<style branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true"/>
|
||||||
|
<nodename posx="949.07356770833326" posy="504.75804646809888" maxwidth="200">
|
||||||
|
<text data="search.accords-library.com"/>
|
||||||
|
</nodename>
|
||||||
|
<nodenote></nodenote>
|
||||||
|
</node>
|
||||||
|
<node id="4" posx="270.75532023111936" posy="469.50143432617188" width="213" height="56" side="left" fold="false" treesize="56" layout="Horizontal" group="false">
|
||||||
|
<style branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true"/>
|
||||||
|
<nodename posx="289.75532023111936" posy="488.50143432617188" maxwidth="200">
|
||||||
|
<text data="img.accords-library.com"/>
|
||||||
|
</nodename>
|
||||||
|
<nodenote></nodenote>
|
||||||
|
</node>
|
||||||
|
<node id="5" posx="723.3406372070308" posy="704.73269653320312" width="236" height="56" side="right" fold="false" treesize="56" layout="Horizontal" group="false">
|
||||||
|
<style branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true"/>
|
||||||
|
<nodename posx="742.3406372070308" posy="723.73269653320312" maxwidth="200">
|
||||||
|
<text data="umami.accords-library.com"/>
|
||||||
|
</nodename>
|
||||||
|
<nodenote></nodenote>
|
||||||
|
</node>
|
||||||
|
<node id="6" posx="438.2949829101554" posy="704.79434204101562" width="234" height="56" side="right" fold="false" treesize="56" layout="Horizontal" group="false">
|
||||||
|
<style branchmargin="100" branchradius="25" linktype="straight" linkwidth="4" linkarrow="false" linkdash="solid" nodeborder="underlined" nodewidth="200" nodeborderwidth="4" nodefill="false" nodemargin="8" nodepadding="11" nodefont="Sans 11" nodemarkup="true"/>
|
||||||
|
<nodename posx="457.2949829101554" posy="723.79434204101562" maxwidth="200">
|
||||||
|
<text data="gallery.accords-library.com"/>
|
||||||
|
</nodename>
|
||||||
|
<nodenote></nodenote>
|
||||||
|
</node>
|
||||||
|
</nodes>
|
||||||
|
<groups/>
|
||||||
|
<connections>
|
||||||
|
<connection from_id="3" to_id="1" drag_x="1008.9155680338538" drag_y="291.56668090820301" color="#813d9c">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>GraphQL queries</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="1" to_id="3" drag_x="952.30708821614598" drag_y="342.49227905273426">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>Webhook</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="2" to_id="1" drag_x="610.53983561197947" drag_y="303.31814575195307" color="#813d9c">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>GraphQL mutations</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="0" to_id="5" drag_x="771.31520589192746" drag_y="639.53558349609443">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>Sends events</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="4" to_id="0" drag_x="501.63850911458314" drag_y="577.36627197265648">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="150"/>
|
||||||
|
<title>Provides the images</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="3" to_id="0" drag_x="887.70037841796875" drag_y="580.34930419921943">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="150"/>
|
||||||
|
<title>Provides search results</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="0" to_id="1" drag_x="825.44006347656227" drag_y="376.72006225585938">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>GraphQL queries</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="0" to_id="6" drag_x="634.52079264322902" drag_y="640.60829671224042">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>Links to</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="4" to_id="1" drag_x="371.127197265625" drag_y="290.4098002115885" color="#813d9c">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>Python script</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="2" to_id="0" drag_x="616.25642903645803" drag_y="443.84891764322913">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="150"/>
|
||||||
|
<title>Provides the videos</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
<connection from_id="1" to_id="4" drag_x="411.43526204427064" drag_y="337.32072957356752">
|
||||||
|
<style connectiondash="dotted" connectionlwidth="2" connectionarrow="fromto" connectionpadding="3" connectionfont="Sans 9" connectiontwidth="100"/>
|
||||||
|
<title>Webhook</title>
|
||||||
|
<note></note>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
<stickers/>
|
||||||
|
</minder>
|
Binary file not shown.
After Width: | Height: | Size: 2.1 MiB |
|
@ -10,9 +10,11 @@
|
||||||
"@fontsource/share-tech-mono": "^4.5.9",
|
"@fontsource/share-tech-mono": "^4.5.9",
|
||||||
"@fontsource/vollkorn": "^4.5.14",
|
"@fontsource/vollkorn": "^4.5.14",
|
||||||
"@fontsource/zen-maru-gothic": "^4.5.16",
|
"@fontsource/zen-maru-gothic": "^4.5.16",
|
||||||
|
"@formatjs/icu-messageformat-parser": "^2.1.14",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"cuid": "^2.1.8",
|
"cuid": "^2.1.8",
|
||||||
|
"intl-messageformat": "^10.2.5",
|
||||||
"isomorphic-dompurify": "^0.26.0",
|
"isomorphic-dompurify": "^0.26.0",
|
||||||
"jotai": "^1.13.1",
|
"jotai": "^1.13.1",
|
||||||
"markdown-to-jsx": "^7.1.8",
|
"markdown-to-jsx": "^7.1.8",
|
||||||
|
@ -22,7 +24,7 @@
|
||||||
"next": "^13.1.5",
|
"next": "^13.1.5",
|
||||||
"nodemailer": "^6.9.0",
|
"nodemailer": "^6.9.0",
|
||||||
"rc-slider": "^10.1.0",
|
"rc-slider": "^10.1.0",
|
||||||
"react": "18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hotkeys-hook": "^3.4.7",
|
"react-hotkeys-hook": "^3.4.7",
|
||||||
"react-swipeable": "^7.0.0",
|
"react-swipeable": "^7.0.0",
|
||||||
|
@ -1529,6 +1531,75 @@
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource/zen-maru-gothic/-/zen-maru-gothic-4.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/zen-maru-gothic/-/zen-maru-gothic-4.5.16.tgz",
|
||||||
"integrity": "sha512-KM3z1IfKRF3p9nE2TEyVSbQUHhrpSh14cUZ8B6asjOzgzS8PXXyUDD+vmvptIswI3C/1tMQUBns8mzuub3lHZQ=="
|
"integrity": "sha512-KM3z1IfKRF3p9nE2TEyVSbQUHhrpSh14cUZ8B6asjOzgzS8PXXyUDD+vmvptIswI3C/1tMQUBns8mzuub3lHZQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@formatjs/ecma402-abstract": {
|
||||||
|
"version": "1.14.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.14.3.tgz",
|
||||||
|
"integrity": "sha512-SlsbRC/RX+/zg4AApWIFNDdkLtFbkq3LNoZWXZCE/nHVKqoIJyaoQyge/I0Y38vLxowUn9KTtXgusLD91+orbg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/intl-localematcher": "0.2.32",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/ecma402-abstract/node_modules/tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/fast-memoize": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-hPeM5LXUUjtCKPybWOUAWpv8lpja8Xz+uKprFPJcg5F2Rd+/bf1E0UUsLRpaAgOReAf5HMRtoIgv/UcyPICrTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/fast-memoize/node_modules/tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/icu-messageformat-parser": {
|
||||||
|
"version": "2.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.14.tgz",
|
||||||
|
"integrity": "sha512-0KqeVOb72losEhUW+59vhZGGd14s1f35uThfEMVKZHKLEObvJdFTiI3ZQwvTMUCzLEMxnS6mtnYPmG4mTvwd3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.14.3",
|
||||||
|
"@formatjs/icu-skeleton-parser": "1.3.18",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/icu-messageformat-parser/node_modules/tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/icu-skeleton-parser": {
|
||||||
|
"version": "1.3.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.18.tgz",
|
||||||
|
"integrity": "sha512-ND1ZkZfmLPcHjAH1sVpkpQxA+QYfOX3py3SjKWMUVGDow18gZ0WPqz3F+pJLYQMpS2LnnQ5zYR2jPVYTbRwMpg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.14.3",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/icu-skeleton-parser/node_modules/tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/intl-localematcher": {
|
||||||
|
"version": "0.2.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz",
|
||||||
|
"integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/intl-localematcher/node_modules/tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
},
|
||||||
"node_modules/@graphql-codegen/cli": {
|
"node_modules/@graphql-codegen/cli": {
|
||||||
"version": "2.16.4",
|
"version": "2.16.4",
|
||||||
"resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-2.16.4.tgz",
|
"resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-2.16.4.tgz",
|
||||||
|
@ -6962,6 +7033,22 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/intl-messageformat": {
|
||||||
|
"version": "10.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.2.5.tgz",
|
||||||
|
"integrity": "sha512-AievYMN6WLLHwBeCTv4aRKG+w3ZNyZtkObwgsKk3Q7GNTq8zDRvDbJSBQkb2OPeVCcAKcIXvak9FF/bRNavoww==",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.14.3",
|
||||||
|
"@formatjs/fast-memoize": "1.2.7",
|
||||||
|
"@formatjs/icu-messageformat-parser": "2.1.14",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/intl-messageformat/node_modules/tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
},
|
||||||
"node_modules/invariant": {
|
"node_modules/invariant": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||||
|
@ -11662,6 +11749,85 @@
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource/zen-maru-gothic/-/zen-maru-gothic-4.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/zen-maru-gothic/-/zen-maru-gothic-4.5.16.tgz",
|
||||||
"integrity": "sha512-KM3z1IfKRF3p9nE2TEyVSbQUHhrpSh14cUZ8B6asjOzgzS8PXXyUDD+vmvptIswI3C/1tMQUBns8mzuub3lHZQ=="
|
"integrity": "sha512-KM3z1IfKRF3p9nE2TEyVSbQUHhrpSh14cUZ8B6asjOzgzS8PXXyUDD+vmvptIswI3C/1tMQUBns8mzuub3lHZQ=="
|
||||||
},
|
},
|
||||||
|
"@formatjs/ecma402-abstract": {
|
||||||
|
"version": "1.14.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.14.3.tgz",
|
||||||
|
"integrity": "sha512-SlsbRC/RX+/zg4AApWIFNDdkLtFbkq3LNoZWXZCE/nHVKqoIJyaoQyge/I0Y38vLxowUn9KTtXgusLD91+orbg==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/intl-localematcher": "0.2.32",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/fast-memoize": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-hPeM5LXUUjtCKPybWOUAWpv8lpja8Xz+uKprFPJcg5F2Rd+/bf1E0UUsLRpaAgOReAf5HMRtoIgv/UcyPICrTQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/icu-messageformat-parser": {
|
||||||
|
"version": "2.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.14.tgz",
|
||||||
|
"integrity": "sha512-0KqeVOb72losEhUW+59vhZGGd14s1f35uThfEMVKZHKLEObvJdFTiI3ZQwvTMUCzLEMxnS6mtnYPmG4mTvwd3Q==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.14.3",
|
||||||
|
"@formatjs/icu-skeleton-parser": "1.3.18",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/icu-skeleton-parser": {
|
||||||
|
"version": "1.3.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.18.tgz",
|
||||||
|
"integrity": "sha512-ND1ZkZfmLPcHjAH1sVpkpQxA+QYfOX3py3SjKWMUVGDow18gZ0WPqz3F+pJLYQMpS2LnnQ5zYR2jPVYTbRwMpg==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.14.3",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@formatjs/intl-localematcher": {
|
||||||
|
"version": "0.2.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz",
|
||||||
|
"integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@graphql-codegen/cli": {
|
"@graphql-codegen/cli": {
|
||||||
"version": "2.16.4",
|
"version": "2.16.4",
|
||||||
"resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-2.16.4.tgz",
|
"resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-2.16.4.tgz",
|
||||||
|
@ -15915,6 +16081,24 @@
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"intl-messageformat": {
|
||||||
|
"version": "10.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.2.5.tgz",
|
||||||
|
"integrity": "sha512-AievYMN6WLLHwBeCTv4aRKG+w3ZNyZtkObwgsKk3Q7GNTq8zDRvDbJSBQkb2OPeVCcAKcIXvak9FF/bRNavoww==",
|
||||||
|
"requires": {
|
||||||
|
"@formatjs/ecma402-abstract": "1.14.3",
|
||||||
|
"@formatjs/fast-memoize": "1.2.7",
|
||||||
|
"@formatjs/icu-messageformat-parser": "2.1.14",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"invariant": {
|
"invariant": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 12499",
|
"dev": "next dev -p 12499",
|
||||||
"precommit": "npm run fetch-local-data && npm run prettier && npm run unused-exports && npm run eslint && npm run tsc && echo ALL PRECOMMIT CHECKS PASSED SUCCESSFULLY, LET\\'S FUCKING GO!",
|
"precommit": "npm run fetch-local-data && npm run icu-to-ts && npm run prettier && npm run unused-exports && npm run eslint && npm run tsc && echo ALL PRECOMMIT CHECKS PASSED SUCCESSFULLY, LET\\'S FUCKING GO!",
|
||||||
"unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport=src/pages --ignoreFiles=generated",
|
"unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport=src/pages --ignoreFiles=generated",
|
||||||
"fetch-local-data": "npm run generate && esrun src/graphql/fetchLocalData.ts --esrun",
|
"fetch-local-data": "npm run generate && esrun src/graphql/fetchLocalData.ts --esrun",
|
||||||
"prebuild": "npm run fetch-local-data",
|
"icu-to-ts": "esrun src/graphql/icuToTypescript.ts --icu",
|
||||||
|
"prebuild": "npm run fetch-local-data && npm run icu-to-ts",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"postbuild": "next-sitemap --config next-sitemap.config.js",
|
"postbuild": "next-sitemap --config next-sitemap.config.js",
|
||||||
"start": "next start -p 12500",
|
"start": "next start -p 12500",
|
||||||
|
@ -21,9 +22,11 @@
|
||||||
"@fontsource/share-tech-mono": "^4.5.9",
|
"@fontsource/share-tech-mono": "^4.5.9",
|
||||||
"@fontsource/vollkorn": "^4.5.14",
|
"@fontsource/vollkorn": "^4.5.14",
|
||||||
"@fontsource/zen-maru-gothic": "^4.5.16",
|
"@fontsource/zen-maru-gothic": "^4.5.16",
|
||||||
|
"@formatjs/icu-messageformat-parser": "^2.1.14",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"cuid": "^2.1.8",
|
"cuid": "^2.1.8",
|
||||||
|
"intl-messageformat": "^10.2.5",
|
||||||
"isomorphic-dompurify": "^0.26.0",
|
"isomorphic-dompurify": "^0.26.0",
|
||||||
"jotai": "^1.13.1",
|
"jotai": "^1.13.1",
|
||||||
"markdown-to-jsx": "^7.1.8",
|
"markdown-to-jsx": "^7.1.8",
|
||||||
|
@ -33,7 +36,7 @@
|
||||||
"next": "^13.1.5",
|
"next": "^13.1.5",
|
||||||
"nodemailer": "^6.9.0",
|
"nodemailer": "^6.9.0",
|
||||||
"rc-slider": "^10.1.0",
|
"rc-slider": "^10.1.0",
|
||||||
"react": "18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hotkeys-hook": "^3.4.7",
|
"react-hotkeys-hook": "^3.4.7",
|
||||||
"react-swipeable": "^7.0.0",
|
"react-swipeable": "^7.0.0",
|
||||||
|
|
|
@ -13,16 +13,14 @@
|
||||||
"wiki_short_description": "An encyclopedia for everything related to DrakeNieR",
|
"wiki_short_description": "An encyclopedia for everything related to DrakeNieR",
|
||||||
"chronicles_short_description": "Experience all events and content in chronological order",
|
"chronicles_short_description": "Experience all events and content in chronological order",
|
||||||
"news": "News",
|
"news": "News",
|
||||||
"merch": "Merch",
|
|
||||||
"gallery": "Gallery",
|
"gallery": "Gallery",
|
||||||
"archives": "Archives",
|
"archives": "Archives",
|
||||||
"about_us": "About us",
|
"about_us": "About us",
|
||||||
"licensing_notice": "This website’s content is made available under [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) unless otherwise noted.",
|
"licensing_notice": "This website’s content is made available under [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) unless otherwise noted.",
|
||||||
"copyright_notice": "Accord’s Library is not affiliated with or endorsed by SQUARE ENIX CO. LTD. All game assets and promotional materials belongs to © SQUARE ENIX CO. LTD.",
|
"copyright_notice": "Accord’s Library is not affiliated with or endorsed by SQUARE ENIX CO. LTD. All game assets and promotional materials belongs to © SQUARE ENIX CO. LTD.",
|
||||||
"contents_description": "All the contents (textual, audio, and video) from the Library or other online sources.",
|
"contents_description": "All the contents (textual, audio, and video) from the Library or other online sources.",
|
||||||
"type": "Type",
|
"type": "{ count, plural, =0 {No types} one {Type} other {Types} }",
|
||||||
"category": "Category",
|
"category": "{ count, plural, =0 {No categories} one {Category} other {Categories} }",
|
||||||
"categories": "Categories",
|
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"release_date": "Release date",
|
"release_date": "Release date",
|
||||||
"release_year": "Release year",
|
"release_year": "Release year",
|
||||||
|
@ -31,23 +29,17 @@
|
||||||
"width": "Width",
|
"width": "Width",
|
||||||
"height": "Height",
|
"height": "Height",
|
||||||
"thickness": "Thickness",
|
"thickness": "Thickness",
|
||||||
"subitem": "Subitem",
|
"subitem": "{ count, plural, =0 {No subitems} one {Subitem} other {Subitems} }",
|
||||||
"subitems": "Subitems",
|
"variant": "{ count, plural, =0 {No variants} one {Variant} other {Variants} }",
|
||||||
"subitem_of": "Subitem of",
|
|
||||||
"variant": "Variant",
|
|
||||||
"variants": "Variants",
|
|
||||||
"variant_of": "Variant of",
|
|
||||||
"summary": "Summary",
|
"summary": "Summary",
|
||||||
"audio": "Audio",
|
"audio": "Audio",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"textual": "Textual",
|
"textual": "Textual",
|
||||||
"game": "Game",
|
"game": "Game",
|
||||||
"other": "Other",
|
"other": "Other",
|
||||||
"return_to": "Return to",
|
|
||||||
"left_to_right": "Left to right",
|
"left_to_right": "Left to right",
|
||||||
"right_to_left": "Right to left",
|
"right_to_left": "Right to left",
|
||||||
"page": "Page",
|
"page": "{ count, plural, =0 {No pages} one {Page} other {Pages} }",
|
||||||
"pages": "Pages",
|
|
||||||
"page_order": "Page order",
|
"page_order": "Page order",
|
||||||
"binding": "Binding",
|
"binding": "Binding",
|
||||||
"type_information": "Type information",
|
"type_information": "Type information",
|
||||||
|
@ -60,15 +52,12 @@
|
||||||
"view_scans": "View scans",
|
"view_scans": "View scans",
|
||||||
"paperback": "Paperback",
|
"paperback": "Paperback",
|
||||||
"hardcover": "Hardcover",
|
"hardcover": "Hardcover",
|
||||||
"languages": "Languages",
|
|
||||||
"select_language": "Select a language",
|
"select_language": "Select a language",
|
||||||
"language": "Language",
|
"language": "{ count, plural, =0 {No languages} one {Language} other {Languages} }",
|
||||||
"library_description": "A comprehensive list of all Yokoverse’s side materials (books, novellas, artbooks, stage plays, manga, drama CDs, and comics). For each, we provide photos, scans, and transcript of the content, information about what it is, when and how it was released, size, initial price…",
|
"library_description": "A comprehensive list of all Yokoverse’s side materials (books, novellas, artbooks, stage plays, manga, drama CDs, and comics). For each, we provide photos, scans, and transcript of the content, information about what it is, when and how it was released, size, initial price…",
|
||||||
"wiki_description": "An encyclopedia for everything related to DrakeNieR. Right now, we only have the Chronology but a lot more pages are planned to be released!",
|
"wiki_description": "An encyclopedia for everything related to DrakeNieR. Right now, we only have the Chronology but a lot more pages are planned to be released!",
|
||||||
"chronicles_description": "Experience all events and content in chronological order.",
|
"chronicles_description": "Experience all events and content in chronological order.",
|
||||||
"news_description": "News articles written by our Recorders! Here you will find announcements about new merch/items releases, guides, theories, unboxings, showcases...",
|
"news_description": "News articles written by our Recorders! Here you will find announcements about new merch/items releases, guides, theories, unboxings, showcases...",
|
||||||
"merch_description": "Harum ut consequatur a earum explicabo suscipit. Nostrum asperiores consectetur aperiam in ut sunt. Ipsa quibusdam et vel quam voluptas placeat. Qui est aliquam voluptatem. Tempora nisi exercitationem tempore sapiente expedita. Voluptas ut eaque nulla sunt ut dolor corrupti quos.",
|
|
||||||
"gallery_description": "A fully tagged Danbooru-styled gallery with currently more than a thousand unique official artworks.",
|
|
||||||
"archives_description": "Besides physical medias, we also archive digital contents such as websites, webpages, videos, and documents.",
|
"archives_description": "Besides physical medias, we also archive digital contents such as websites, webpages, videos, and documents.",
|
||||||
"about_us_description": "Find more information about the Accord's Library project in the following pages.",
|
"about_us_description": "Find more information about the Accord's Library project in the following pages.",
|
||||||
"page_not_found": "Oops! We’re having trouble finding this page",
|
"page_not_found": "Oops! We’re having trouble finding this page",
|
||||||
|
@ -77,8 +66,6 @@
|
||||||
"show_subitems": "Show subitems",
|
"show_subitems": "Show subitems",
|
||||||
"show_primary_items": "Show primary items",
|
"show_primary_items": "Show primary items",
|
||||||
"show_secondary_items": "Show secondary items",
|
"show_secondary_items": "Show secondary items",
|
||||||
"no_type": "No type",
|
|
||||||
"no_year": "No year",
|
|
||||||
"order_by": "Order by",
|
"order_by": "Order by",
|
||||||
"group_by": "Group by",
|
"group_by": "Group by",
|
||||||
"select_option_sidebar": "Select one of the options in the sidebar",
|
"select_option_sidebar": "Select one of the options in the sidebar",
|
||||||
|
@ -109,20 +96,14 @@
|
||||||
"translation_notice": "This content is a fan-translation",
|
"translation_notice": "This content is a fan-translation",
|
||||||
"source_language": "Source language",
|
"source_language": "Source language",
|
||||||
"pronouns": "Pronouns",
|
"pronouns": "Pronouns",
|
||||||
"no_category": "No category",
|
"item": "{ count, plural, =0 {No items} one {Item} other {Items} }",
|
||||||
"item": "Item",
|
|
||||||
"items": "Items",
|
|
||||||
"content": "Content",
|
"content": "Content",
|
||||||
"result": "Result",
|
|
||||||
"results": "Results",
|
|
||||||
"language_switch_message": "",
|
|
||||||
"open_settings": "Open settings",
|
"open_settings": "Open settings",
|
||||||
"change_language": "Change language",
|
"change_language": "Change language",
|
||||||
"open_search": "Open search",
|
"open_search": "Open search",
|
||||||
"chronology": "Chronology",
|
"chronology": "Chronology",
|
||||||
"accords_handbook": "Accord's Handbook",
|
"accords_handbook": "Accord's Handbook",
|
||||||
"legality": "Legality",
|
"legality": "Legality",
|
||||||
"members": "Members",
|
|
||||||
"sharing_policy": "Sharing Policy",
|
"sharing_policy": "Sharing Policy",
|
||||||
"contact_us": "Contact us",
|
"contact_us": "Contact us",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
@ -155,7 +136,6 @@
|
||||||
"only_display_unmarked_items": "Only display unmarked items",
|
"only_display_unmarked_items": "Only display unmarked items",
|
||||||
"display_all_items": "Display all items",
|
"display_all_items": "Display all items",
|
||||||
"table_of_contents": "Table of Contents",
|
"table_of_contents": "Table of Contents",
|
||||||
"definition": "Definition",
|
|
||||||
"no_results_message": "No results. You can try changing or resetting the search parameters.",
|
"no_results_message": "No results. You can try changing or resetting the search parameters.",
|
||||||
"all": "All",
|
"all": "All",
|
||||||
"special_pages": "Special Pages",
|
"special_pages": "Special Pages",
|
||||||
|
@ -193,7 +173,13 @@
|
||||||
"most_popular": "Most popular",
|
"most_popular": "Most popular",
|
||||||
"shortest": "Shortest",
|
"shortest": "Shortest",
|
||||||
"longest": "Longest",
|
"longest": "Longest",
|
||||||
"search": "Search"
|
"search": "Search",
|
||||||
|
"showing_x_out_of_y_results": "Showing {x} out of {y} results",
|
||||||
|
"return_to_x": "Return { x, select, undefined {} other {to {x}} }",
|
||||||
|
"x_results": "{ x, plural, =0 {No results} one {# result} other {# results} }",
|
||||||
|
"definition_x": "Definition {x}",
|
||||||
|
"subitem_of_x": "Subitem of {x}",
|
||||||
|
"variant_of_x": "Variant of {x}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -208,16 +194,14 @@
|
||||||
"wiki_short_description": "Une encyclopédie pour tout l'univers DrakeNieR",
|
"wiki_short_description": "Une encyclopédie pour tout l'univers DrakeNieR",
|
||||||
"chronicles_short_description": "Parcourir tous les événements et les contenu dans l'ordre chronologique",
|
"chronicles_short_description": "Parcourir tous les événements et les contenu dans l'ordre chronologique",
|
||||||
"news": "News",
|
"news": "News",
|
||||||
"merch": "Merch",
|
|
||||||
"gallery": "Galerie",
|
"gallery": "Galerie",
|
||||||
"archives": "Archives",
|
"archives": "Archives",
|
||||||
"about_us": "À propos",
|
"about_us": "À propos",
|
||||||
"licensing_notice": "Le contenu de ce site web est mis à disposition sous licence [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/), sauf indication contraire.",
|
"licensing_notice": "Le contenu de ce site web est mis à disposition sous licence [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/), sauf indication contraire.",
|
||||||
"copyright_notice": "Accord's Library n'est pas affiliée ni approuvée par SQUARE ENIX CO. LTD. Tous les contenus du jeu et les contenus promotionnel appartiennent à © SQUARE ENIX CO. LTD.",
|
"copyright_notice": "Accord's Library n'est pas affiliée ni approuvée par SQUARE ENIX CO. LTD. Tous les contenus du jeu et les contenus promotionnel appartiennent à © SQUARE ENIX CO. LTD.",
|
||||||
"contents_description": "",
|
"contents_description": "Tous les contenus (textuels, audio et vidéo) de la Bibliothèque ou d'autres sources en ligne.",
|
||||||
"type": "Type",
|
"type": "{ count, plural, =0 {Pas de type} one {Type} other {Types} }",
|
||||||
"category": "Catégorie",
|
"category": "{ count, plural, =0 {Pas de catégorie} one {Catégorie} other {Catégories} }",
|
||||||
"categories": "Catégories",
|
|
||||||
"size": "Dimension",
|
"size": "Dimension",
|
||||||
"release_date": "Date de sortie",
|
"release_date": "Date de sortie",
|
||||||
"release_year": "Année de sortie",
|
"release_year": "Année de sortie",
|
||||||
|
@ -226,23 +210,17 @@
|
||||||
"width": "Largeur",
|
"width": "Largeur",
|
||||||
"height": "Hauteur",
|
"height": "Hauteur",
|
||||||
"thickness": "Épaisseur",
|
"thickness": "Épaisseur",
|
||||||
"subitem": "Sous-item",
|
"subitem": "{ count, plural, =0 {Pas de sous-item} one {Sous-item} other {Sous-items} }",
|
||||||
"subitems": "Sous-items",
|
"variant": "{ count, plural, =0 {Pas de variante} one {Variante} other {Variantes} }",
|
||||||
"subitem_of": "Sous-item de",
|
|
||||||
"variant": "Variante",
|
|
||||||
"variants": "Variantes",
|
|
||||||
"variant_of": "Variante de",
|
|
||||||
"summary": "Résumé",
|
"summary": "Résumé",
|
||||||
"audio": "Audio",
|
"audio": "Audio",
|
||||||
"video": "Vidéo",
|
"video": "Vidéo",
|
||||||
"textual": "Textuel",
|
"textual": "Textuel",
|
||||||
"game": "Jeux",
|
"game": "Jeux",
|
||||||
"other": "Autre",
|
"other": "Autre",
|
||||||
"return_to": "Retourner à ",
|
|
||||||
"left_to_right": "Gauche à droite",
|
"left_to_right": "Gauche à droite",
|
||||||
"right_to_left": "Droite à gauche",
|
"right_to_left": "Droite à gauche",
|
||||||
"page": "Page",
|
"page": "{ count, plural, =0 {Aucune page} one {Page} other {Pages} }",
|
||||||
"pages": "Pages",
|
|
||||||
"page_order": "Order des pages",
|
"page_order": "Order des pages",
|
||||||
"binding": "Brochure",
|
"binding": "Brochure",
|
||||||
"type_information": "Information de type",
|
"type_information": "Information de type",
|
||||||
|
@ -255,25 +233,20 @@
|
||||||
"view_scans": "Voir les scans",
|
"view_scans": "Voir les scans",
|
||||||
"paperback": "Broché",
|
"paperback": "Broché",
|
||||||
"hardcover": "Relié",
|
"hardcover": "Relié",
|
||||||
"languages": "Langues",
|
|
||||||
"select_language": "Séléctionner la langue",
|
"select_language": "Séléctionner la langue",
|
||||||
"language": "Langue",
|
"language": "{ count, plural, =0 {Aucune langue} one {Langue} other {Langues} }",
|
||||||
"library_description": "",
|
"library_description": "Une liste complète de tous les médias annexes du Yokoverse (livres, romans, artbooks, pièces de théâtre, mangas, CD de théâtre et bandes dessinées). Pour chacun, nous fournissons des photos, des scans et une transcription du contenu, des informations sur sa nature, quand il est sorti, sa taille, son prix initial...",
|
||||||
"wiki_description": "",
|
"wiki_description": "Une encyclopédie pour tout ce qui concerne DrakeNieR. Pour l'instant, nous n'avons que la Chronologie mais beaucoup plus de pages sont prévues !",
|
||||||
"chronicles_description": "",
|
"chronicles_description": "Découvrez tous les événements et contenus de manière chronologique.",
|
||||||
"news_description": "Articles d'actualité écrits par nos Recorders ! Vous trouverez ici des annonces sur les nouvelles sorties de merch/items, des guides, des théories, des unboxings, des showcases...",
|
"news_description": "Articles d'actualité écrits par nos Recorders ! Vous trouverez ici des annonces sur les nouvelles sorties de merch/items, des guides, des théories, des unboxings, des showcases...",
|
||||||
"merch_description": "Lorem ipsum",
|
"archives_description": "Outre les supports physiques, nous archivons également les contenus numériques tels que les sites web, les pages web, les vidéos et les documents.",
|
||||||
"gallery_description": "",
|
"about_us_description": "Vous trouverez plus d'informations sur le projet Accord's Library dans les pages suivantes.",
|
||||||
"archives_description": "",
|
|
||||||
"about_us_description": "",
|
|
||||||
"page_not_found": "Page introuvable",
|
"page_not_found": "Page introuvable",
|
||||||
"default_description": "Accord's Library a pour but de rassembler et d'archiver l'ensemble des travaux de Yoko Taro. Yoko Taro est un réalisateur et scénariste de jeux vidéo japonais.",
|
"default_description": "Accord's Library a pour but de rassembler et d'archiver l'ensemble des travaux de Yoko Taro. Yoko Taro est un réalisateur et scénariste de jeux vidéo japonais.",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"show_subitems": "Afficher les sous-items",
|
"show_subitems": "Afficher les sous-items",
|
||||||
"show_primary_items": "Afficher les items primaires",
|
"show_primary_items": "Afficher les items primaires",
|
||||||
"show_secondary_items": "Afficher les items secondaires",
|
"show_secondary_items": "Afficher les items secondaires",
|
||||||
"no_type": "Pas de type",
|
|
||||||
"no_year": "Pas d'année",
|
|
||||||
"order_by": "Ordonné par",
|
"order_by": "Ordonné par",
|
||||||
"group_by": "Groupé par",
|
"group_by": "Groupé par",
|
||||||
"select_option_sidebar": "Sélectionner l'une des options de la barre latérale",
|
"select_option_sidebar": "Sélectionner l'une des options de la barre latérale",
|
||||||
|
@ -287,7 +260,7 @@
|
||||||
"player_name": "Nom du joueur",
|
"player_name": "Nom du joueur",
|
||||||
"currency": "Devise",
|
"currency": "Devise",
|
||||||
"font": "Police d'écriture",
|
"font": "Police d'écriture",
|
||||||
"calculated": "calculé",
|
"calculated": "Calculé",
|
||||||
"status_incomplete": "Cette entrée n'est que partiellement traduite/transcrite.",
|
"status_incomplete": "Cette entrée n'est que partiellement traduite/transcrite.",
|
||||||
"status_draft": "Cette entrée n'est qu'un brouillon. Cela signifie généralement qu'il s'agit d'un travail en cours. La traduction/transcription peut être médiocre et/ou auto-générée par ordinateur.",
|
"status_draft": "Cette entrée n'est qu'un brouillon. Cela signifie généralement qu'il s'agit d'un travail en cours. La traduction/transcription peut être médiocre et/ou auto-générée par ordinateur.",
|
||||||
"status_review": "Cet entrée n'a pas encore été relue. Le contenu devrait néanmoins être correct.",
|
"status_review": "Cet entrée n'a pas encore été relue. Le contenu devrait néanmoins être correct.",
|
||||||
|
@ -304,20 +277,14 @@
|
||||||
"translation_notice": "Ceci est une traduction",
|
"translation_notice": "Ceci est une traduction",
|
||||||
"source_language": "Langue source",
|
"source_language": "Langue source",
|
||||||
"pronouns": "Pronoms",
|
"pronouns": "Pronoms",
|
||||||
"no_category": "Pas de categorie",
|
"item": "{ count, plural, =0 {Aucun item} one {Item} other {Items} }",
|
||||||
"item": "Item",
|
|
||||||
"items": "Items",
|
|
||||||
"content": "Content",
|
"content": "Content",
|
||||||
"result": "Resultat",
|
|
||||||
"results": "Résultats",
|
|
||||||
"language_switch_message": "Ce contenu n'est pas disponible dans la langue actuellement sélectionnée. Vous pouvez sélectionner l'une des langues suivantes à la place :",
|
|
||||||
"open_settings": "Ouvrir les paramètres",
|
"open_settings": "Ouvrir les paramètres",
|
||||||
"change_language": "Changer de langue",
|
"change_language": "Changer de langue",
|
||||||
"open_search": "Ouvrir le menu de recherche",
|
"open_search": "Ouvrir le menu de recherche",
|
||||||
"chronology": "Chronologie",
|
"chronology": "Chronologie",
|
||||||
"accords_handbook": "Le manuel de Accord",
|
"accords_handbook": "Le manuel de Accord",
|
||||||
"legality": "Légalité",
|
"legality": "Légalité",
|
||||||
"members": "Membres",
|
|
||||||
"sharing_policy": "Politique de partage",
|
"sharing_policy": "Politique de partage",
|
||||||
"contact_us": "Nous contacter",
|
"contact_us": "Nous contacter",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
@ -350,7 +317,6 @@
|
||||||
"only_display_unmarked_items": "Seulement afficher les items non-marqués",
|
"only_display_unmarked_items": "Seulement afficher les items non-marqués",
|
||||||
"display_all_items": "Afficher tous les items",
|
"display_all_items": "Afficher tous les items",
|
||||||
"table_of_contents": "Sommaire",
|
"table_of_contents": "Sommaire",
|
||||||
"definition": "Definition",
|
|
||||||
"no_results_message": "Aucun résultat. Vous pouvez essayer de modifier ou de réinitialiser les paramètres de recherche.",
|
"no_results_message": "Aucun résultat. Vous pouvez essayer de modifier ou de réinitialiser les paramètres de recherche.",
|
||||||
"all": "Tous",
|
"all": "Tous",
|
||||||
"special_pages": "Pages spéciales",
|
"special_pages": "Pages spéciales",
|
||||||
|
@ -388,7 +354,13 @@
|
||||||
"most_popular": "Moins populaires",
|
"most_popular": "Moins populaires",
|
||||||
"shortest": "Plus courtes",
|
"shortest": "Plus courtes",
|
||||||
"longest": "Plus longues",
|
"longest": "Plus longues",
|
||||||
"search": "Rechercher"
|
"search": "Rechercher",
|
||||||
|
"showing_x_out_of_y_results": "{x} résultats sur {y} affichés",
|
||||||
|
"return_to_x": "Retour { x, select, undefined {} other {à {x}} }",
|
||||||
|
"x_results": "{ x, plural, =0 {Pas de résultat} one {# résultat} other {# résultats} }",
|
||||||
|
"definition_x": "Définition {x}",
|
||||||
|
"subitem_of_x": "Sous-item de {x}",
|
||||||
|
"variant_of_x": "Variante de {x}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -403,7 +375,6 @@
|
||||||
"wiki_short_description": "ゲーム宇宙に関連するすべての百科事典です。",
|
"wiki_short_description": "ゲーム宇宙に関連するすべての百科事典です。",
|
||||||
"chronicles_short_description": "すべてのイベントとコンテンツを時系列で体験できる",
|
"chronicles_short_description": "すべてのイベントとコンテンツを時系列で体験できる",
|
||||||
"news": "ニュース",
|
"news": "ニュース",
|
||||||
"merch": "マーチ",
|
|
||||||
"gallery": "ギャラリー",
|
"gallery": "ギャラリー",
|
||||||
"archives": "アーカイブス",
|
"archives": "アーカイブス",
|
||||||
"about_us": "会社概要",
|
"about_us": "会社概要",
|
||||||
|
@ -412,7 +383,6 @@
|
||||||
"contents_description": "図書館や他のオンラインソースのすべてのコンテンツ(テキスト、オーディオ、ビデオ)。",
|
"contents_description": "図書館や他のオンラインソースのすべてのコンテンツ(テキスト、オーディオ、ビデオ)。",
|
||||||
"type": "タイプ",
|
"type": "タイプ",
|
||||||
"category": "カテゴリー",
|
"category": "カテゴリー",
|
||||||
"categories": "カテゴリー",
|
|
||||||
"size": "サイズ",
|
"size": "サイズ",
|
||||||
"release_date": "発売日",
|
"release_date": "発売日",
|
||||||
"release_year": "発売年",
|
"release_year": "発売年",
|
||||||
|
@ -422,22 +392,16 @@
|
||||||
"height": "高さ",
|
"height": "高さ",
|
||||||
"thickness": "厚み",
|
"thickness": "厚み",
|
||||||
"subitem": "サブアイテム",
|
"subitem": "サブアイテム",
|
||||||
"subitems": "サブアイテム",
|
|
||||||
"subitem_of": "のサブアイテム",
|
|
||||||
"variant": "バリアント",
|
"variant": "バリアント",
|
||||||
"variants": "バリアント",
|
|
||||||
"variant_of": "のバリアント",
|
|
||||||
"summary": "概要",
|
"summary": "概要",
|
||||||
"audio": "オーディオ",
|
"audio": "オーディオ",
|
||||||
"video": "ビデオ",
|
"video": "ビデオ",
|
||||||
"textual": "テキスト",
|
"textual": "テキスト",
|
||||||
"game": "ゲーム",
|
"game": "ゲーム",
|
||||||
"other": "他",
|
"other": "他",
|
||||||
"return_to": "戻る",
|
|
||||||
"left_to_right": "左から右へ",
|
"left_to_right": "左から右へ",
|
||||||
"right_to_left": "右から左へ",
|
"right_to_left": "右から左へ",
|
||||||
"page": "ページ",
|
"page": "ページ",
|
||||||
"pages": "ページ",
|
|
||||||
"page_order": "ページ順序",
|
"page_order": "ページ順序",
|
||||||
"binding": "製本",
|
"binding": "製本",
|
||||||
"type_information": "タイプ情報",
|
"type_information": "タイプ情報",
|
||||||
|
@ -450,15 +414,12 @@
|
||||||
"view_scans": "スキャンを開放",
|
"view_scans": "スキャンを開放",
|
||||||
"paperback": "ペーパーバック",
|
"paperback": "ペーパーバック",
|
||||||
"hardcover": "ハードカバー",
|
"hardcover": "ハードカバー",
|
||||||
"languages": "言語",
|
|
||||||
"select_language": "言語を選択する",
|
"select_language": "言語を選択する",
|
||||||
"language": "言語",
|
"language": "言語",
|
||||||
"library_description": "ヨコベースの副教材(書籍、小説、画集、舞台劇、漫画、ドラマCD、コミック)を網羅したリストです。それぞれについて、写真、スキャン、内容の書き起こし、どんなものなのか、いつ、どのように発売されたのか、サイズ、初回価格...などの情報を掲載しています。",
|
"library_description": "ヨコベースの副教材(書籍、小説、画集、舞台劇、漫画、ドラマCD、コミック)を網羅したリストです。それぞれについて、写真、スキャン、内容の書き起こし、どんなものなのか、いつ、どのように発売されたのか、サイズ、初回価格...などの情報を掲載しています。",
|
||||||
"wiki_description": "DrakeNieRに関連するすべての百科事典です。現在は年表のみですが、今後多くのページを公開予定です",
|
"wiki_description": "DrakeNieRに関連するすべての百科事典です。現在は年表のみですが、今後多くのページを公開予定です",
|
||||||
"chronicles_description": "Accord's Libraryは、ヨーコ・タローの全作品を収集・保存することを目的としています。ヨーコ・タローは、日本のゲームディレクター、シナリオライターです。",
|
"chronicles_description": "Accord's Libraryは、ヨーコ・タローの全作品を収集・保存することを目的としています。ヨーコ・タローは、日本のゲームディレクター、シナリオライターです。",
|
||||||
"news_description": "レコーダーが書いたニュース記事です ここでは、新しい商品/アイテムのリリースに関するお知らせ、ガイド、セオリー、アンボックス、ショーケース...をご紹介しています。",
|
"news_description": "レコーダーが書いたニュース記事です ここでは、新しい商品/アイテムのリリースに関するお知らせ、ガイド、セオリー、アンボックス、ショーケース...をご紹介しています。",
|
||||||
"merch_description": "",
|
|
||||||
"gallery_description": "",
|
|
||||||
"archives_description": "",
|
"archives_description": "",
|
||||||
"about_us_description": "Accord's Libraryプロジェクトについては、以下のページで詳しくご紹介しています。",
|
"about_us_description": "Accord's Libraryプロジェクトについては、以下のページで詳しくご紹介しています。",
|
||||||
"page_not_found": "ページが見つかりません",
|
"page_not_found": "ページが見つかりません",
|
||||||
|
@ -467,8 +428,6 @@
|
||||||
"show_subitems": "サブアイテムをみせる",
|
"show_subitems": "サブアイテムをみせる",
|
||||||
"show_primary_items": "一次のイテムをみせる",
|
"show_primary_items": "一次のイテムをみせる",
|
||||||
"show_secondary_items": "二次のイテムをみせる",
|
"show_secondary_items": "二次のイテムをみせる",
|
||||||
"no_type": "タイプなし",
|
|
||||||
"no_year": "年なし",
|
|
||||||
"order_by": "注文する",
|
"order_by": "注文する",
|
||||||
"group_by": "グループ化する",
|
"group_by": "グループ化する",
|
||||||
"select_option_sidebar": "サイドバーのオプションを選択します",
|
"select_option_sidebar": "サイドバーのオプションを選択します",
|
||||||
|
@ -499,20 +458,14 @@
|
||||||
"translation_notice": "このコンテンツはファンによる翻訳です",
|
"translation_notice": "このコンテンツはファンによる翻訳です",
|
||||||
"source_language": "ソース言語",
|
"source_language": "ソース言語",
|
||||||
"pronouns": "代名詞",
|
"pronouns": "代名詞",
|
||||||
"no_category": "カテゴリーなし",
|
|
||||||
"item": "項目",
|
"item": "項目",
|
||||||
"items": "項目",
|
|
||||||
"content": "コンテンツ",
|
"content": "コンテンツ",
|
||||||
"result": "結果",
|
|
||||||
"results": "結果",
|
|
||||||
"language_switch_message": null,
|
|
||||||
"open_settings": "オープン設定",
|
"open_settings": "オープン設定",
|
||||||
"change_language": "言語を変更する",
|
"change_language": "言語を変更する",
|
||||||
"open_search": "オープンサーチ",
|
"open_search": "オープンサーチ",
|
||||||
"chronology": "年表",
|
"chronology": "年表",
|
||||||
"accords_handbook": "アコードの手引き",
|
"accords_handbook": "アコードの手引き",
|
||||||
"legality": "合法性",
|
"legality": "合法性",
|
||||||
"members": "メンバー紹介",
|
|
||||||
"sharing_policy": "共有ポリシー",
|
"sharing_policy": "共有ポリシー",
|
||||||
"contact_us": "お問い合わせ",
|
"contact_us": "お問い合わせ",
|
||||||
"email": "電子メール",
|
"email": "電子メール",
|
||||||
|
@ -545,7 +498,6 @@
|
||||||
"only_display_unmarked_items": "無印のアイテムのみ表示",
|
"only_display_unmarked_items": "無印のアイテムのみ表示",
|
||||||
"display_all_items": "すべての項目を表示する",
|
"display_all_items": "すべての項目を表示する",
|
||||||
"table_of_contents": "目次",
|
"table_of_contents": "目次",
|
||||||
"definition": "定義",
|
|
||||||
"no_results_message": "結果が出ません。検索条件を変更またはリセットしてみてください。",
|
"no_results_message": "結果が出ません。検索条件を変更またはリセットしてみてください。",
|
||||||
"all": "すべて",
|
"all": "すべて",
|
||||||
"special_pages": "特設ページ",
|
"special_pages": "特設ページ",
|
||||||
|
@ -583,7 +535,13 @@
|
||||||
"most_popular": null,
|
"most_popular": null,
|
||||||
"shortest": null,
|
"shortest": null,
|
||||||
"longest": null,
|
"longest": null,
|
||||||
"search": null
|
"search": null,
|
||||||
|
"showing_x_out_of_y_results": null,
|
||||||
|
"return_to_x": null,
|
||||||
|
"x_results": null,
|
||||||
|
"definition_x": null,
|
||||||
|
"subitem_of_x": null,
|
||||||
|
"variant_of_x": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -598,7 +556,6 @@
|
||||||
"wiki_short_description": "Una enciclopedia para todo lo relacionado con DrakeNieR",
|
"wiki_short_description": "Una enciclopedia para todo lo relacionado con DrakeNieR",
|
||||||
"chronicles_short_description": "Experimenta todos los eventos y contenidos en orden cronológico",
|
"chronicles_short_description": "Experimenta todos los eventos y contenidos en orden cronológico",
|
||||||
"news": "Novedades",
|
"news": "Novedades",
|
||||||
"merch": "Merch",
|
|
||||||
"gallery": "Galería",
|
"gallery": "Galería",
|
||||||
"archives": "Archivos",
|
"archives": "Archivos",
|
||||||
"about_us": "Sobre nosotros",
|
"about_us": "Sobre nosotros",
|
||||||
|
@ -607,7 +564,6 @@
|
||||||
"contents_description": "Todo el contenido (textual, audio y video) de la Biblioteca u otras fuentes en línea.",
|
"contents_description": "Todo el contenido (textual, audio y video) de la Biblioteca u otras fuentes en línea.",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"category": "Categoría",
|
"category": "Categoría",
|
||||||
"categories": "Categorías",
|
|
||||||
"size": "Tamaño",
|
"size": "Tamaño",
|
||||||
"release_date": "Fecha de lanzamiento",
|
"release_date": "Fecha de lanzamiento",
|
||||||
"release_year": "Año de lanzamiento",
|
"release_year": "Año de lanzamiento",
|
||||||
|
@ -617,22 +573,16 @@
|
||||||
"height": "Altura",
|
"height": "Altura",
|
||||||
"thickness": "Grosor",
|
"thickness": "Grosor",
|
||||||
"subitem": "Sub-item",
|
"subitem": "Sub-item",
|
||||||
"subitems": "Sub-items",
|
|
||||||
"subitem_of": "Sub-item de",
|
|
||||||
"variant": "Variante",
|
"variant": "Variante",
|
||||||
"variants": "Variantes",
|
|
||||||
"variant_of": "Variante de",
|
|
||||||
"summary": "Sumario",
|
"summary": "Sumario",
|
||||||
"audio": "Audio",
|
"audio": "Audio",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"textual": "Textual",
|
"textual": "Textual",
|
||||||
"game": "Juego",
|
"game": "Juego",
|
||||||
"other": "Otros",
|
"other": "Otros",
|
||||||
"return_to": "Volver a",
|
|
||||||
"left_to_right": "Izquierda a derecha",
|
"left_to_right": "Izquierda a derecha",
|
||||||
"right_to_left": "Derecha a izquierda",
|
"right_to_left": "Derecha a izquierda",
|
||||||
"page": "Página",
|
"page": "Página",
|
||||||
"pages": "Páginas",
|
|
||||||
"page_order": "Orden de las páginas",
|
"page_order": "Orden de las páginas",
|
||||||
"binding": "Encuadernación",
|
"binding": "Encuadernación",
|
||||||
"type_information": "Tipo de información",
|
"type_information": "Tipo de información",
|
||||||
|
@ -645,15 +595,12 @@
|
||||||
"view_scans": "Ver escaneos",
|
"view_scans": "Ver escaneos",
|
||||||
"paperback": "Tapa blanda",
|
"paperback": "Tapa blanda",
|
||||||
"hardcover": "Tapa dura",
|
"hardcover": "Tapa dura",
|
||||||
"languages": "Idiomas",
|
|
||||||
"select_language": "Seleccionar idioma",
|
"select_language": "Seleccionar idioma",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"library_description": "Una lista completa de todos los materiales complementarios de Yokoverse (libros, novelas, libros de arte, obras de teatro, manga, CDs novelizados y cómics). Para cada uno, proporcionamos fotos, escaneos y transcripciones del contenido, información sobre qué es, cuándo y cómo se ha publicado, tamaño, precio inicial...",
|
"library_description": "Una lista completa de todos los materiales complementarios de Yokoverse (libros, novelas, libros de arte, obras de teatro, manga, CDs novelizados y cómics). Para cada uno, proporcionamos fotos, escaneos y transcripciones del contenido, información sobre qué es, cuándo y cómo se ha publicado, tamaño, precio inicial...",
|
||||||
"wiki_description": "Una enciclopedia para todo lo relacionado con DrakeNieR. En este momento, solo tenemos la Cronología, ¡pero muchas más páginas están planeadas para ser publicadas!",
|
"wiki_description": "Una enciclopedia para todo lo relacionado con DrakeNieR. En este momento, solo tenemos la Cronología, ¡pero muchas más páginas están planeadas para ser publicadas!",
|
||||||
"chronicles_description": "",
|
"chronicles_description": "",
|
||||||
"news_description": "¡Nuevos artículos escritos por nuestros/as Archivistas! Aquí encontrarás anuncios sobre nuevos lanzamientos de merchandising/artículos, guías, teorías, unboxings, showcases...",
|
"news_description": "¡Nuevos artículos escritos por nuestros/as Archivistas! Aquí encontrarás anuncios sobre nuevos lanzamientos de merchandising/artículos, guías, teorías, unboxings, showcases...",
|
||||||
"merch_description": "",
|
|
||||||
"gallery_description": "Una galería completamente etiquetada de estilo Danbooru, actualmente con más de mil obras de arte oficiales únicas.",
|
|
||||||
"archives_description": "",
|
"archives_description": "",
|
||||||
"about_us_description": "Encuentra más información sobre el proyecto de Accord's Library en las siguientes páginas.",
|
"about_us_description": "Encuentra más información sobre el proyecto de Accord's Library en las siguientes páginas.",
|
||||||
"page_not_found": "Página no encontrada",
|
"page_not_found": "Página no encontrada",
|
||||||
|
@ -662,8 +609,6 @@
|
||||||
"show_subitems": "Mostrar sub-items",
|
"show_subitems": "Mostrar sub-items",
|
||||||
"show_primary_items": "Mostrar items principales",
|
"show_primary_items": "Mostrar items principales",
|
||||||
"show_secondary_items": "Mostrar items secundarios",
|
"show_secondary_items": "Mostrar items secundarios",
|
||||||
"no_type": "Ningún tipo",
|
|
||||||
"no_year": "Ningún año",
|
|
||||||
"order_by": "Ordenar por",
|
"order_by": "Ordenar por",
|
||||||
"group_by": "Agrupar por",
|
"group_by": "Agrupar por",
|
||||||
"select_option_sidebar": "Selecciona una de las opciones en la barra lateral",
|
"select_option_sidebar": "Selecciona una de las opciones en la barra lateral",
|
||||||
|
@ -671,7 +616,7 @@
|
||||||
"settings": "Ajustes",
|
"settings": "Ajustes",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"light": "Claro",
|
"light": "Claro",
|
||||||
"auto": "Auto",
|
"auto": "Automático",
|
||||||
"dark": "Oscuro",
|
"dark": "Oscuro",
|
||||||
"font_size": "Tamaño de la fuente",
|
"font_size": "Tamaño de la fuente",
|
||||||
"player_name": "Nombre del jugador/a",
|
"player_name": "Nombre del jugador/a",
|
||||||
|
@ -694,83 +639,76 @@
|
||||||
"translation_notice": "Este contenido es una traducción de fans",
|
"translation_notice": "Este contenido es una traducción de fans",
|
||||||
"source_language": "Idioma original",
|
"source_language": "Idioma original",
|
||||||
"pronouns": "Pronombres",
|
"pronouns": "Pronombres",
|
||||||
"no_category": "Ningún categoría",
|
"item": "Ítem",
|
||||||
"item": null,
|
"content": "Contenido",
|
||||||
"items": null,
|
"open_settings": "Abrir ajustes",
|
||||||
"content": null,
|
"change_language": "Cambiar idioma",
|
||||||
"result": null,
|
"open_search": "Abrir búsqueda",
|
||||||
"results": null,
|
"chronology": "Cronología",
|
||||||
"language_switch_message": null,
|
"accords_handbook": "Manual de Accord",
|
||||||
"open_settings": null,
|
"legality": "Legalidad",
|
||||||
"change_language": null,
|
"sharing_policy": "Política de Uso Compartido",
|
||||||
"open_search": null,
|
"contact_us": "Contáctanos",
|
||||||
"chronology": null,
|
|
||||||
"accords_handbook": null,
|
|
||||||
"legality": null,
|
|
||||||
"members": null,
|
|
||||||
"sharing_policy": null,
|
|
||||||
"contact_us": null,
|
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"email_gdpr_notice": "Solo usamos tu correo electrónico exclusivamente para contactarte en relación a tu solicitud. No compartimos este correo electrónico con nadie ni lo usamos para ningún otro propósito.",
|
"email_gdpr_notice": "Solo usamos tu correo electrónico exclusivamente para contactarte en relación a tu solicitud. No compartimos este correo electrónico con nadie ni lo usamos para ningún otro propósito.",
|
||||||
"message": null,
|
"message": "Mensaje",
|
||||||
"send": null,
|
"send": "Enviar",
|
||||||
"response_invalid_code": "El código de verificación es incorrecto.",
|
"response_invalid_code": "El código de verificación es incorrecto.",
|
||||||
"response_invalid_email": "¡Por favor, introduce una dirección de correo electrónico válida!",
|
"response_invalid_email": "¡Por favor, introduce una dirección de correo electrónico válida!",
|
||||||
"response_email_success": "¡Gracias por contactarnos! Nos pondremos en contacto contigo en breve.",
|
"response_email_success": "¡Gracias por contactarnos! Nos pondremos en contacto contigo en breve.",
|
||||||
"always_show_info": null,
|
"always_show_info": "Siempre mostrar la información",
|
||||||
"item_not_available": null,
|
"item_not_available": "Ítem no disponible",
|
||||||
"primary_language": null,
|
"primary_language": "Idioma primario",
|
||||||
"secondary_language": null,
|
"secondary_language": "Idioma secundario",
|
||||||
"combine_related_contents": null,
|
"combine_related_contents": "Combinar contenido relacionado",
|
||||||
"previous_content": null,
|
"previous_content": "Contenido anterior",
|
||||||
"followup_content": null,
|
"followup_content": "Contenido siguiente",
|
||||||
"videos": null,
|
"videos": "Vídeos",
|
||||||
"view_on": null,
|
"view_on": null,
|
||||||
"channel": null,
|
"channel": "Canal",
|
||||||
"subscribers": null,
|
"subscribers": "Suscriptores",
|
||||||
"description": null,
|
"description": "Descripción",
|
||||||
"available_at": null,
|
"available_at": "Disponible en",
|
||||||
"search_title": null,
|
"search_title": "Buscar título",
|
||||||
"want_it": null,
|
"want_it": "Lo quiero!",
|
||||||
"have_it": null,
|
"have_it": "Lo tengo!",
|
||||||
"source": null,
|
"source": "Fuente",
|
||||||
"reset_all_filters": null,
|
"reset_all_filters": "Restablecer todos los filtros",
|
||||||
"only_display_items_i_have": null,
|
"only_display_items_i_have": "Sólo mostrar lo que ya tengo",
|
||||||
"only_display_items_i_want": null,
|
"only_display_items_i_want": "Sólo mostrar lo que quiero",
|
||||||
"only_display_unmarked_items": null,
|
"only_display_unmarked_items": "Sólo mostrar ítems sin marcación",
|
||||||
"display_all_items": null,
|
"display_all_items": "Mostar todos los ítems",
|
||||||
"table_of_contents": null,
|
"table_of_contents": "Tabla de contenido",
|
||||||
"definition": null,
|
"no_results_message": "No hay resultados",
|
||||||
"no_results_message": null,
|
"all": "Todos",
|
||||||
"all": null,
|
"special_pages": "Páginas especiales",
|
||||||
"special_pages": null,
|
"scan": "Escaneos",
|
||||||
"scan": null,
|
"scanlation": "Escaneo/Traducción",
|
||||||
"scanlation": null,
|
"scanners": "Escaneadores",
|
||||||
"scanners": null,
|
"cleaners": "Limpiadores",
|
||||||
"cleaners": null,
|
"typesetters": "Maquetadores",
|
||||||
"typesetters": null,
|
"notes": "Notas",
|
||||||
"notes": null,
|
"cover": "Portada",
|
||||||
"cover": null,
|
"tags": "Etiquetas",
|
||||||
"tags": null,
|
"no_source_warning": "No hay fuente",
|
||||||
"no_source_warning": null,
|
"copy_anchor_link": "Copiar enlace de anclaje",
|
||||||
"copy_anchor_link": null,
|
"anchor_link_copied": "Enlace de anclaje copiado",
|
||||||
"anchor_link_copied": null,
|
"folders": "Carpetas",
|
||||||
"folders": null,
|
"empty_folder_message": "Carpeta vacía",
|
||||||
"empty_folder_message": null,
|
"switch_to_grid_view": "Cambiar a vista de retícula",
|
||||||
"switch_to_grid_view": null,
|
"switch_to_folder_view": "Cambiar a vista de carpeta",
|
||||||
"switch_to_folder_view": null,
|
"content_is_not_available": "Contenido no disponible",
|
||||||
"content_is_not_available": null,
|
"paper_texture": "Papel texturizado",
|
||||||
"paper_texture": null,
|
"book_fold": "Tapa dura",
|
||||||
"book_fold": null,
|
"lighting": "Iluminación",
|
||||||
"lighting": null,
|
"side_pages": "Contraportada",
|
||||||
"side_pages": null,
|
"shadow": "Sombra",
|
||||||
"shadow": null,
|
"night_reader": "Lector nocturno",
|
||||||
"night_reader": null,
|
"single_page_view": "Vista de página única",
|
||||||
"single_page_view": null,
|
"double_page_view": "Vista de página doble",
|
||||||
"double_page_view": null,
|
"reset_all_options": "Restablecer todas las opciones",
|
||||||
"reset_all_options": null,
|
"reading_layout": "Disposición de lectura",
|
||||||
"reading_layout": null,
|
"quality": "Calidad",
|
||||||
"quality": null,
|
|
||||||
"only_unavailable_videos": null,
|
"only_unavailable_videos": null,
|
||||||
"oldest": null,
|
"oldest": null,
|
||||||
"newest": null,
|
"newest": null,
|
||||||
|
@ -778,7 +716,13 @@
|
||||||
"most_popular": null,
|
"most_popular": null,
|
||||||
"shortest": null,
|
"shortest": null,
|
||||||
"longest": null,
|
"longest": null,
|
||||||
"search": null
|
"search": null,
|
||||||
|
"showing_x_out_of_y_results": null,
|
||||||
|
"return_to_x": null,
|
||||||
|
"x_results": null,
|
||||||
|
"definition_x": null,
|
||||||
|
"subitem_of_x": null,
|
||||||
|
"variant_of_x": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -793,7 +737,6 @@
|
||||||
"wiki_short_description": "Uma enciclopédia com tudo relacionado a DrakeNieR",
|
"wiki_short_description": "Uma enciclopédia com tudo relacionado a DrakeNieR",
|
||||||
"chronicles_short_description": "Explore as crônicas de DrakeNieR em ordem cronológica.",
|
"chronicles_short_description": "Explore as crônicas de DrakeNieR em ordem cronológica.",
|
||||||
"news": "Notícias",
|
"news": "Notícias",
|
||||||
"merch": "Mercadorias",
|
|
||||||
"gallery": "Galeria",
|
"gallery": "Galeria",
|
||||||
"archives": "Arquivos",
|
"archives": "Arquivos",
|
||||||
"about_us": "Sobre Nós",
|
"about_us": "Sobre Nós",
|
||||||
|
@ -802,7 +745,6 @@
|
||||||
"contents_description": "",
|
"contents_description": "",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"category": "Categoria",
|
"category": "Categoria",
|
||||||
"categories": "Categorias",
|
|
||||||
"size": "Tamanho",
|
"size": "Tamanho",
|
||||||
"release_date": "Dia de lançamento",
|
"release_date": "Dia de lançamento",
|
||||||
"release_year": "Ano de lançamento",
|
"release_year": "Ano de lançamento",
|
||||||
|
@ -812,22 +754,16 @@
|
||||||
"height": "Altura",
|
"height": "Altura",
|
||||||
"thickness": "Grossura",
|
"thickness": "Grossura",
|
||||||
"subitem": "Subitem",
|
"subitem": "Subitem",
|
||||||
"subitems": "Subitens",
|
|
||||||
"subitem_of": "Subitem de",
|
|
||||||
"variant": "Variante",
|
"variant": "Variante",
|
||||||
"variants": "Variantes",
|
|
||||||
"variant_of": "Variante de",
|
|
||||||
"summary": "Sumário",
|
"summary": "Sumário",
|
||||||
"audio": "Audio",
|
"audio": "Audio",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"textual": "Textos",
|
"textual": "Textos",
|
||||||
"game": "Jogos",
|
"game": "Jogos",
|
||||||
"other": "Outros",
|
"other": "Outros",
|
||||||
"return_to": "Voltar para",
|
|
||||||
"left_to_right": "Esquerda para direita",
|
"left_to_right": "Esquerda para direita",
|
||||||
"right_to_left": "Direita para esquerda",
|
"right_to_left": "Direita para esquerda",
|
||||||
"page": "Página",
|
"page": "Página",
|
||||||
"pages": "Páginas",
|
|
||||||
"page_order": "Ordem de páginas",
|
"page_order": "Ordem de páginas",
|
||||||
"binding": "Encadernação",
|
"binding": "Encadernação",
|
||||||
"type_information": "Informação do tipo",
|
"type_information": "Informação do tipo",
|
||||||
|
@ -840,15 +776,12 @@
|
||||||
"view_scans": "Ver scans",
|
"view_scans": "Ver scans",
|
||||||
"paperback": "Brochura",
|
"paperback": "Brochura",
|
||||||
"hardcover": "Capa dura",
|
"hardcover": "Capa dura",
|
||||||
"languages": "Línguas",
|
|
||||||
"select_language": "Selecionar língua",
|
"select_language": "Selecionar língua",
|
||||||
"language": "Língua",
|
"language": "Língua",
|
||||||
"library_description": "",
|
"library_description": "",
|
||||||
"wiki_description": null,
|
"wiki_description": null,
|
||||||
"chronicles_description": null,
|
"chronicles_description": null,
|
||||||
"news_description": "",
|
"news_description": "",
|
||||||
"merch_description": "",
|
|
||||||
"gallery_description": "",
|
|
||||||
"archives_description": "",
|
"archives_description": "",
|
||||||
"about_us_description": "",
|
"about_us_description": "",
|
||||||
"page_not_found": "Página não encontrada",
|
"page_not_found": "Página não encontrada",
|
||||||
|
@ -857,8 +790,6 @@
|
||||||
"show_subitems": "Mostrar subitens",
|
"show_subitems": "Mostrar subitens",
|
||||||
"show_primary_items": "Mostrar itens primários",
|
"show_primary_items": "Mostrar itens primários",
|
||||||
"show_secondary_items": "Mostrar itens secundários",
|
"show_secondary_items": "Mostrar itens secundários",
|
||||||
"no_type": "Sem tipo",
|
|
||||||
"no_year": "Sem ano",
|
|
||||||
"order_by": "Ordenar por",
|
"order_by": "Ordenar por",
|
||||||
"group_by": "Agrupar por",
|
"group_by": "Agrupar por",
|
||||||
"select_option_sidebar": "Selecione uma opção na aba lateral",
|
"select_option_sidebar": "Selecione uma opção na aba lateral",
|
||||||
|
@ -889,20 +820,14 @@
|
||||||
"translation_notice": "Este conteúdo é uma tradução de fã.",
|
"translation_notice": "Este conteúdo é uma tradução de fã.",
|
||||||
"source_language": "Língua original",
|
"source_language": "Língua original",
|
||||||
"pronouns": "Pronomes",
|
"pronouns": "Pronomes",
|
||||||
"no_category": "Sem Categoria",
|
|
||||||
"item": "Item",
|
"item": "Item",
|
||||||
"items": "Itens",
|
|
||||||
"content": "Conteúdo",
|
"content": "Conteúdo",
|
||||||
"result": "Resultado",
|
|
||||||
"results": "Resultados",
|
|
||||||
"language_switch_message": "Este conteúdo não está disponível na língua selecionada. Você pode escolher uma das seguintes línguas:",
|
|
||||||
"open_settings": "Abrir configurações",
|
"open_settings": "Abrir configurações",
|
||||||
"change_language": "Mudar língua",
|
"change_language": "Mudar língua",
|
||||||
"open_search": "Abrir pesquisa",
|
"open_search": "Abrir pesquisa",
|
||||||
"chronology": "Cronologia",
|
"chronology": "Cronologia",
|
||||||
"accords_handbook": "Livro de mão da Accord",
|
"accords_handbook": "Livro de mão da Accord",
|
||||||
"legality": "Legalidade",
|
"legality": "Legalidade",
|
||||||
"members": "Membros",
|
|
||||||
"sharing_policy": "Política de compartilhamento",
|
"sharing_policy": "Política de compartilhamento",
|
||||||
"contact_us": "Fale conosco",
|
"contact_us": "Fale conosco",
|
||||||
"email": null,
|
"email": null,
|
||||||
|
@ -935,7 +860,6 @@
|
||||||
"only_display_unmarked_items": null,
|
"only_display_unmarked_items": null,
|
||||||
"display_all_items": null,
|
"display_all_items": null,
|
||||||
"table_of_contents": null,
|
"table_of_contents": null,
|
||||||
"definition": null,
|
|
||||||
"no_results_message": null,
|
"no_results_message": null,
|
||||||
"all": null,
|
"all": null,
|
||||||
"special_pages": null,
|
"special_pages": null,
|
||||||
|
@ -973,7 +897,13 @@
|
||||||
"most_popular": null,
|
"most_popular": null,
|
||||||
"shortest": null,
|
"shortest": null,
|
||||||
"longest": null,
|
"longest": null,
|
||||||
"search": null
|
"search": null,
|
||||||
|
"showing_x_out_of_y_results": null,
|
||||||
|
"return_to_x": null,
|
||||||
|
"x_results": null,
|
||||||
|
"definition_x": null,
|
||||||
|
"subitem_of_x": null,
|
||||||
|
"variant_of_x": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { Ico } from "./Ico";
|
import { Ico } from "./Ico";
|
||||||
import { ToolTip } from "./ToolTip";
|
import { ToolTip } from "./ToolTip";
|
||||||
import { cJoin } from "helpers/className";
|
import { cJoin } from "helpers/className";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { atoms } from "contexts/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -17,10 +16,10 @@ interface Props {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const AnchorShare = ({ id, className }: Props): JSX.Element => {
|
export const AnchorShare = ({ id, className }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<ToolTip content={langui.copy_anchor_link} trigger="mouseenter" className="text-sm">
|
<ToolTip content={format("copy_anchor_link")} trigger="mouseenter" className="text-sm">
|
||||||
<ToolTip content={langui.anchor_link_copied} trigger="click" className="text-sm">
|
<ToolTip content={format("anchor_link_copied")} trigger="click" className="text-sm">
|
||||||
<Ico
|
<Ico
|
||||||
icon="link"
|
icon="link"
|
||||||
className={cJoin("cursor-pointer transition-colors hover:text-dark", className)}
|
className={cJoin("cursor-pointer transition-colors hover:text-dark", className)}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -48,7 +49,7 @@ export const AppLayout = ({
|
||||||
const [isMainPanelOpened, setMainPanelOpened] = useAtomPair(atoms.layout.mainPanelOpened);
|
const [isMainPanelOpened, setMainPanelOpened] = useAtomPair(atoms.layout.mainPanelOpened);
|
||||||
const isMenuGesturesEnabled = useAtomGetter(atoms.layout.menuGesturesEnabled);
|
const isMenuGesturesEnabled = useAtomGetter(atoms.layout.menuGesturesEnabled);
|
||||||
|
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
|
|
||||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
const isScreenAtLeastXs = useAtomGetter(atoms.containerQueries.isScreenAtLeastXs);
|
const isScreenAtLeastXs = useAtomGetter(atoms.containerQueries.isScreenAtLeastXs);
|
||||||
|
@ -149,7 +150,7 @@ export const AppLayout = ({
|
||||||
{isDefined(contentPanel) ? (
|
{isDefined(contentPanel) ? (
|
||||||
contentPanel
|
contentPanel
|
||||||
) : (
|
) : (
|
||||||
<ContentPlaceholder message={langui.select_option_sidebar ?? ""} icon={"chevron_left"} />
|
<ContentPlaceholder message={format("select_option_sidebar")} icon={"chevron_left"} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { PageSelector } from "components/Inputs/PageSelector";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { isUndefined } from "helpers/asserts";
|
import { isUndefined } from "helpers/asserts";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
|
|
||||||
|
@ -48,14 +49,14 @@ export const Paginator = ({
|
||||||
|
|
||||||
const DefaultRenderWhenEmpty = () => {
|
const DefaultRenderWhenEmpty = () => {
|
||||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<div className="grid h-full place-content-center">
|
<div className="grid h-full place-content-center">
|
||||||
<div
|
<div
|
||||||
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
||||||
border-dark p-8 text-dark opacity-40">
|
border-dark p-8 text-dark opacity-40">
|
||||||
{is3ColumnsLayout && <Ico icon="chevron_left" className="!text-[300%]" />}
|
{is3ColumnsLayout && <Ico icon="chevron_left" className="!text-[300%]" />}
|
||||||
<p className="max-w-xs text-2xl">{langui.no_results_message}</p>
|
<p className="max-w-xs text-2xl">{format("no_results_message")}</p>
|
||||||
{!is3ColumnsLayout && <Ico icon="chevron_right" className="!text-[300%]" />}
|
{!is3ColumnsLayout && <Ico icon="chevron_right" className="!text-[300%]" />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,8 +3,7 @@ import { ToolTip } from "components/ToolTip";
|
||||||
import { LibraryItemUserStatus } from "types/types";
|
import { LibraryItemUserStatus } from "types/types";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -20,7 +19,7 @@ interface Props {
|
||||||
|
|
||||||
export const PreviewCardCTAs = ({ id, expand = false }: Props): JSX.Element => {
|
export const PreviewCardCTAs = ({ id, expand = false }: Props): JSX.Element => {
|
||||||
const { libraryItemUserStatus, setLibraryItemUserStatus } = useLibraryItemUserStatus();
|
const { libraryItemUserStatus, setLibraryItemUserStatus } = useLibraryItemUserStatus();
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -28,10 +27,10 @@ export const PreviewCardCTAs = ({ id, expand = false }: Props): JSX.Element => {
|
||||||
"flex flex-row flex-wrap place-content-center place-items-center",
|
"flex flex-row flex-wrap place-content-center place-items-center",
|
||||||
cIf(expand, "gap-4", "gap-2")
|
cIf(expand, "gap-4", "gap-2")
|
||||||
)}>
|
)}>
|
||||||
<ToolTip content={langui.want_it} disabled={expand}>
|
<ToolTip content={format("want_it")} disabled={expand}>
|
||||||
<Button
|
<Button
|
||||||
icon="favorite"
|
icon="favorite"
|
||||||
text={expand ? langui.want_it : undefined}
|
text={expand ? format("want_it") : undefined}
|
||||||
active={libraryItemUserStatus[id] === LibraryItemUserStatus.Want}
|
active={libraryItemUserStatus[id] === LibraryItemUserStatus.Want}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -46,10 +45,10 @@ export const PreviewCardCTAs = ({ id, expand = false }: Props): JSX.Element => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
<ToolTip content={langui.have_it} disabled={expand}>
|
<ToolTip content={format("have_it")} disabled={expand}>
|
||||||
<Button
|
<Button
|
||||||
icon="back_hand"
|
icon="back_hand"
|
||||||
text={expand ? langui.have_it : undefined}
|
text={expand ? format("have_it") : undefined}
|
||||||
active={libraryItemUserStatus[id] === LibraryItemUserStatus.Have}
|
active={libraryItemUserStatus[id] === LibraryItemUserStatus.Have}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { Link } from "components/Inputs/Link";
|
import { Link } from "components/Inputs/Link";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -225,7 +226,7 @@ export const TableOfContents = ({
|
||||||
title,
|
title,
|
||||||
horizontalLine = false,
|
horizontalLine = false,
|
||||||
}: TableOfContentsProps): JSX.Element => {
|
}: TableOfContentsProps): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const toc = getTocFromMarkdawn(preprocessMarkDawn(text), title);
|
const toc = getTocFromMarkdawn(preprocessMarkDawn(text), title);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -233,7 +234,7 @@ export const TableOfContents = ({
|
||||||
{toc.children.length > 0 && (
|
{toc.children.length > 0 && (
|
||||||
<>
|
<>
|
||||||
{horizontalLine && <HorizontalLine />}
|
{horizontalLine && <HorizontalLine />}
|
||||||
<h3 className="text-xl">{langui.table_of_contents}</h3>
|
<h3 className="text-xl">{format("table_of_contents")}</h3>
|
||||||
<div className="max-w-[14.5rem] text-left">
|
<div className="max-w-[14.5rem] text-left">
|
||||||
<p
|
<p
|
||||||
className="relative my-2 overflow-x-hidden text-ellipsis whitespace-nowrap
|
className="relative my-2 overflow-x-hidden text-ellipsis whitespace-nowrap
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { isUndefined } from "helpers/asserts";
|
import { isUndefined } from "helpers/asserts";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -22,7 +23,7 @@ interface Props {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props): JSX.Element => {
|
export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -31,7 +32,7 @@ export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props):
|
||||||
(!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
|
(!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
|
||||||
isUndefined(displayOnlyOn)) && (
|
isUndefined(displayOnlyOn)) && (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Button href={href} text={`${langui.return_to} ${title}`} icon="navigate_before" />
|
<Button href={href} text={format("return_to_x", { x: title })} icon="navigate_before" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { ColoredSvg } from "components/ColoredSvg";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter, useAtomPair, useAtomSetter } from "helpers/atoms";
|
import { useAtomGetter, useAtomPair, useAtomSetter } from "helpers/atoms";
|
||||||
import { Markdawn } from "components/Markdown/Markdawn";
|
import { Markdawn } from "components/Markdown/Markdawn";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -18,7 +19,7 @@ import { Markdawn } from "components/Markdown/Markdawn";
|
||||||
|
|
||||||
export const MainPanel = (): JSX.Element => {
|
export const MainPanel = (): JSX.Element => {
|
||||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const [isMainPanelReduced, setMainPanelReduced] = useAtomPair(atoms.layout.mainPanelReduced);
|
const [isMainPanelReduced, setMainPanelReduced] = useAtomPair(atoms.layout.mainPanelReduced);
|
||||||
const setSettingsOpened = useAtomSetter(atoms.layout.settingsOpened);
|
const setSettingsOpened = useAtomSetter(atoms.layout.settingsOpened);
|
||||||
const setSearchOpened = useAtomSetter(atoms.layout.searchOpened);
|
const setSearchOpened = useAtomSetter(atoms.layout.searchOpened);
|
||||||
|
@ -72,7 +73,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
cIf(isMainPanelReduced && is3ColumnsLayout, "flex-col gap-3", "flex-row")
|
cIf(isMainPanelReduced && is3ColumnsLayout, "flex-col gap-3", "flex-row")
|
||||||
)}>
|
)}>
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={<h3 className="text-2xl">{langui.open_settings}</h3>}
|
content={<h3 className="text-2xl">{format("open_settings")}</h3>}
|
||||||
placement={isMainPanelReduced ? "right" : "top"}>
|
placement={isMainPanelReduced ? "right" : "top"}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -83,7 +84,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={<h3 className="text-2xl">{langui.open_search}</h3>}
|
content={<h3 className="text-2xl">{format("open_search")}</h3>}
|
||||||
placement={isMainPanelReduced ? "right" : "top"}>
|
placement={isMainPanelReduced ? "right" : "top"}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -102,32 +103,32 @@ export const MainPanel = (): JSX.Element => {
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/library"
|
url="/library"
|
||||||
icon="auto_stories"
|
icon="auto_stories"
|
||||||
title={langui.library}
|
title={format("library")}
|
||||||
subtitle={langui.library_short_description}
|
subtitle={format("library_short_description")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/contents"
|
url="/contents"
|
||||||
icon="workspaces"
|
icon="workspaces"
|
||||||
title={langui.contents}
|
title={format("contents")}
|
||||||
subtitle={langui.contents_short_description}
|
subtitle={format("contents_short_description")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/wiki"
|
url="/wiki"
|
||||||
icon="travel_explore"
|
icon="travel_explore"
|
||||||
title={langui.wiki}
|
title={format("wiki")}
|
||||||
subtitle={langui.wiki_short_description}
|
subtitle={format("wiki_short_description")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/chronicles"
|
url="/chronicles"
|
||||||
icon="schedule"
|
icon="schedule"
|
||||||
title={langui.chronicles}
|
title={format("chronicles")}
|
||||||
subtitle={langui.chronicles_short_description}
|
subtitle={format("chronicles_short_description")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -136,7 +137,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/news"
|
url="/news"
|
||||||
icon="newspaper"
|
icon="newspaper"
|
||||||
title={langui.news}
|
title={format("news")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -144,7 +145,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/merch"
|
url="/merch"
|
||||||
icon="store"
|
icon="store"
|
||||||
title={langui.merch}
|
title={format("merch")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
*/}
|
*/}
|
||||||
|
@ -152,30 +153,30 @@ export const MainPanel = (): JSX.Element => {
|
||||||
<NavOption
|
<NavOption
|
||||||
url="https://gallery.accords-library.com/posts/"
|
url="https://gallery.accords-library.com/posts/"
|
||||||
icon="perm_media"
|
icon="perm_media"
|
||||||
title={langui.gallery}
|
title={format("gallery")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/archives"
|
url="/archives"
|
||||||
icon="save"
|
icon="save"
|
||||||
title={langui.archives}
|
title={format("archives")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/about-us"
|
url="/about-us"
|
||||||
icon="info"
|
icon="info"
|
||||||
title={langui.about_us}
|
title={format("about_us")}
|
||||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{(!isMainPanelReduced || !is3ColumnsLayout) && <HorizontalLine />}
|
{(!isMainPanelReduced || !is3ColumnsLayout) && <HorizontalLine />}
|
||||||
|
|
||||||
<div className={cJoin("text-center", cIf(isMainPanelReduced && is3ColumnsLayout, "hidden"))}>
|
<div className={cJoin("text-center", cIf(isMainPanelReduced && is3ColumnsLayout, "hidden"))}>
|
||||||
{isDefinedAndNotEmpty(langui.licensing_notice) && (
|
{isDefinedAndNotEmpty(format("licensing_notice")) && (
|
||||||
<p>
|
<p>
|
||||||
<Markdawn text={langui.licensing_notice} />
|
<Markdawn text={format("licensing_notice")} />
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="mt-4 mb-8 grid place-content-center">
|
<div className="mt-4 mb-8 grid place-content-center">
|
||||||
|
@ -199,9 +200,9 @@ export const MainPanel = (): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{isDefinedAndNotEmpty(langui.copyright_notice) && (
|
{isDefinedAndNotEmpty(format("copyright_notice")) && (
|
||||||
<p>
|
<p>
|
||||||
<Markdawn text={langui.copyright_notice} />
|
<Markdawn text={format("copyright_notice")} />
|
||||||
</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">
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { MaterialSymbol } from "material-symbols";
|
||||||
import { Popup } from "components/Containers/Popup";
|
import { Popup } from "components/Containers/Popup";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomPair, useAtomGetter } from "helpers/atoms";
|
import { useAtomPair } from "helpers/atoms";
|
||||||
import { TextInput } from "components/Inputs/TextInput";
|
import { TextInput } from "components/Inputs/TextInput";
|
||||||
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
||||||
import { PreviewCard, TranslatedPreviewCard } from "components/PreviewCard";
|
import { PreviewCard, TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
|
@ -20,6 +20,7 @@ import { getVideoThumbnailURL } from "helpers/videos";
|
||||||
import { UpPressable } from "components/Containers/UpPressable";
|
import { UpPressable } from "components/Containers/UpPressable";
|
||||||
import { prettyItemSubType, prettySlug } from "helpers/formatters";
|
import { prettyItemSubType, prettySlug } from "helpers/formatters";
|
||||||
import { Ico } from "components/Ico";
|
import { Ico } from "components/Ico";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -36,7 +37,7 @@ const SEARCH_LIMIT = 8;
|
||||||
export const SearchPopup = (): JSX.Element => {
|
export const SearchPopup = (): JSX.Element => {
|
||||||
const [isSearchOpened, setSearchOpened] = useAtomPair(atoms.layout.searchOpened);
|
const [isSearchOpened, setSearchOpened] = useAtomPair(atoms.layout.searchOpened);
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const [libraryItems, setLibraryItems] = useState<CustomSearchResponse<MeiliLibraryItem>>();
|
const [libraryItems, setLibraryItems] = useState<CustomSearchResponse<MeiliLibraryItem>>();
|
||||||
const [contents, setContents] = useState<CustomSearchResponse<MeiliContent>>();
|
const [contents, setContents] = useState<CustomSearchResponse<MeiliContent>>();
|
||||||
const [videos, setVideos] = useState<CustomSearchResponse<MeiliVideo>>();
|
const [videos, setVideos] = useState<CustomSearchResponse<MeiliVideo>>();
|
||||||
|
@ -178,14 +179,14 @@ export const SearchPopup = (): JSX.Element => {
|
||||||
fillViewport>
|
fillViewport>
|
||||||
<h2 className="inline-flex place-items-center gap-2 text-2xl">
|
<h2 className="inline-flex place-items-center gap-2 text-2xl">
|
||||||
<Ico icon="search" isFilled />
|
<Ico icon="search" isFilled />
|
||||||
{langui.search}
|
{format("search")}
|
||||||
</h2>
|
</h2>
|
||||||
<TextInput onChange={setQuery} value={query} placeholder={langui.search_title} />
|
<TextInput onChange={setQuery} value={query} placeholder={format("search_title")} />
|
||||||
|
|
||||||
<div className="flex w-full flex-wrap gap-12 gap-x-16">
|
<div className="flex w-full flex-wrap gap-12 gap-x-16">
|
||||||
{isDefined(libraryItems) && (
|
{isDefined(libraryItems) && (
|
||||||
<SearchResultSection
|
<SearchResultSection
|
||||||
title={langui.library}
|
title={format("library")}
|
||||||
icon="auto_stories"
|
icon="auto_stories"
|
||||||
href={`/library?page=1&query=${query}\
|
href={`/library?page=1&query=${query}\
|
||||||
&sort=0&primary=true&secondary=true&subitems=true&status=all`}
|
&sort=0&primary=true&secondary=true&subitems=true&status=all`}
|
||||||
|
@ -232,7 +233,7 @@ export const SearchPopup = (): JSX.Element => {
|
||||||
|
|
||||||
{isDefined(contents) && (
|
{isDefined(contents) && (
|
||||||
<SearchResultSection
|
<SearchResultSection
|
||||||
title={langui.contents}
|
title={format("contents")}
|
||||||
icon="workspaces"
|
icon="workspaces"
|
||||||
href={`/contents/all?page=1&query=${query}&sort=0`}
|
href={`/contents/all?page=1&query=${query}&sort=0`}
|
||||||
totalHits={contents.estimatedTotalHits}>
|
totalHits={contents.estimatedTotalHits}>
|
||||||
|
@ -276,7 +277,7 @@ export const SearchPopup = (): JSX.Element => {
|
||||||
|
|
||||||
{isDefined(wikiPages) && (
|
{isDefined(wikiPages) && (
|
||||||
<SearchResultSection
|
<SearchResultSection
|
||||||
title={langui.wiki}
|
title={format("wiki")}
|
||||||
icon="travel_explore"
|
icon="travel_explore"
|
||||||
href={`/wiki?page=1&query=${query}`}
|
href={`/wiki?page=1&query=${query}`}
|
||||||
totalHits={wikiPages.estimatedTotalHits}>
|
totalHits={wikiPages.estimatedTotalHits}>
|
||||||
|
@ -327,7 +328,7 @@ export const SearchPopup = (): JSX.Element => {
|
||||||
|
|
||||||
{isDefined(posts) && (
|
{isDefined(posts) && (
|
||||||
<SearchResultSection
|
<SearchResultSection
|
||||||
title={langui.news}
|
title={format("news")}
|
||||||
icon="newspaper"
|
icon="newspaper"
|
||||||
href={`/news?page=1&query=${query}`}
|
href={`/news?page=1&query=${query}`}
|
||||||
totalHits={posts.estimatedTotalHits}>
|
totalHits={posts.estimatedTotalHits}>
|
||||||
|
@ -369,7 +370,7 @@ export const SearchPopup = (): JSX.Element => {
|
||||||
|
|
||||||
{isDefined(videos) && (
|
{isDefined(videos) && (
|
||||||
<SearchResultSection
|
<SearchResultSection
|
||||||
title={langui.videos}
|
title={format("videos")}
|
||||||
icon="movie"
|
icon="movie"
|
||||||
href={`/archives/videos?page=1&query=${query}&sort=1&gone=`}
|
href={`/archives/videos?page=1&query=${query}&sort=1&gone=`}
|
||||||
totalHits={videos.estimatedTotalHits}>
|
totalHits={videos.estimatedTotalHits}>
|
||||||
|
@ -424,7 +425,9 @@ const SearchResultSection = ({
|
||||||
href,
|
href,
|
||||||
totalHits,
|
totalHits,
|
||||||
children,
|
children,
|
||||||
}: SearchResultSectionProps) => (
|
}: SearchResultSectionProps) => {
|
||||||
|
const { format } = useFormat();
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
{isDefined(totalHits) && totalHits > 0 && (
|
{isDefined(totalHits) && totalHits > 0 && (
|
||||||
<div>
|
<div>
|
||||||
|
@ -436,8 +439,9 @@ const SearchResultSection = ({
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers text-lg">{title}</p>
|
<p className="font-headers text-lg">{title}</p>
|
||||||
{isDefined(totalHits) && totalHits > SEARCH_LIMIT && (
|
{isDefined(totalHits) && totalHits > SEARCH_LIMIT && (
|
||||||
/* TODO: Langui */
|
<p className="text-sm">
|
||||||
<p className="text-sm">{`(showing ${SEARCH_LIMIT} out of ${totalHits} results)`}</p>
|
({format("showing_x_out_of_y_results", { x: SEARCH_LIMIT, y: totalHits })})
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</UpPressable>
|
</UpPressable>
|
||||||
|
@ -447,3 +451,4 @@ const SearchResultSection = ({
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||||
import { ThemeMode } from "contexts/settings";
|
import { ThemeMode } from "contexts/settings";
|
||||||
import { Ico } from "components/Ico";
|
import { Ico } from "components/Ico";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
export const SettingsPopup = (): JSX.Element => {
|
export const SettingsPopup = (): JSX.Element => {
|
||||||
const [preferredLanguages, setPreferredLanguages] = useAtomPair(
|
const [preferredLanguages, setPreferredLanguages] = useAtomPair(
|
||||||
|
@ -27,7 +28,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
const [themeMode, setThemeMode] = useAtomPair(atoms.settings.themeMode);
|
const [themeMode, setThemeMode] = useAtomPair(atoms.settings.themeMode);
|
||||||
|
|
||||||
const languages = useAtomGetter(atoms.localData.languages);
|
const languages = useAtomGetter(atoms.localData.languages);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const currencies = useAtomGetter(atoms.localData.currencies);
|
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||||
|
|
||||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
|
@ -52,7 +53,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
}}>
|
}}>
|
||||||
<h2 className="inline-flex place-items-center gap-2 text-2xl">
|
<h2 className="inline-flex place-items-center gap-2 text-2xl">
|
||||||
<Ico icon="discover_tune" isFilled />
|
<Ico icon="discover_tune" isFilled />
|
||||||
{langui.settings}
|
{format("settings")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -62,7 +63,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
)}>
|
)}>
|
||||||
{router.locales && (
|
{router.locales && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl">{langui.languages}</h3>
|
<h3 className="text-xl">{format("language", { count: preferredLanguages.length })}</h3>
|
||||||
{preferredLanguages.length > 0 && (
|
{preferredLanguages.length > 0 && (
|
||||||
<OrderableList
|
<OrderableList
|
||||||
items={preferredLanguages.map((locale) => ({
|
items={preferredLanguages.map((locale) => ({
|
||||||
|
@ -72,11 +73,11 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
insertLabels={[
|
insertLabels={[
|
||||||
{
|
{
|
||||||
insertAt: 0,
|
insertAt: 0,
|
||||||
name: langui.primary_language ?? "Primary language",
|
name: format("primary_language"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
insertAt: 1,
|
insertAt: 1,
|
||||||
name: langui.secondary_language ?? "Secondary languages",
|
name: format("secondary_language"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onChange={(items) => {
|
onChange={(items) => {
|
||||||
|
@ -94,7 +95,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
cIf(!is1ColumnLayout, "grid-cols-2")
|
cIf(!is1ColumnLayout, "grid-cols-2")
|
||||||
)}>
|
)}>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl">{langui.theme}</h3>
|
<h3 className="text-xl">{format("theme")}</h3>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
buttonsProps={[
|
buttonsProps={[
|
||||||
{
|
{
|
||||||
|
@ -103,7 +104,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
sendAnalytics("Settings", "Change theme (light)");
|
sendAnalytics("Settings", "Change theme (light)");
|
||||||
},
|
},
|
||||||
active: themeMode === ThemeMode.Light,
|
active: themeMode === ThemeMode.Light,
|
||||||
text: langui.light,
|
text: format("light"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
@ -111,7 +112,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
sendAnalytics("Settings", "Change theme (auto)");
|
sendAnalytics("Settings", "Change theme (auto)");
|
||||||
},
|
},
|
||||||
active: themeMode === ThemeMode.Auto,
|
active: themeMode === ThemeMode.Auto,
|
||||||
text: langui.auto,
|
text: format("auto"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
@ -119,14 +120,14 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
sendAnalytics("Settings", "Change theme (dark)");
|
sendAnalytics("Settings", "Change theme (dark)");
|
||||||
},
|
},
|
||||||
active: themeMode === ThemeMode.Dark,
|
active: themeMode === ThemeMode.Dark,
|
||||||
text: langui.dark,
|
text: format("dark"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl">{langui.currency}</h3>
|
<h3 className="text-xl">{format("currency")}</h3>
|
||||||
<div>
|
<div>
|
||||||
<Select
|
<Select
|
||||||
options={currencyOptions}
|
options={currencyOptions}
|
||||||
|
@ -144,7 +145,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl">{langui.font_size}</h3>
|
<h3 className="text-xl">{format("font_size")}</h3>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
buttonsProps={[
|
buttonsProps={[
|
||||||
{
|
{
|
||||||
|
@ -185,7 +186,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl">{langui.font}</h3>
|
<h3 className="text-xl">{format("font")}</h3>
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Button
|
<Button
|
||||||
active={!isDyslexic}
|
active={!isDyslexic}
|
||||||
|
@ -209,7 +210,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xl">{langui.player_name}</h3>
|
<h3 className="text-xl">{format("player_name")}</h3>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="<player>"
|
placeholder="<player>"
|
||||||
className="w-48"
|
className="w-48"
|
||||||
|
|
|
@ -11,11 +11,9 @@ import { ThumbnailHeader } from "./ThumbnailHeader";
|
||||||
import { ToolTip } from "./ToolTip";
|
import { ToolTip } from "./ToolTip";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { PostWithTranslations } from "types/types";
|
import { PostWithTranslations } from "types/types";
|
||||||
import { getStatusDescription } from "helpers/others";
|
|
||||||
import { filterHasAttributes } from "helpers/asserts";
|
import { filterHasAttributes } from "helpers/asserts";
|
||||||
import { prettySlug } from "helpers/formatters";
|
import { prettySlug } from "helpers/formatters";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -50,7 +48,8 @@ export const PostPage = ({
|
||||||
displayTitle = true,
|
displayTitle = true,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format, formatStatusDescription } = useFormat();
|
||||||
|
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: post.translations,
|
items: post.translations,
|
||||||
languageExtractor: useCallback(
|
languageExtractor: useCallback(
|
||||||
|
@ -79,10 +78,10 @@ export const PostPage = ({
|
||||||
|
|
||||||
{selectedTranslation && (
|
{selectedTranslation && (
|
||||||
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
||||||
<p className="font-headers font-bold">{langui.status}:</p>
|
<p className="font-headers font-bold">{format("status")}:</p>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(selectedTranslation.status, langui)}
|
content={formatStatusDescription(selectedTranslation.status)}
|
||||||
maxWidth={"20rem"}>
|
maxWidth={"20rem"}>
|
||||||
<Chip text={selectedTranslation.status} />
|
<Chip text={selectedTranslation.status} />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
|
@ -6,8 +6,7 @@ import { Chip } from "components/Chip";
|
||||||
import { RecorderChipFragment } from "graphql/generated";
|
import { RecorderChipFragment } from "graphql/generated";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
import { filterHasAttributes } from "helpers/asserts";
|
import { filterHasAttributes } from "helpers/asserts";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -22,7 +21,7 @@ interface Props {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const RecorderChip = ({ recorder }: Props): JSX.Element => {
|
export const RecorderChip = ({ recorder }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
@ -40,7 +39,7 @@ export const RecorderChip = ({ recorder }: Props): JSX.Element => {
|
||||||
<h3 className=" text-2xl">{recorder.username}</h3>
|
<h3 className=" text-2xl">{recorder.username}</h3>
|
||||||
{recorder.languages?.data && recorder.languages.data.length > 0 && (
|
{recorder.languages?.data && recorder.languages.data.length > 0 && (
|
||||||
<div className="flex flex-row flex-wrap gap-1">
|
<div className="flex flex-row flex-wrap gap-1">
|
||||||
<p>{langui.languages}:</p>
|
<p>{format("language", { count: recorder.languages.data.length })}:</p>
|
||||||
{filterHasAttributes(recorder.languages.data, ["attributes"] as const).map(
|
{filterHasAttributes(recorder.languages.data, ["attributes"] as const).map(
|
||||||
(language) => (
|
(language) => (
|
||||||
<Fragment key={language.__typename}>
|
<Fragment key={language.__typename}>
|
||||||
|
@ -52,7 +51,7 @@ export const RecorderChip = ({ recorder }: Props): JSX.Element => {
|
||||||
)}
|
)}
|
||||||
{recorder.pronouns && (
|
{recorder.pronouns && (
|
||||||
<div className="flex flex-row flex-wrap gap-1">
|
<div className="flex flex-row flex-wrap gap-1">
|
||||||
<p>{langui.pronouns}:</p>
|
<p>{format("pronouns")}:</p>
|
||||||
<Chip text={recorder.pronouns} />
|
<Chip text={recorder.pronouns} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
interface Group<T> {
|
interface Group<T> {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -71,7 +72,7 @@ export const SmartList = <T,>({
|
||||||
className,
|
className,
|
||||||
}: Props<T>): JSX.Element => {
|
}: Props<T>): JSX.Element => {
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
useScrollTopOnChange(Ids.ContentPanel, [page], paginationScroolTop);
|
useScrollTopOnChange(Ids.ContentPanel, [page], paginationScroolTop);
|
||||||
useEffect(() => setPage(1), [searchingTerm, groupingFunction, groupSortingFunction]);
|
useEffect(() => setPage(1), [searchingTerm, groupingFunction, groupSortingFunction]);
|
||||||
|
|
||||||
|
@ -180,13 +181,7 @@ export const SmartList = <T,>({
|
||||||
className="flex flex-row place-items-center gap-2 pb-2 pt-10 text-2xl
|
className="flex flex-row place-items-center gap-2 pb-2 pt-10 text-2xl
|
||||||
first-of-type:pt-0">
|
first-of-type:pt-0">
|
||||||
{group.name}
|
{group.name}
|
||||||
<Chip
|
<Chip text={format("x_results", { x: group.totalCount })} />
|
||||||
text={`${group.totalCount} ${
|
|
||||||
group.items.length <= 1
|
|
||||||
? langui.result?.toLowerCase() ?? ""
|
|
||||||
: langui.results?.toLowerCase() ?? ""
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</h2>
|
</h2>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
@ -222,15 +217,15 @@ export const SmartList = <T,>({
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const DefaultRenderWhenEmpty = () => {
|
const DefaultRenderWhenEmpty = () => {
|
||||||
|
const { format } = useFormat();
|
||||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
|
||||||
return (
|
return (
|
||||||
<div className="grid h-full place-content-center">
|
<div className="grid h-full place-content-center">
|
||||||
<div
|
<div
|
||||||
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
||||||
border-dark p-8 text-dark opacity-40">
|
border-dark p-8 text-dark opacity-40">
|
||||||
{is3ColumnsLayout && <Ico icon="chevron_left" className="!text-[300%]" />}
|
{is3ColumnsLayout && <Ico icon="chevron_left" className="!text-[300%]" />}
|
||||||
<p className="max-w-xs text-2xl">{langui.no_results_message}</p>
|
<p className="max-w-xs text-2xl">{format("no_results_message")}</p>
|
||||||
{!is3ColumnsLayout && <Ico icon="chevron_right" className="!text-[300%]" />}
|
{!is3ColumnsLayout && <Ico icon="chevron_right" className="!text-[300%]" />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { ImageQuality } from "helpers/img";
|
||||||
import { filterHasAttributes } from "helpers/asserts";
|
import { filterHasAttributes } from "helpers/asserts";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -42,7 +43,7 @@ export const ThumbnailHeader = ({
|
||||||
description,
|
description,
|
||||||
languageSwitcher,
|
languageSwitcher,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -72,7 +73,7 @@ export const ThumbnailHeader = ({
|
||||||
<div className="flew-wrap flex flex-row place-content-center gap-8">
|
<div className="flew-wrap flex flex-row place-content-center gap-8">
|
||||||
{type?.data?.attributes && (
|
{type?.data?.attributes && (
|
||||||
<div className="flex flex-col place-items-center gap-2">
|
<div className="flex flex-col place-items-center gap-2">
|
||||||
<h3 className="text-xl">{langui.type}</h3>
|
<h3 className="text-xl">{format("type", { count: 1 })}</h3>
|
||||||
<div className="flex flex-row flex-wrap">
|
<div className="flex flex-row flex-wrap">
|
||||||
<Chip
|
<Chip
|
||||||
text={
|
text={
|
||||||
|
@ -85,7 +86,7 @@ export const ThumbnailHeader = ({
|
||||||
|
|
||||||
{categories && categories.data.length > 0 && (
|
{categories && categories.data.length > 0 && (
|
||||||
<div className="flex flex-col place-items-center gap-2">
|
<div className="flex flex-col place-items-center gap-2">
|
||||||
<h3 className="text-xl">{langui.categories}</h3>
|
<h3 className="text-xl">{format("category", { count: categories.data.length })}</h3>
|
||||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||||
{filterHasAttributes(categories.data, ["attributes", "id"] as const).map(
|
{filterHasAttributes(categories.data, ["attributes", "id"] as const).map(
|
||||||
(category) => (
|
(category) => (
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { getStatusDescription } from "helpers/others";
|
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { ContentStatus, useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -21,7 +21,7 @@ interface Props {
|
||||||
translations: {
|
translations: {
|
||||||
language: string | undefined;
|
language: string | undefined;
|
||||||
definition: string | null | undefined;
|
definition: string | null | undefined;
|
||||||
status: string | undefined;
|
status: ContentStatus | undefined;
|
||||||
}[];
|
}[];
|
||||||
index: number;
|
index: number;
|
||||||
categories: string[];
|
categories: string[];
|
||||||
|
@ -31,7 +31,7 @@ interface Props {
|
||||||
|
|
||||||
const DefinitionCard = ({ source, translations = [], index, categories }: Props): JSX.Element => {
|
const DefinitionCard = ({ source, translations = [], index, categories }: Props): JSX.Element => {
|
||||||
const isContentPanelAtLeastMd = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastMd);
|
const isContentPanelAtLeastMd = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastMd);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format, formatStatusDescription } = useFormat();
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: translations,
|
items: translations,
|
||||||
languageExtractor: useCallback((item: Props["translations"][number]) => item.language, []),
|
languageExtractor: useCallback((item: Props["translations"][number]) => item.language, []),
|
||||||
|
@ -40,7 +40,7 @@ const DefinitionCard = ({ source, translations = [], index, categories }: Props)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-wrap place-items-center gap-2">
|
<div className="flex flex-wrap place-items-center gap-2">
|
||||||
<p className="font-headers text-lg font-bold">{`${langui.definition} ${index}`}</p>
|
<p className="font-headers text-lg font-bold">{format("definition_x", { x: index })}</p>
|
||||||
|
|
||||||
{translations.length > 1 && (
|
{translations.length > 1 && (
|
||||||
<>
|
<>
|
||||||
|
@ -53,7 +53,7 @@ const DefinitionCard = ({ source, translations = [], index, categories }: Props)
|
||||||
<>
|
<>
|
||||||
<Separator />
|
<Separator />
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(selectedTranslation.status, langui)}
|
content={formatStatusDescription(selectedTranslation.status)}
|
||||||
maxWidth={"20rem"}>
|
maxWidth={"20rem"}>
|
||||||
<Chip text={selectedTranslation.status} />
|
<Chip text={selectedTranslation.status} />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
@ -80,7 +80,7 @@ const DefinitionCard = ({ source, translations = [], index, categories }: Props)
|
||||||
"mt-3 flex place-items-center gap-2",
|
"mt-3 flex place-items-center gap-2",
|
||||||
cIf(!isContentPanelAtLeastMd, "flex-col text-center")
|
cIf(!isContentPanelAtLeastMd, "flex-col text-center")
|
||||||
)}>
|
)}>
|
||||||
<p>{langui.source}: </p>
|
<p>{format("source")}: </p>
|
||||||
<Button href={source.url} size="small" text={source.name} />
|
<Button href={source.url} size="small" text={source.name} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -21,11 +21,13 @@ import { LocalDataFile } from "graphql/fetchLocalData";
|
||||||
const languages = atomPairing(atom<Languages>([]));
|
const languages = atomPairing(atom<Languages>([]));
|
||||||
const currencies = atomPairing(atom<Currencies>([]));
|
const currencies = atomPairing(atom<Currencies>([]));
|
||||||
const langui = atomPairing(atom<Langui>({}));
|
const langui = atomPairing(atom<Langui>({}));
|
||||||
|
const fallbackLangui = atomPairing(atom<Langui>({}));
|
||||||
|
|
||||||
export const localData = {
|
export const localData = {
|
||||||
languages: languages[0],
|
languages: languages[0],
|
||||||
currencies: currencies[0],
|
currencies: currencies[0],
|
||||||
langui: langui[0],
|
langui: langui[0],
|
||||||
|
fallbackLangui: fallbackLangui[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`;
|
const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`;
|
||||||
|
@ -34,6 +36,7 @@ export const useLocalData = (): void => {
|
||||||
const setLanguages = useAtomSetter(languages);
|
const setLanguages = useAtomSetter(languages);
|
||||||
const setCurrencies = useAtomSetter(currencies);
|
const setCurrencies = useAtomSetter(currencies);
|
||||||
const setLangui = useAtomSetter(langui);
|
const setLangui = useAtomSetter(langui);
|
||||||
|
const setFallbackLangui = useAtomSetter(fallbackLangui);
|
||||||
|
|
||||||
const { locale } = useRouter();
|
const { locale } = useRouter();
|
||||||
const { data: rawLanguages } = useFetch<LocalDataGetLanguagesQuery>(getFileName("languages"));
|
const { data: rawLanguages } = useFetch<LocalDataGetLanguagesQuery>(getFileName("languages"));
|
||||||
|
@ -56,4 +59,9 @@ export const useLocalData = (): void => {
|
||||||
console.log("[useLocalData] Refresh langui");
|
console.log("[useLocalData] Refresh langui");
|
||||||
setLangui(processLangui(rawLangui, locale));
|
setLangui(processLangui(rawLangui, locale));
|
||||||
}, [locale, rawLangui, setLangui]);
|
}, [locale, rawLangui, setLangui]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("[useLocalData] Refresh fallback langui");
|
||||||
|
setFallbackLangui(processLangui(rawLangui, "en"));
|
||||||
|
}, [rawLangui, setFallbackLangui]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,7 +41,7 @@ export const getPostStaticProps =
|
||||||
[langui.release_date ?? "Release date"]: [
|
[langui.release_date ?? "Release date"]: [
|
||||||
prettyDate(post.posts.data[0].attributes.date, context.locale),
|
prettyDate(post.posts.data[0].attributes.date, context.locale),
|
||||||
],
|
],
|
||||||
[langui.categories ?? "Categories"]: filterHasAttributes(
|
[langui.category ?? "Categories"]: filterHasAttributes(
|
||||||
post.posts.data[0].attributes.categories?.data,
|
post.posts.data[0].attributes.categories?.data,
|
||||||
["attributes"] as const
|
["attributes"] as const
|
||||||
).map((category) => category.attributes.short),
|
).map((category) => category.attributes.short),
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
export interface ICUParams {
|
||||||
|
library: never;
|
||||||
|
contents: never;
|
||||||
|
wiki: never;
|
||||||
|
chronicles: never;
|
||||||
|
library_short_description: never;
|
||||||
|
contents_short_description: never;
|
||||||
|
wiki_short_description: never;
|
||||||
|
chronicles_short_description: never;
|
||||||
|
news: never;
|
||||||
|
gallery: never;
|
||||||
|
archives: never;
|
||||||
|
about_us: never;
|
||||||
|
licensing_notice: never;
|
||||||
|
copyright_notice: never;
|
||||||
|
contents_description: never;
|
||||||
|
type: { count: number };
|
||||||
|
category: { count: number };
|
||||||
|
size: never;
|
||||||
|
release_date: never;
|
||||||
|
release_year: never;
|
||||||
|
details: never;
|
||||||
|
price: never;
|
||||||
|
width: never;
|
||||||
|
height: never;
|
||||||
|
thickness: never;
|
||||||
|
subitem: { count: number };
|
||||||
|
variant: { count: number };
|
||||||
|
summary: never;
|
||||||
|
audio: never;
|
||||||
|
video: never;
|
||||||
|
textual: never;
|
||||||
|
game: never;
|
||||||
|
other: never;
|
||||||
|
left_to_right: never;
|
||||||
|
right_to_left: never;
|
||||||
|
page: { count: number };
|
||||||
|
page_order: never;
|
||||||
|
binding: never;
|
||||||
|
type_information: never;
|
||||||
|
front_matter: never;
|
||||||
|
back_matter: never;
|
||||||
|
open_content: never;
|
||||||
|
read_content: never;
|
||||||
|
watch_content: never;
|
||||||
|
listen_content: never;
|
||||||
|
view_scans: never;
|
||||||
|
paperback: never;
|
||||||
|
hardcover: never;
|
||||||
|
select_language: never;
|
||||||
|
language: { count: number };
|
||||||
|
library_description: never;
|
||||||
|
wiki_description: never;
|
||||||
|
chronicles_description: never;
|
||||||
|
news_description: never;
|
||||||
|
archives_description: never;
|
||||||
|
about_us_description: never;
|
||||||
|
page_not_found: never;
|
||||||
|
default_description: never;
|
||||||
|
name: never;
|
||||||
|
show_subitems: never;
|
||||||
|
show_primary_items: never;
|
||||||
|
show_secondary_items: never;
|
||||||
|
order_by: never;
|
||||||
|
group_by: never;
|
||||||
|
select_option_sidebar: never;
|
||||||
|
group: never;
|
||||||
|
settings: never;
|
||||||
|
theme: never;
|
||||||
|
light: never;
|
||||||
|
auto: never;
|
||||||
|
dark: never;
|
||||||
|
font_size: never;
|
||||||
|
player_name: never;
|
||||||
|
currency: never;
|
||||||
|
font: never;
|
||||||
|
calculated: never;
|
||||||
|
status_incomplete: never;
|
||||||
|
status_draft: never;
|
||||||
|
status_review: never;
|
||||||
|
status_done: never;
|
||||||
|
incomplete: never;
|
||||||
|
draft: never;
|
||||||
|
review: never;
|
||||||
|
done: never;
|
||||||
|
status: never;
|
||||||
|
transcribers: never;
|
||||||
|
translators: never;
|
||||||
|
proofreaders: never;
|
||||||
|
transcript_notice: never;
|
||||||
|
translation_notice: never;
|
||||||
|
source_language: never;
|
||||||
|
pronouns: never;
|
||||||
|
item: { count: number };
|
||||||
|
content: never;
|
||||||
|
open_settings: never;
|
||||||
|
change_language: never;
|
||||||
|
open_search: never;
|
||||||
|
chronology: never;
|
||||||
|
accords_handbook: never;
|
||||||
|
legality: never;
|
||||||
|
sharing_policy: never;
|
||||||
|
contact_us: never;
|
||||||
|
email: never;
|
||||||
|
email_gdpr_notice: never;
|
||||||
|
message: never;
|
||||||
|
send: never;
|
||||||
|
response_invalid_code: never;
|
||||||
|
response_invalid_email: never;
|
||||||
|
response_email_success: never;
|
||||||
|
always_show_info: never;
|
||||||
|
item_not_available: never;
|
||||||
|
primary_language: never;
|
||||||
|
secondary_language: never;
|
||||||
|
combine_related_contents: never;
|
||||||
|
previous_content: never;
|
||||||
|
followup_content: never;
|
||||||
|
videos: never;
|
||||||
|
view_on: never;
|
||||||
|
channel: never;
|
||||||
|
subscribers: never;
|
||||||
|
description: never;
|
||||||
|
available_at: never;
|
||||||
|
search_title: never;
|
||||||
|
want_it: never;
|
||||||
|
have_it: never;
|
||||||
|
source: never;
|
||||||
|
reset_all_filters: never;
|
||||||
|
only_display_items_i_have: never;
|
||||||
|
only_display_items_i_want: never;
|
||||||
|
only_display_unmarked_items: never;
|
||||||
|
display_all_items: never;
|
||||||
|
table_of_contents: never;
|
||||||
|
no_results_message: never;
|
||||||
|
all: never;
|
||||||
|
special_pages: never;
|
||||||
|
scan: never;
|
||||||
|
scanlation: never;
|
||||||
|
scanners: never;
|
||||||
|
cleaners: never;
|
||||||
|
typesetters: never;
|
||||||
|
notes: never;
|
||||||
|
cover: never;
|
||||||
|
tags: never;
|
||||||
|
no_source_warning: never;
|
||||||
|
copy_anchor_link: never;
|
||||||
|
anchor_link_copied: never;
|
||||||
|
folders: never;
|
||||||
|
empty_folder_message: never;
|
||||||
|
switch_to_grid_view: never;
|
||||||
|
switch_to_folder_view: never;
|
||||||
|
content_is_not_available: never;
|
||||||
|
paper_texture: never;
|
||||||
|
book_fold: never;
|
||||||
|
lighting: never;
|
||||||
|
side_pages: never;
|
||||||
|
shadow: never;
|
||||||
|
night_reader: never;
|
||||||
|
single_page_view: never;
|
||||||
|
double_page_view: never;
|
||||||
|
reset_all_options: never;
|
||||||
|
reading_layout: never;
|
||||||
|
quality: never;
|
||||||
|
only_unavailable_videos: never;
|
||||||
|
oldest: never;
|
||||||
|
newest: never;
|
||||||
|
least_popular: never;
|
||||||
|
most_popular: never;
|
||||||
|
shortest: never;
|
||||||
|
longest: never;
|
||||||
|
search: never;
|
||||||
|
showing_x_out_of_y_results: {
|
||||||
|
x: Date | boolean | number | string;
|
||||||
|
y: Date | boolean | number | string;
|
||||||
|
};
|
||||||
|
return_to_x: { x: undefined | null | Date | boolean | number | string };
|
||||||
|
x_results: { x: number };
|
||||||
|
definition_x: { x: Date | boolean | number | string };
|
||||||
|
subitem_of_x: { x: Date | boolean | number | string };
|
||||||
|
variant_of_x: { x: Date | boolean | number | string };
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* eslint-disable import/no-nodejs-modules */
|
||||||
|
import { createWriteStream } from "fs";
|
||||||
|
import { parse, TYPE } from "@formatjs/icu-messageformat-parser";
|
||||||
|
import { getLangui } from "./fetchLocalData";
|
||||||
|
import { filterDefined } from "helpers/asserts";
|
||||||
|
|
||||||
|
const OUTPUT_FOLDER = `${process.cwd()}/src/graphql`;
|
||||||
|
|
||||||
|
const icuToTypescript = () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { ui_language, ...langui } = getLangui("en");
|
||||||
|
|
||||||
|
const output = createWriteStream(`${OUTPUT_FOLDER}/icuParams.ts`);
|
||||||
|
|
||||||
|
output.write("export interface ICUParams {\n");
|
||||||
|
|
||||||
|
Object.keys(langui).map((oKey) => {
|
||||||
|
const key = oKey as keyof typeof langui;
|
||||||
|
const parsedMessage = parse(langui[key] ?? "");
|
||||||
|
|
||||||
|
const variables = filterDefined(
|
||||||
|
parsedMessage.map((elem) => {
|
||||||
|
if (elem.type === TYPE.argument) {
|
||||||
|
return `${elem.value}: Date | boolean | number | string`;
|
||||||
|
} else if (elem.type === TYPE.plural) {
|
||||||
|
return `${elem.value}: number`;
|
||||||
|
} else if (elem.type === TYPE.select) {
|
||||||
|
const options = Object.keys(elem.options);
|
||||||
|
const stringOptions = options.filter(
|
||||||
|
(option) => option !== "undefined" && option !== "other"
|
||||||
|
);
|
||||||
|
const type: string[] = stringOptions.map((option) => `"${option}"`);
|
||||||
|
if (options.includes("undefined")) type.push(...["undefined", "null"]);
|
||||||
|
if (options.includes("other")) type.push(...["Date", "boolean", "number", "string"]);
|
||||||
|
return `${elem.value}: ${type.join(` | `)}`;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const variablesType = variables.length > 0 ? `{ ${variables.join(";")} }` : "never";
|
||||||
|
|
||||||
|
output.write(` ${key}: ${variablesType};\n`);
|
||||||
|
});
|
||||||
|
|
||||||
|
output.write("}\n");
|
||||||
|
|
||||||
|
console.log(`${OUTPUT_FOLDER}/icu-params.ts has been written!`);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.argv[2] === "--icu") {
|
||||||
|
icuToTypescript();
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ query localDataGetWebsiteInterfaces {
|
||||||
wiki_short_description
|
wiki_short_description
|
||||||
chronicles_short_description
|
chronicles_short_description
|
||||||
news
|
news
|
||||||
merch
|
|
||||||
gallery
|
gallery
|
||||||
archives
|
archives
|
||||||
about_us
|
about_us
|
||||||
|
@ -27,7 +26,6 @@ query localDataGetWebsiteInterfaces {
|
||||||
contents_description
|
contents_description
|
||||||
type
|
type
|
||||||
category
|
category
|
||||||
categories
|
|
||||||
size
|
size
|
||||||
release_date
|
release_date
|
||||||
release_year
|
release_year
|
||||||
|
@ -37,22 +35,16 @@ query localDataGetWebsiteInterfaces {
|
||||||
height
|
height
|
||||||
thickness
|
thickness
|
||||||
subitem
|
subitem
|
||||||
subitems
|
|
||||||
subitem_of
|
|
||||||
variant
|
variant
|
||||||
variants
|
|
||||||
variant_of
|
|
||||||
summary
|
summary
|
||||||
audio
|
audio
|
||||||
video
|
video
|
||||||
textual
|
textual
|
||||||
game
|
game
|
||||||
other
|
other
|
||||||
return_to
|
|
||||||
left_to_right
|
left_to_right
|
||||||
right_to_left
|
right_to_left
|
||||||
page
|
page
|
||||||
pages
|
|
||||||
page_order
|
page_order
|
||||||
binding
|
binding
|
||||||
type_information
|
type_information
|
||||||
|
@ -65,15 +57,12 @@ query localDataGetWebsiteInterfaces {
|
||||||
view_scans
|
view_scans
|
||||||
paperback
|
paperback
|
||||||
hardcover
|
hardcover
|
||||||
languages
|
|
||||||
select_language
|
select_language
|
||||||
language
|
language
|
||||||
library_description
|
library_description
|
||||||
wiki_description
|
wiki_description
|
||||||
chronicles_description
|
chronicles_description
|
||||||
news_description
|
news_description
|
||||||
merch_description
|
|
||||||
gallery_description
|
|
||||||
archives_description
|
archives_description
|
||||||
about_us_description
|
about_us_description
|
||||||
page_not_found
|
page_not_found
|
||||||
|
@ -82,8 +71,6 @@ query localDataGetWebsiteInterfaces {
|
||||||
show_subitems
|
show_subitems
|
||||||
show_primary_items
|
show_primary_items
|
||||||
show_secondary_items
|
show_secondary_items
|
||||||
no_type
|
|
||||||
no_year
|
|
||||||
order_by
|
order_by
|
||||||
group_by
|
group_by
|
||||||
select_option_sidebar
|
select_option_sidebar
|
||||||
|
@ -114,20 +101,14 @@ query localDataGetWebsiteInterfaces {
|
||||||
translation_notice
|
translation_notice
|
||||||
source_language
|
source_language
|
||||||
pronouns
|
pronouns
|
||||||
no_category
|
|
||||||
item
|
item
|
||||||
items
|
|
||||||
content
|
content
|
||||||
result
|
|
||||||
results
|
|
||||||
language_switch_message
|
|
||||||
open_settings
|
open_settings
|
||||||
change_language
|
change_language
|
||||||
open_search
|
open_search
|
||||||
chronology
|
chronology
|
||||||
accords_handbook
|
accords_handbook
|
||||||
legality
|
legality
|
||||||
members
|
|
||||||
sharing_policy
|
sharing_policy
|
||||||
contact_us
|
contact_us
|
||||||
email
|
email
|
||||||
|
@ -160,7 +141,6 @@ query localDataGetWebsiteInterfaces {
|
||||||
only_display_unmarked_items
|
only_display_unmarked_items
|
||||||
display_all_items
|
display_all_items
|
||||||
table_of_contents
|
table_of_contents
|
||||||
definition
|
|
||||||
no_results_message
|
no_results_message
|
||||||
all
|
all
|
||||||
special_pages
|
special_pages
|
||||||
|
@ -199,6 +179,12 @@ query localDataGetWebsiteInterfaces {
|
||||||
shortest
|
shortest
|
||||||
longest
|
longest
|
||||||
search
|
search
|
||||||
|
showing_x_out_of_y_results
|
||||||
|
return_to_x
|
||||||
|
x_results
|
||||||
|
definition_x
|
||||||
|
subitem_of_x
|
||||||
|
variant_of_x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { convertPrice } from "./numbers";
|
import { convertPrice } from "./numbers";
|
||||||
import { isDefinedAndNotEmpty, isUndefined } from "./asserts";
|
import { isDefinedAndNotEmpty, isUndefined } from "./asserts";
|
||||||
import { datePickerToDate } from "./date";
|
import { datePickerToDate } from "./date";
|
||||||
import { Currencies, Languages, Langui } from "./localData";
|
import { Currencies, Languages } from "./localData";
|
||||||
import { DatePickerFragment, PricePickerFragment } from "graphql/generated";
|
import { DatePickerFragment, PricePickerFragment } from "graphql/generated";
|
||||||
|
|
||||||
export const prettyDate = (
|
export const prettyDate = (
|
||||||
|
@ -58,25 +58,6 @@ export const prettyInlineTitle = (
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prettyItemType = (metadata: { __typename: string }, langui: Langui): string => {
|
|
||||||
switch (metadata.__typename) {
|
|
||||||
case "ComponentMetadataAudio":
|
|
||||||
return langui.audio ?? "Audio";
|
|
||||||
case "ComponentMetadataBooks":
|
|
||||||
return langui.textual ?? "Textual";
|
|
||||||
case "ComponentMetadataGame":
|
|
||||||
return langui.game ?? "Game";
|
|
||||||
case "ComponentMetadataVideo":
|
|
||||||
return langui.video ?? "Video";
|
|
||||||
case "ComponentMetadataGroup":
|
|
||||||
return langui.group ?? "Group";
|
|
||||||
case "ComponentMetadataOther":
|
|
||||||
return langui.other ?? "Other";
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* eslint-disable id-denylist */
|
/* eslint-disable id-denylist */
|
||||||
export const prettyItemSubType = (
|
export const prettyItemSubType = (
|
||||||
metadata:
|
metadata:
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import { Langui } from "./localData";
|
|
||||||
import { isDefined } from "./asserts";
|
import { isDefined } from "./asserts";
|
||||||
import {
|
import { GetLibraryItemQuery, GetLibraryItemScansQuery } from "graphql/generated";
|
||||||
Enum_Componentsetstextset_Status,
|
|
||||||
GetLibraryItemQuery,
|
|
||||||
GetLibraryItemScansQuery,
|
|
||||||
} from "graphql/generated";
|
|
||||||
|
|
||||||
type SortRangedContentProps =
|
type SortRangedContentProps =
|
||||||
| NonNullable<
|
| NonNullable<
|
||||||
|
@ -26,25 +21,6 @@ export const sortRangedContent = (contents: SortRangedContentProps): void => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStatusDescription = (status: string, langui: Langui): string | null | undefined => {
|
|
||||||
switch (status) {
|
|
||||||
case Enum_Componentsetstextset_Status.Incomplete:
|
|
||||||
return langui.status_incomplete;
|
|
||||||
|
|
||||||
case Enum_Componentsetstextset_Status.Draft:
|
|
||||||
return langui.status_draft;
|
|
||||||
|
|
||||||
case Enum_Componentsetstextset_Status.Review:
|
|
||||||
return langui.status_review;
|
|
||||||
|
|
||||||
case Enum_Componentsetstextset_Status.Done:
|
|
||||||
return langui.status_done;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const iterateMap = <K, V, U>(
|
export const iterateMap = <K, V, U>(
|
||||||
map: Map<K, V>,
|
map: Map<K, V>,
|
||||||
callbackfn: (key: K, value: V, index: number) => U,
|
callbackfn: (key: K, value: V, index: number) => U,
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { IntlMessageFormat } from "intl-messageformat";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { atoms } from "contexts/atoms";
|
||||||
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { LibraryItemMetadataDynamicZone } from "graphql/generated";
|
||||||
|
import { ICUParams } from "graphql/icuParams";
|
||||||
|
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||||
|
|
||||||
|
type WordingKey = keyof ICUParams;
|
||||||
|
|
||||||
|
type LibraryItemType = Exclude<LibraryItemMetadataDynamicZone["__typename"], undefined>;
|
||||||
|
|
||||||
|
export type ContentStatus = "Done" | "Draft" | "Incomplete" | "Review";
|
||||||
|
|
||||||
|
const componentMetadataToWording: Record<LibraryItemType, WordingKey> = {
|
||||||
|
ComponentMetadataAudio: "audio",
|
||||||
|
ComponentMetadataBooks: "textual",
|
||||||
|
ComponentMetadataGame: "game",
|
||||||
|
ComponentMetadataGroup: "group",
|
||||||
|
ComponentMetadataVideo: "video",
|
||||||
|
ComponentMetadataOther: "other",
|
||||||
|
Error: "item",
|
||||||
|
};
|
||||||
|
|
||||||
|
const componentSetsTextsetStatusToWording: Record<
|
||||||
|
ContentStatus,
|
||||||
|
{ label: WordingKey; description: WordingKey }
|
||||||
|
> = {
|
||||||
|
Draft: { label: "draft", description: "status_draft" },
|
||||||
|
Incomplete: { label: "incomplete", description: "status_incomplete" },
|
||||||
|
Review: { label: "review", description: "status_review" },
|
||||||
|
Done: { label: "done", description: "status_done" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFormat = (): {
|
||||||
|
format: <K extends WordingKey>(
|
||||||
|
key: K,
|
||||||
|
...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]]
|
||||||
|
) => string;
|
||||||
|
formatLibraryItemType: (metadata: { __typename: LibraryItemType }) => string;
|
||||||
|
formatStatusLabel: (status: ContentStatus) => string;
|
||||||
|
formatStatusDescription: (status: ContentStatus) => string;
|
||||||
|
} => {
|
||||||
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
|
const fallbackLangui = useAtomGetter(atoms.localData.fallbackLangui);
|
||||||
|
|
||||||
|
const format = useCallback(
|
||||||
|
(
|
||||||
|
key: WordingKey,
|
||||||
|
values?: Record<string, Date | boolean | number | string | null | undefined>
|
||||||
|
): string => {
|
||||||
|
const processedValues = Object.fromEntries(
|
||||||
|
Object.entries(values ?? {}).map(([oKey, value]) => [
|
||||||
|
oKey,
|
||||||
|
isDefined(value) ? value : "undefined",
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const result = new IntlMessageFormat(langui[key] ?? "").format(processedValues).toString();
|
||||||
|
if (isDefinedAndNotEmpty(result)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return new IntlMessageFormat(fallbackLangui[key] ?? "").format(processedValues).toString();
|
||||||
|
},
|
||||||
|
[langui, fallbackLangui]
|
||||||
|
);
|
||||||
|
|
||||||
|
const formatLibraryItemType = useCallback(
|
||||||
|
(metadata: { __typename: LibraryItemType }): string =>
|
||||||
|
format(componentMetadataToWording[metadata.__typename]),
|
||||||
|
[format]
|
||||||
|
);
|
||||||
|
|
||||||
|
const formatStatusLabel = useCallback(
|
||||||
|
(status: ContentStatus): string => format(componentSetsTextsetStatusToWording[status].label),
|
||||||
|
[format]
|
||||||
|
);
|
||||||
|
|
||||||
|
const formatStatusDescription = useCallback(
|
||||||
|
(status: ContentStatus): string =>
|
||||||
|
format(componentSetsTextsetStatusToWording[status].description),
|
||||||
|
[format]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
format,
|
||||||
|
formatLibraryItemType,
|
||||||
|
formatStatusLabel,
|
||||||
|
formatStatusDescription,
|
||||||
|
};
|
||||||
|
};
|
|
@ -5,8 +5,7 @@ import { ContentPanel } from "components/Containers/ContentPanel";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Img } from "components/Img";
|
import { Img } from "components/Img";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -16,7 +15,7 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
interface Props extends AppLayoutRequired {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const FourOhFour = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
const FourOhFour = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
contentPanel={
|
contentPanel={
|
||||||
|
@ -26,7 +25,7 @@ const FourOhFour = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||||
className="animate-zoom-in drop-shadow-lg shadow-shade"
|
className="animate-zoom-in drop-shadow-lg shadow-shade"
|
||||||
/>
|
/>
|
||||||
<div className="mt-8 grid place-items-center gap-6">
|
<div className="mt-8 grid place-items-center gap-6">
|
||||||
<h2>{langui.page_not_found}</h2>
|
<h2>{format("page_not_found")}</h2>
|
||||||
<ReturnButton href="/" title="Home" />
|
<ReturnButton href="/" title="Home" />
|
||||||
</div>
|
</div>
|
||||||
</ContentPanel>
|
</ContentPanel>
|
||||||
|
|
|
@ -5,8 +5,7 @@ import { ContentPanel } from "components/Containers/ContentPanel";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Img } from "components/Img";
|
import { Img } from "components/Img";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -16,7 +15,7 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
interface Props extends AppLayoutRequired {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const FiveHundred = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
const FiveHundred = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
contentPanel={
|
contentPanel={
|
||||||
|
@ -26,7 +25,7 @@ const FiveHundred = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||||
className="animate-zoom-in drop-shadow-lg shadow-shade"
|
className="animate-zoom-in drop-shadow-lg shadow-shade"
|
||||||
/>
|
/>
|
||||||
<div className="mt-8 grid place-items-center gap-6">
|
<div className="mt-8 grid place-items-center gap-6">
|
||||||
<h2>{langui.page_not_found}</h2>
|
<h2>{format("page_not_found")}</h2>
|
||||||
<ReturnButton href="/" title="Home" />
|
<ReturnButton href="/" title="Home" />
|
||||||
</div>
|
</div>
|
||||||
</ContentPanel>
|
</ContentPanel>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -9,12 +8,12 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AccordsHandbook = (props: PostStaticProps): JSX.Element => {
|
const AccordsHandbook = (props: PostStaticProps): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<PostPage
|
<PostPage
|
||||||
{...props}
|
{...props}
|
||||||
returnHref="/about-us/"
|
returnHref="/about-us/"
|
||||||
returnTitle={langui.about_us}
|
returnTitle={format("about_us")}
|
||||||
displayToc
|
displayToc
|
||||||
displayLanguageSwitcher
|
displayLanguageSwitcher
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -17,7 +18,7 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
const AboutUs = (props: PostStaticProps): JSX.Element => {
|
const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
const [formResponse, setFormResponse] = useState("");
|
const [formResponse, setFormResponse] = useState("");
|
||||||
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">("stale");
|
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">("stale");
|
||||||
|
@ -65,13 +66,13 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
.then((response: ResponseMailProps) => {
|
.then((response: ResponseMailProps) => {
|
||||||
switch (response.code) {
|
switch (response.code) {
|
||||||
case "OKAY":
|
case "OKAY":
|
||||||
setFormResponse(langui.response_email_success ?? "");
|
setFormResponse(format("response_email_success"));
|
||||||
setFormState("completed");
|
setFormState("completed");
|
||||||
sendAnalytics("Contact", "Send email (success)");
|
sendAnalytics("Contact", "Send email (success)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "EENVELOPE":
|
case "EENVELOPE":
|
||||||
setFormResponse(langui.response_invalid_email ?? "");
|
setFormResponse(format("response_invalid_email"));
|
||||||
setFormState("stale");
|
setFormState("stale");
|
||||||
sendAnalytics("Contact", "Send email (invalid email)");
|
sendAnalytics("Contact", "Send email (invalid email)");
|
||||||
break;
|
break;
|
||||||
|
@ -84,7 +85,7 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setFormResponse(langui.response_invalid_code ?? "");
|
setFormResponse(format("response_invalid_code"));
|
||||||
setFormState("stale");
|
setFormState("stale");
|
||||||
setRandomNumber1(randomInt(0, 10));
|
setRandomNumber1(randomInt(0, 10));
|
||||||
setRandomNumber2(randomInt(0, 10));
|
setRandomNumber2(randomInt(0, 10));
|
||||||
|
@ -94,7 +95,7 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
fields.verif.value = "";
|
fields.verif.value = "";
|
||||||
}}>
|
}}>
|
||||||
<div className="flex flex-col place-items-center gap-1">
|
<div className="flex flex-col place-items-center gap-1">
|
||||||
<label htmlFor="name">{langui.name}:</label>
|
<label htmlFor="name">{format("name")}:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className={cIf(is1ColumnLayout, "w-full")}
|
className={cIf(is1ColumnLayout, "w-full")}
|
||||||
|
@ -106,7 +107,7 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col place-items-center gap-1">
|
<div className="flex flex-col place-items-center gap-1">
|
||||||
<label htmlFor="email">{langui.email}:</label>
|
<label htmlFor="email">{format("email")}:</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
className={cIf(is1ColumnLayout, "w-full")}
|
className={cIf(is1ColumnLayout, "w-full")}
|
||||||
|
@ -115,11 +116,11 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
required
|
required
|
||||||
disabled={formState !== "stale"}
|
disabled={formState !== "stale"}
|
||||||
/>
|
/>
|
||||||
<p className="text-sm italic text-dark opacity-70">{langui.email_gdpr_notice}</p>
|
<p className="text-sm italic text-dark opacity-70">{format("email_gdpr_notice")}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex w-full flex-col place-items-center gap-1">
|
<div className="flex w-full flex-col place-items-center gap-1">
|
||||||
<label htmlFor="message">{langui.message}:</label>
|
<label htmlFor="message">{format("message")}:</label>
|
||||||
<textarea
|
<textarea
|
||||||
name="message"
|
name="message"
|
||||||
id="message"
|
id="message"
|
||||||
|
@ -147,7 +148,7 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
value={langui.send ?? "Send"}
|
value={format("send")}
|
||||||
className="w-min !px-6"
|
className="w-min !px-6"
|
||||||
disabled={formState !== "stale"}
|
disabled={formState !== "stale"}
|
||||||
/>
|
/>
|
||||||
|
@ -168,7 +169,7 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
<PostPage
|
<PostPage
|
||||||
{...props}
|
{...props}
|
||||||
returnHref="/about-us/"
|
returnHref="/about-us/"
|
||||||
returnTitle={langui.about_us}
|
returnTitle={format("about_us")}
|
||||||
displayToc
|
displayToc
|
||||||
appendBody={contactForm}
|
appendBody={contactForm}
|
||||||
displayLanguageSwitcher
|
displayLanguageSwitcher
|
||||||
|
|
|
@ -6,8 +6,7 @@ import { SubPanel } from "components/Containers/SubPanel";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -17,23 +16,23 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
interface Props extends AppLayoutRequired {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const AboutUs = (props: Props): JSX.Element => {
|
const AboutUs = (props: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
subPanel={
|
subPanel={
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
icon="info"
|
icon="info"
|
||||||
title={langui.about_us}
|
title={format("about_us")}
|
||||||
description={langui.about_us_description}
|
description={format("about_us_description")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<NavOption title={langui.accords_handbook} url="/about-us/accords-handbook" border />
|
<NavOption title={format("accords_handbook")} url="/about-us/accords-handbook" border />
|
||||||
<NavOption title={langui.legality} url="/about-us/legality" border />
|
<NavOption title={format("legality")} url="/about-us/legality" border />
|
||||||
<NavOption title={langui.sharing_policy} url="/about-us/sharing-policy" border />
|
<NavOption title={format("sharing_policy")} url="/about-us/sharing-policy" border />
|
||||||
<NavOption title={langui.contact_us} url="/about-us/contact" border />
|
<NavOption title={format("contact_us")} url="/about-us/contact" border />
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
}
|
}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -9,12 +8,12 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Legality = (props: PostStaticProps): JSX.Element => {
|
const Legality = (props: PostStaticProps): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<PostPage
|
<PostPage
|
||||||
{...props}
|
{...props}
|
||||||
returnHref="/about-us/"
|
returnHref="/about-us/"
|
||||||
returnTitle={langui.about_us}
|
returnTitle={format("about_us")}
|
||||||
displayToc
|
displayToc
|
||||||
displayLanguageSwitcher
|
displayLanguageSwitcher
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -9,12 +8,12 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SharingPolicy = (props: PostStaticProps): JSX.Element => {
|
const SharingPolicy = (props: PostStaticProps): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<PostPage
|
<PostPage
|
||||||
{...props}
|
{...props}
|
||||||
returnHref="/about-us/"
|
returnHref="/about-us/"
|
||||||
returnTitle={langui.about_us}
|
returnTitle={format("about_us")}
|
||||||
displayToc
|
displayToc
|
||||||
displayLanguageSwitcher
|
displayLanguageSwitcher
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -6,8 +6,7 @@ import { SubPanel } from "components/Containers/SubPanel";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -17,10 +16,14 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
interface Props extends AppLayoutRequired {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const Archives = (props: Props): JSX.Element => {
|
const Archives = (props: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader icon="save" title={langui.archives} description={langui.archives_description} />
|
<PanelHeader
|
||||||
|
icon="save"
|
||||||
|
title={format("archives")}
|
||||||
|
description={format("archives_description")}
|
||||||
|
/>
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
<NavOption title={"Videos"} url="/archives/videos/" border />
|
<NavOption title={"Videos"} url="/archives/videos/" border />
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
|
|
|
@ -14,8 +14,6 @@ import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
import { CustomSearchResponse, meiliSearch } from "helpers/search";
|
import { CustomSearchResponse, meiliSearch } from "helpers/search";
|
||||||
import { MeiliIndices, MeiliVideo } from "shared/meilisearch-graphql-typings/meiliTypes";
|
import { MeiliIndices, MeiliVideo } from "shared/meilisearch-graphql-typings/meiliTypes";
|
||||||
import { PreviewCard } from "components/PreviewCard";
|
import { PreviewCard } from "components/PreviewCard";
|
||||||
|
@ -28,6 +26,7 @@ import { Button } from "components/Inputs/Button";
|
||||||
import { GetVideoChannelQuery } from "graphql/generated";
|
import { GetVideoChannelQuery } from "graphql/generated";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { Paginator } from "components/Containers/Paginator";
|
import { Paginator } from "components/Containers/Paginator";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -61,27 +60,20 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const router = useTypedRouter(queryParamSchema);
|
const router = useTypedRouter(queryParamSchema);
|
||||||
|
|
||||||
const sortingMethods = useMemo(
|
const sortingMethods = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ meiliAttribute: "sortable_published_date:asc", displayedName: langui.oldest },
|
{ meiliAttribute: "sortable_published_date:asc", displayedName: format("oldest") },
|
||||||
{ meiliAttribute: "sortable_published_date:desc", displayedName: langui.newest },
|
{ meiliAttribute: "sortable_published_date:desc", displayedName: format("newest") },
|
||||||
{ meiliAttribute: "views:asc", displayedName: langui.least_popular },
|
{ meiliAttribute: "views:asc", displayedName: format("least_popular") },
|
||||||
{ meiliAttribute: "views:desc", displayedName: langui.most_popular },
|
{ meiliAttribute: "views:desc", displayedName: format("most_popular") },
|
||||||
{ meiliAttribute: "duration:asc", displayedName: langui.shortest },
|
{ meiliAttribute: "duration:asc", displayedName: format("shortest") },
|
||||||
{ meiliAttribute: "duration:desc", displayedName: langui.longest },
|
{ meiliAttribute: "duration:desc", displayedName: format("longest") },
|
||||||
],
|
],
|
||||||
[
|
[format]
|
||||||
langui.least_popular,
|
|
||||||
langui.longest,
|
|
||||||
langui.most_popular,
|
|
||||||
langui.newest,
|
|
||||||
langui.oldest,
|
|
||||||
langui.shortest,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -159,7 +151,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
href="/archives/videos"
|
href="/archives/videos"
|
||||||
title={langui.videos}
|
title={format("videos")}
|
||||||
displayOnlyOn={"3ColumnsLayout"}
|
displayOnlyOn={"3ColumnsLayout"}
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
@ -167,14 +159,16 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
icon="movie"
|
icon="movie"
|
||||||
title={channel.title}
|
title={channel.title}
|
||||||
description={`${channel.subscribers.toLocaleString()} ${langui.subscribers?.toLowerCase()}`}
|
description={`${channel.subscribers.toLocaleString()} ${format(
|
||||||
|
"subscribers"
|
||||||
|
).toLowerCase()}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
className="mb-6 w-full"
|
className="mb-6 w-full"
|
||||||
placeholder={langui.search_title}
|
placeholder={format("search_title")}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(newQuery) => {
|
onChange={(newQuery) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -187,10 +181,10 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WithLabel label={langui.order_by}>
|
<WithLabel label={format("order_by")}>
|
||||||
<Select
|
<Select
|
||||||
className="w-full"
|
className="w-full"
|
||||||
options={sortingMethods.map((item) => item.displayedName ?? "")}
|
options={sortingMethods.map((item) => item.displayedName)}
|
||||||
value={sortingMethod}
|
value={sortingMethod}
|
||||||
onChange={(newSort) => {
|
onChange={(newSort) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -203,7 +197,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.only_unavailable_videos}>
|
<WithLabel label={format("only_unavailable_videos")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={onlyShowGone}
|
value={onlyShowGone}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -213,14 +207,14 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel label={langui.always_show_info}>
|
<WithLabel label={format("always_show_info")}>
|
||||||
<Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} />
|
<Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
text={langui.reset_all_filters}
|
text={format("reset_all_filters")}
|
||||||
icon="settings_backup_restore"
|
icon="settings_backup_restore"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOnlyShowGone(DEFAULT_FILTERS_STATE.onlyShowGone);
|
setOnlyShowGone(DEFAULT_FILTERS_STATE.onlyShowGone);
|
||||||
|
|
|
@ -14,8 +14,6 @@ import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
import { CustomSearchResponse, meiliSearch } from "helpers/search";
|
import { CustomSearchResponse, meiliSearch } from "helpers/search";
|
||||||
import { MeiliIndices, MeiliVideo } from "shared/meilisearch-graphql-typings/meiliTypes";
|
import { MeiliIndices, MeiliVideo } from "shared/meilisearch-graphql-typings/meiliTypes";
|
||||||
import { PreviewCard } from "components/PreviewCard";
|
import { PreviewCard } from "components/PreviewCard";
|
||||||
|
@ -26,6 +24,7 @@ import { Select } from "components/Inputs/Select";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { Paginator } from "components/Containers/Paginator";
|
import { Paginator } from "components/Containers/Paginator";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -55,27 +54,20 @@ const queryParamSchema = z.object({
|
||||||
interface Props extends AppLayoutRequired {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const Videos = ({ ...otherProps }: Props): JSX.Element => {
|
const Videos = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const router = useTypedRouter(queryParamSchema);
|
const router = useTypedRouter(queryParamSchema);
|
||||||
|
|
||||||
const sortingMethods = useMemo(
|
const sortingMethods = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ meiliAttribute: "sortable_published_date:asc", displayedName: langui.oldest },
|
{ meiliAttribute: "sortable_published_date:asc", displayedName: format("oldest") },
|
||||||
{ meiliAttribute: "sortable_published_date:desc", displayedName: langui.newest },
|
{ meiliAttribute: "sortable_published_date:desc", displayedName: format("newest") },
|
||||||
{ meiliAttribute: "views:asc", displayedName: langui.least_popular },
|
{ meiliAttribute: "views:asc", displayedName: format("least_popular") },
|
||||||
{ meiliAttribute: "views:desc", displayedName: langui.most_popular },
|
{ meiliAttribute: "views:desc", displayedName: format("most_popular") },
|
||||||
{ meiliAttribute: "duration:asc", displayedName: langui.shortest },
|
{ meiliAttribute: "duration:asc", displayedName: format("shortest") },
|
||||||
{ meiliAttribute: "duration:desc", displayedName: langui.longest },
|
{ meiliAttribute: "duration:desc", displayedName: format("longest") },
|
||||||
],
|
],
|
||||||
[
|
[format]
|
||||||
langui.least_popular,
|
|
||||||
langui.longest,
|
|
||||||
langui.most_popular,
|
|
||||||
langui.newest,
|
|
||||||
langui.oldest,
|
|
||||||
langui.shortest,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -158,13 +150,17 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PanelHeader icon="movie" title={langui.videos} description={langui.archives_description} />
|
<PanelHeader
|
||||||
|
icon="movie"
|
||||||
|
title={format("videos")}
|
||||||
|
description={format("archives_description")}
|
||||||
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
className="mb-6 w-full"
|
className="mb-6 w-full"
|
||||||
placeholder={langui.search_title}
|
placeholder={format("search_title")}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(newQuery) => {
|
onChange={(newQuery) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -177,10 +173,10 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WithLabel label={langui.order_by}>
|
<WithLabel label={format("order_by")}>
|
||||||
<Select
|
<Select
|
||||||
className="w-full"
|
className="w-full"
|
||||||
options={sortingMethods.map((item) => item.displayedName ?? "")}
|
options={sortingMethods.map((item) => item.displayedName)}
|
||||||
value={sortingMethod}
|
value={sortingMethod}
|
||||||
onChange={(newSort) => {
|
onChange={(newSort) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -193,7 +189,7 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.only_unavailable_videos}>
|
<WithLabel label={format("only_unavailable_videos")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={onlyShowGone}
|
value={onlyShowGone}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -203,14 +199,14 @@ const Videos = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel label={langui.always_show_info}>
|
<WithLabel label={format("always_show_info")}>
|
||||||
<Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} />
|
<Switch value={keepInfoVisible} onClick={toggleKeepInfoVisible} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
text={langui.reset_all_filters}
|
text={format("reset_all_filters")}
|
||||||
icon="settings_backup_restore"
|
icon="settings_backup_restore"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { Link } from "components/Inputs/Link";
|
import { Link } from "components/Inputs/Link";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -31,22 +32,22 @@ interface Props extends AppLayoutRequired {
|
||||||
|
|
||||||
const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
href="/archives/videos/"
|
href="/archives/videos/"
|
||||||
title={langui.videos}
|
title={format("videos")}
|
||||||
displayOnlyOn={"3ColumnsLayout"}
|
displayOnlyOn={"3ColumnsLayout"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<NavOption title={langui.video} url="#video" border />
|
<NavOption title={format("video")} url="#video" border />
|
||||||
<NavOption title={langui.channel} url="#channel" border />
|
<NavOption title={format("channel")} url="#channel" border />
|
||||||
<NavOption title={langui.description} url="#description" border />
|
<NavOption title={format("description")} url="#description" border />
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
href="/library/"
|
href="/library/"
|
||||||
title={langui.library}
|
title={format("library")}
|
||||||
displayOnlyOn={"1ColumnLayout"}
|
displayOnlyOn={"1ColumnLayout"}
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
@ -97,7 +98,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<Link href={`https://youtu.be/${video.uid}`} alwaysNewTab>
|
<Link href={`https://youtu.be/${video.uid}`} alwaysNewTab>
|
||||||
<Button size="small" text={`${langui.view_on} ${video.source}`} />
|
<Button size="small" text={`${format("view_on")} ${video.source}`} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -106,7 +107,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
{video.channel?.data?.attributes && (
|
{video.channel?.data?.attributes && (
|
||||||
<InsetBox id="channel" className="grid place-items-center">
|
<InsetBox id="channel" className="grid place-items-center">
|
||||||
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-4 text-center">
|
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-4 text-center">
|
||||||
<h2 className="text-2xl">{langui.channel}</h2>
|
<h2 className="text-2xl">{format("channel")}</h2>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
href={`/archives/videos/c/${video.channel.data.attributes.uid}\
|
href={`/archives/videos/c/${video.channel.data.attributes.uid}\
|
||||||
|
@ -115,7 +116,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
<p>
|
<p>
|
||||||
{`${video.channel.data.attributes.subscribers.toLocaleString()}
|
{`${video.channel.data.attributes.subscribers.toLocaleString()}
|
||||||
${langui.subscribers?.toLowerCase()}`}
|
${format("subscribers").toLowerCase()}`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -124,7 +125,7 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
<InsetBox id="description" className="grid place-items-center">
|
<InsetBox id="description" className="grid place-items-center">
|
||||||
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-8">
|
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-8">
|
||||||
<h2 className="text-2xl">{langui.description}</h2>
|
<h2 className="text-2xl">{format("description")}</h2>
|
||||||
<p className="whitespace-pre-line">{video.description}</p>
|
<p className="whitespace-pre-line">{video.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</InsetBox>
|
</InsetBox>
|
||||||
|
|
|
@ -18,10 +18,9 @@ import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/local
|
||||||
import { getDescription } from "helpers/description";
|
import { getDescription } from "helpers/description";
|
||||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -34,7 +33,7 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => {
|
const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
useScrollTopOnChange(Ids.ContentPanel, [chronicle.slug]);
|
useScrollTopOnChange(Ids.ContentPanel, [chronicle.slug]);
|
||||||
|
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
|
@ -71,7 +70,7 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element =
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
displayOnlyOn={"1ColumnLayout"}
|
displayOnlyOn={"1ColumnLayout"}
|
||||||
href="/chronicles"
|
href="/chronicles"
|
||||||
title={langui.chronicles}
|
title={format("chronicles")}
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -119,7 +118,11 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element =
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton displayOnlyOn={"3ColumnsLayout"} href="/chronicles" title={langui.chronicles} />
|
<ReturnButton
|
||||||
|
displayOnlyOn={"3ColumnsLayout"}
|
||||||
|
href="/chronicles"
|
||||||
|
title={format("chronicles")}
|
||||||
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
|
@ -194,7 +197,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.type?.data
|
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.type?.data
|
||||||
?.attributes?.titles?.[0]?.title,
|
?.attributes?.titles?.[0]?.title,
|
||||||
],
|
],
|
||||||
[langui.categories ?? "Categories"]: filterHasAttributes(
|
[langui.category ?? "Categories"]: filterHasAttributes(
|
||||||
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.categories
|
chronicle.chronicles.data[0].attributes.contents.data[0].attributes.categories
|
||||||
?.data,
|
?.data,
|
||||||
["attributes"] as const
|
["attributes"] as const
|
||||||
|
|
|
@ -10,8 +10,7 @@ import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -23,13 +22,13 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
const Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
icon="schedule"
|
icon="schedule"
|
||||||
title={langui.chronicles}
|
title={format("chronicles")}
|
||||||
description={langui.chronicles_description}
|
description={format("chronicles_description")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
|
@ -21,7 +21,6 @@ import {
|
||||||
prettySlug,
|
prettySlug,
|
||||||
} from "helpers/formatters";
|
} from "helpers/formatters";
|
||||||
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||||
import { getStatusDescription } from "helpers/others";
|
|
||||||
import { filterHasAttributes, isDefinedAndNotEmpty } from "helpers/asserts";
|
import { filterHasAttributes, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||||
import { ContentWithTranslations } from "types/types";
|
import { ContentWithTranslations } from "types/types";
|
||||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
|
@ -35,6 +34,7 @@ import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -49,7 +49,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
const isContentPanelAtLeast2xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast2xl);
|
const isContentPanelAtLeast2xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast2xl);
|
||||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
|
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format, formatStatusDescription } = useFormat();
|
||||||
const languages = useAtomGetter(atoms.localData.languages);
|
const languages = useAtomGetter(atoms.localData.languages);
|
||||||
|
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
|
@ -86,9 +86,8 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
fallback: {
|
fallback: {
|
||||||
title: content.folder?.data?.attributes
|
title: content.folder?.data?.attributes
|
||||||
? prettySlug(content.folder.data.attributes.slug)
|
? prettySlug(content.folder.data.attributes.slug)
|
||||||
: langui.contents,
|
: format("contents"),
|
||||||
},
|
},
|
||||||
langui,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
|
@ -102,14 +101,14 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
<h2 className="text-xl">
|
<h2 className="text-xl">
|
||||||
{selectedTranslation.text_set.source_language.data.attributes.code ===
|
{selectedTranslation.text_set.source_language.data.attributes.code ===
|
||||||
selectedTranslation.language?.data?.attributes?.code
|
selectedTranslation.language?.data?.attributes?.code
|
||||||
? langui.transcript_notice
|
? format("transcript_notice")
|
||||||
: langui.translation_notice}
|
: format("translation_notice")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{selectedTranslation.text_set.source_language.data.attributes.code !==
|
{selectedTranslation.text_set.source_language.data.attributes.code !==
|
||||||
selectedTranslation.language?.data?.attributes?.code && (
|
selectedTranslation.language?.data?.attributes?.code && (
|
||||||
<div className="grid place-items-center gap-2">
|
<div className="grid place-items-center gap-2">
|
||||||
<p className="font-headers font-bold">{langui.source_language}:</p>
|
<p className="font-headers font-bold">{format("source_language")}:</p>
|
||||||
<Chip
|
<Chip
|
||||||
text={prettyLanguage(
|
text={prettyLanguage(
|
||||||
selectedTranslation.text_set.source_language.data.attributes.code,
|
selectedTranslation.text_set.source_language.data.attributes.code,
|
||||||
|
@ -120,10 +119,10 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
||||||
<p className="font-headers font-bold">{langui.status}:</p>
|
<p className="font-headers font-bold">{format("status")}:</p>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(selectedTranslation.text_set.status, langui)}
|
content={formatStatusDescription(selectedTranslation.text_set.status)}
|
||||||
maxWidth={"20rem"}>
|
maxWidth={"20rem"}>
|
||||||
<Chip text={selectedTranslation.text_set.status} />
|
<Chip text={selectedTranslation.text_set.status} />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
@ -132,7 +131,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
{selectedTranslation.text_set.transcribers &&
|
{selectedTranslation.text_set.transcribers &&
|
||||||
selectedTranslation.text_set.transcribers.data.length > 0 && (
|
selectedTranslation.text_set.transcribers.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers font-bold">{langui.transcribers}:</p>
|
<p className="font-headers font-bold">{format("transcribers")}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedTranslation.text_set.transcribers.data, [
|
{filterHasAttributes(selectedTranslation.text_set.transcribers.data, [
|
||||||
"attributes",
|
"attributes",
|
||||||
|
@ -149,7 +148,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
{selectedTranslation.text_set.translators &&
|
{selectedTranslation.text_set.translators &&
|
||||||
selectedTranslation.text_set.translators.data.length > 0 && (
|
selectedTranslation.text_set.translators.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers font-bold">{langui.translators}:</p>
|
<p className="font-headers font-bold">{format("translators")}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedTranslation.text_set.translators.data, [
|
{filterHasAttributes(selectedTranslation.text_set.translators.data, [
|
||||||
"attributes",
|
"attributes",
|
||||||
|
@ -166,7 +165,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
{selectedTranslation.text_set.proofreaders &&
|
{selectedTranslation.text_set.proofreaders &&
|
||||||
selectedTranslation.text_set.proofreaders.data.length > 0 && (
|
selectedTranslation.text_set.proofreaders.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers font-bold">{langui.proofreaders}:</p>
|
<p className="font-headers font-bold">{format("proofreaders")}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedTranslation.text_set.proofreaders.data, [
|
{filterHasAttributes(selectedTranslation.text_set.proofreaders.data, [
|
||||||
"attributes",
|
"attributes",
|
||||||
|
@ -182,7 +181,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (
|
{isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers font-bold">{langui.notes}:</p>
|
<p className="font-headers font-bold">{format("notes")}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
<Markdawn text={selectedTranslation.text_set.notes} />
|
<Markdawn text={selectedTranslation.text_set.notes} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -210,7 +209,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
<>
|
<>
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers text-2xl font-bold">{langui.source}</p>
|
<p className="font-headers text-2xl font-bold">{format("source")}</p>
|
||||||
<div className="mt-6 grid place-items-center gap-6">
|
<div className="mt-6 grid place-items-center gap-6">
|
||||||
{filterHasAttributes(content.ranged_contents.data, [
|
{filterHasAttributes(content.ranged_contents.data, [
|
||||||
"attributes.library_item.data.attributes",
|
"attributes.library_item.data.attributes",
|
||||||
|
@ -283,7 +282,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{previousContent?.attributes && (
|
{previousContent?.attributes && (
|
||||||
<div className="mt-12 mb-8 w-full">
|
<div className="mt-12 mb-8 w-full">
|
||||||
<h2 className="mb-4 text-center text-2xl">{langui.previous_content}</h2>
|
<h2 className="mb-4 text-center text-2xl">{format("previous_content")}</h2>
|
||||||
<TranslatedPreviewLine
|
<TranslatedPreviewLine
|
||||||
href={`/contents/${previousContent.attributes.slug}`}
|
href={`/contents/${previousContent.attributes.slug}`}
|
||||||
translations={filterHasAttributes(previousContent.attributes.translations, [
|
translations={filterHasAttributes(previousContent.attributes.translations, [
|
||||||
|
@ -328,7 +327,7 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
{nextContent?.attributes && (
|
{nextContent?.attributes && (
|
||||||
<>
|
<>
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
<h2 className="mb-4 text-center text-2xl">{langui.followup_content}</h2>
|
<h2 className="mb-4 text-center text-2xl">{format("followup_content")}</h2>
|
||||||
<TranslatedPreviewLine
|
<TranslatedPreviewLine
|
||||||
href={`/contents/${nextContent.attributes.slug}`}
|
href={`/contents/${nextContent.attributes.slug}`}
|
||||||
translations={filterHasAttributes(nextContent.attributes.translations, [
|
translations={filterHasAttributes(nextContent.attributes.translations, [
|
||||||
|
@ -404,7 +403,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
[langui.type ?? "Type"]: [
|
[langui.type ?? "Type"]: [
|
||||||
content.contents.data[0].attributes.type?.data?.attributes?.titles?.[0]?.title,
|
content.contents.data[0].attributes.type?.data?.attributes?.titles?.[0]?.title,
|
||||||
],
|
],
|
||||||
[langui.categories ?? "Categories"]: filterHasAttributes(
|
[langui.category ?? "Categories"]: filterHasAttributes(
|
||||||
content.contents.data[0].attributes.categories?.data,
|
content.contents.data[0].attributes.categories?.data,
|
||||||
["attributes"] as const
|
["attributes"] as const
|
||||||
).map((category) => category.attributes.short),
|
).map((category) => category.attributes.short),
|
||||||
|
|
|
@ -22,14 +22,13 @@ import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { atoms } from "contexts/atoms";
|
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
||||||
import { MeiliContent, MeiliIndices } from "shared/meilisearch-graphql-typings/meiliTypes";
|
import { MeiliContent, MeiliIndices } from "shared/meilisearch-graphql-typings/meiliTypes";
|
||||||
import { useTypedRouter } from "hooks/useTypedRouter";
|
import { useTypedRouter } from "hooks/useTypedRouter";
|
||||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
import { prettySlug } from "helpers/formatters";
|
import { prettySlug } from "helpers/formatters";
|
||||||
import { Paginator } from "components/Containers/Paginator";
|
import { Paginator } from "components/Containers/Paginator";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -58,16 +57,16 @@ interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const Contents = (props: Props): JSX.Element => {
|
const Contents = (props: Props): JSX.Element => {
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const router = useTypedRouter(queryParamSchema);
|
const router = useTypedRouter(queryParamSchema);
|
||||||
|
|
||||||
const sortingMethods = useMemo(
|
const sortingMethods = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ meiliAttribute: "slug:asc", displayedName: langui.name },
|
{ meiliAttribute: "slug:asc", displayedName: format("name") },
|
||||||
{ meiliAttribute: "sortable_updated_date:asc", displayedName: langui.oldest },
|
{ meiliAttribute: "sortable_updated_date:asc", displayedName: format("oldest") },
|
||||||
{ meiliAttribute: "sortable_updated_date:desc", displayedName: langui.newest },
|
{ meiliAttribute: "sortable_updated_date:desc", displayedName: format("newest") },
|
||||||
],
|
],
|
||||||
[langui.name, langui.newest, langui.oldest]
|
[format]
|
||||||
);
|
);
|
||||||
|
|
||||||
const [sortingMethod, setSortingMethod] = useState<number>(
|
const [sortingMethod, setSortingMethod] = useState<number>(
|
||||||
|
@ -131,19 +130,19 @@ const Contents = (props: Props): JSX.Element => {
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
icon="workspaces"
|
icon="workspaces"
|
||||||
title={langui.contents}
|
title={format("contents")}
|
||||||
description={langui.contents_description}
|
description={format("contents_description")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<Button href="/contents" text={langui.switch_to_folder_view} icon="folder" />
|
<Button href="/contents" text={format("switch_to_folder_view")} icon="folder" />
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
className="mb-6 w-full"
|
className="mb-6 w-full"
|
||||||
placeholder={langui.search_title ?? "Search..."}
|
placeholder={format("search_title")}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(name) => {
|
onChange={(name) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -156,10 +155,10 @@ const Contents = (props: Props): JSX.Element => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WithLabel label={langui.order_by}>
|
<WithLabel label={format("order_by")}>
|
||||||
<Select
|
<Select
|
||||||
className="w-full"
|
className="w-full"
|
||||||
options={sortingMethods.map((item) => item.displayedName ?? "")}
|
options={sortingMethods.map((item) => item.displayedName)}
|
||||||
value={sortingMethod}
|
value={sortingMethod}
|
||||||
onChange={(newSort) => {
|
onChange={(newSort) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -173,7 +172,7 @@ const Contents = (props: Props): JSX.Element => {
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel label={langui.always_show_info}>
|
<WithLabel label={format("always_show_info")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={keepInfoVisible}
|
value={keepInfoVisible}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -186,7 +185,7 @@ const Contents = (props: Props): JSX.Element => {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
text={langui.reset_all_filters}
|
text={format("reset_all_filters")}
|
||||||
icon="settings_backup_restore"
|
icon="settings_backup_restore"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { TranslatedPreviewFolder } from "components/Contents/PreviewFolder";
|
import { TranslatedPreviewFolder } from "components/Contents/PreviewFolder";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -33,20 +34,20 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Element => {
|
const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
icon="workspaces"
|
icon="workspaces"
|
||||||
title={langui.contents}
|
title={format("contents")}
|
||||||
description={langui.contents_description}
|
description={format("contents_description")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<Button href="/contents/all" text={langui.switch_to_grid_view} icon="apps" />
|
<Button href="/contents/all" text={format("switch_to_grid_view")} icon="apps" />
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Elemen
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
renderWhenEmpty={() => <></>}
|
renderWhenEmpty={() => <></>}
|
||||||
groupingFunction={() => [langui.folders ?? "Folders"]}
|
groupingFunction={() => [format("folders")]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SmartList
|
<SmartList
|
||||||
|
@ -159,7 +160,7 @@ const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Elemen
|
||||||
"grid-cols-2 gap-x-3 gap-y-5"
|
"grid-cols-2 gap-x-3 gap-y-5"
|
||||||
)}
|
)}
|
||||||
renderWhenEmpty={() => <></>}
|
renderWhenEmpty={() => <></>}
|
||||||
groupingFunction={() => [langui.contents ?? "Contents"]}
|
groupingFunction={() => [format("contents")]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{folder.contents?.data.length === 0 && folder.subfolders?.data.length === 0 && (
|
{folder.contents?.data.length === 0 && folder.subfolders?.data.length === 0 && (
|
||||||
|
@ -258,13 +259,13 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const NoContentNorFolderMessage = () => {
|
const NoContentNorFolderMessage = () => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<div className="grid place-content-center">
|
<div className="grid place-content-center">
|
||||||
<div
|
<div
|
||||||
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
||||||
border-dark p-8 text-dark opacity-40">
|
border-dark p-8 text-dark opacity-40">
|
||||||
<p className="max-w-xs text-2xl">{langui.empty_folder_message}</p>
|
<p className="max-w-xs text-2xl">{format("empty_folder_message")}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,7 +24,6 @@ import {
|
||||||
prettyDate,
|
prettyDate,
|
||||||
prettyInlineTitle,
|
prettyInlineTitle,
|
||||||
prettyItemSubType,
|
prettyItemSubType,
|
||||||
prettyItemType,
|
|
||||||
prettyPrice,
|
prettyPrice,
|
||||||
prettySlug,
|
prettySlug,
|
||||||
prettyURL,
|
prettyURL,
|
||||||
|
@ -54,6 +53,7 @@ import { Ids } from "types/ids";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { Link } from "components/Inputs/Link";
|
import { Link } from "components/Inputs/Link";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -74,7 +74,7 @@ interface Props extends AppLayoutRequired {
|
||||||
|
|
||||||
const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
const currency = useAtomGetter(atoms.settings.currency);
|
const currency = useAtomGetter(atoms.settings.currency);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format, formatLibraryItemType } = useFormat();
|
||||||
const currencies = useAtomGetter(atoms.localData.currencies);
|
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||||
|
|
||||||
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
|
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
|
||||||
|
@ -99,13 +99,13 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton href="/library/" title={langui.library} displayOnlyOn="3ColumnsLayout" />
|
<ReturnButton href="/library/" title={format("library")} displayOnlyOn="3ColumnsLayout" />
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<NavOption
|
<NavOption
|
||||||
title={langui.summary}
|
title={format("summary")}
|
||||||
url={`#${intersectionIds[0]}`}
|
url={`#${intersectionIds[0]}`}
|
||||||
border
|
border
|
||||||
active={currentIntersection === 0}
|
active={currentIntersection === 0}
|
||||||
|
@ -113,7 +113,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.gallery && item.gallery.data.length > 0 && (
|
{item.gallery && item.gallery.data.length > 0 && (
|
||||||
<NavOption
|
<NavOption
|
||||||
title={langui.gallery}
|
title={format("gallery")}
|
||||||
url={`#${intersectionIds[1]}`}
|
url={`#${intersectionIds[1]}`}
|
||||||
border
|
border
|
||||||
active={currentIntersection === 1}
|
active={currentIntersection === 1}
|
||||||
|
@ -121,7 +121,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
title={langui.details}
|
title={format("details")}
|
||||||
url={`#${intersectionIds[2]}`}
|
url={`#${intersectionIds[2]}`}
|
||||||
border
|
border
|
||||||
active={currentIntersection === 2}
|
active={currentIntersection === 2}
|
||||||
|
@ -129,7 +129,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.subitems && item.subitems.data.length > 0 && (
|
{item.subitems && item.subitems.data.length > 0 && (
|
||||||
<NavOption
|
<NavOption
|
||||||
title={isVariantSet ? langui.variants : langui.subitems}
|
title={format(isVariantSet ? "variant" : "subitem", { count: Infinity })}
|
||||||
url={`#${intersectionIds[3]}`}
|
url={`#${intersectionIds[3]}`}
|
||||||
border
|
border
|
||||||
active={currentIntersection === 3}
|
active={currentIntersection === 3}
|
||||||
|
@ -138,7 +138,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.contents && item.contents.data.length > 0 && (
|
{item.contents && item.contents.data.length > 0 && (
|
||||||
<NavOption
|
<NavOption
|
||||||
title={langui.contents}
|
title={format("contents")}
|
||||||
url={`#${intersectionIds[4]}`}
|
url={`#${intersectionIds[4]}`}
|
||||||
border
|
border
|
||||||
active={currentIntersection === 4}
|
active={currentIntersection === 4}
|
||||||
|
@ -152,7 +152,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
href="/library/"
|
href="/library/"
|
||||||
title={langui.library}
|
title={format("library")}
|
||||||
displayOnlyOn="1ColumnLayout"
|
displayOnlyOn="1ColumnLayout"
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
@ -180,7 +180,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-8">
|
<div className="grid w-[clamp(0px,100%,42rem)] place-items-center gap-8">
|
||||||
{item.subitem_of?.data[0]?.attributes && (
|
{item.subitem_of?.data[0]?.attributes && (
|
||||||
<div className="grid place-items-center">
|
<div className="grid place-items-center">
|
||||||
<p>{langui.subitem_of}</p>
|
<p>{format("subitem_of_x", { x: "" })}</p>
|
||||||
<Button
|
<Button
|
||||||
href={`/library/${item.subitem_of.data[0].attributes.slug}`}
|
href={`/library/${item.subitem_of.data[0].attributes.slug}`}
|
||||||
text={prettyInlineTitle(
|
text={prettyInlineTitle(
|
||||||
|
@ -212,7 +212,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
<>
|
<>
|
||||||
{item.urls?.length ? (
|
{item.urls?.length ? (
|
||||||
<div className="flex flex-row place-items-center gap-3">
|
<div className="flex flex-row place-items-center gap-3">
|
||||||
<p>{langui.available_at}</p>
|
<p>{format("available_at")}</p>
|
||||||
{filterHasAttributes(item.urls, ["url"] as const).map((url, index) => (
|
{filterHasAttributes(item.urls, ["url"] as const).map((url, index) => (
|
||||||
<Fragment key={index}>
|
<Fragment key={index}>
|
||||||
<Button href={url.url} text={prettyURL(url.url)} alwaysNewTab />
|
<Button href={url.url} text={prettyURL(url.url)} alwaysNewTab />
|
||||||
|
@ -220,7 +220,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<p>{langui.item_not_available}</p>
|
<p>{format("item_not_available")}</p>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -229,7 +229,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.gallery && item.gallery.data.length > 0 && (
|
{item.gallery && item.gallery.data.length > 0 && (
|
||||||
<div id={intersectionIds[1]} className="grid w-full place-items-center gap-8">
|
<div id={intersectionIds[1]} className="grid w-full place-items-center gap-8">
|
||||||
<h2 className="text-2xl">{langui.gallery}</h2>
|
<h2 className="text-2xl">{format("gallery")}</h2>
|
||||||
<div
|
<div
|
||||||
className="grid w-full grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-end
|
className="grid w-full grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] items-end
|
||||||
gap-8">
|
gap-8">
|
||||||
|
@ -262,7 +262,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
<InsetBox id={intersectionIds[2]} className="grid place-items-center">
|
<InsetBox id={intersectionIds[2]} className="grid place-items-center">
|
||||||
<div className="place-items grid w-[clamp(0px,100%,42rem)] gap-10">
|
<div className="place-items grid w-[clamp(0px,100%,42rem)] gap-10">
|
||||||
<h2 className="text-center text-2xl">{langui.details}</h2>
|
<h2 className="text-center text-2xl">{format("details")}</h2>
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
"grid place-items-center gap-y-8",
|
"grid place-items-center gap-y-8",
|
||||||
|
@ -270,9 +270,9 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
)}>
|
)}>
|
||||||
{item.metadata?.[0] && (
|
{item.metadata?.[0] && (
|
||||||
<div className="grid place-content-start place-items-center">
|
<div className="grid place-content-start place-items-center">
|
||||||
<h3 className="text-xl">{langui.type}</h3>
|
<h3 className="text-xl">{format("type", { count: 1 })}</h3>
|
||||||
<div className="grid grid-flow-col gap-1">
|
<div className="grid grid-flow-col gap-1">
|
||||||
<Chip text={prettyItemType(item.metadata[0], langui)} />
|
<Chip text={formatLibraryItemType(item.metadata[0])} />
|
||||||
{"›"}
|
{"›"}
|
||||||
<Chip text={prettyItemSubType(item.metadata[0])} />
|
<Chip text={prettyItemSubType(item.metadata[0])} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -281,14 +281,14 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.release_date && (
|
{item.release_date && (
|
||||||
<div className="grid place-content-start place-items-center">
|
<div className="grid place-content-start place-items-center">
|
||||||
<h3 className="text-xl">{langui.release_date}</h3>
|
<h3 className="text-xl">{format("release_date")}</h3>
|
||||||
<p>{prettyDate(item.release_date, router.locale)}</p>
|
<p>{prettyDate(item.release_date, router.locale)}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{item.price && (
|
{item.price && (
|
||||||
<div className="grid place-content-start place-items-center text-center">
|
<div className="grid place-content-start place-items-center text-center">
|
||||||
<h3 className="text-xl">{langui.price}</h3>
|
<h3 className="text-xl">{format("price")}</h3>
|
||||||
<p>
|
<p>
|
||||||
{prettyPrice(
|
{prettyPrice(
|
||||||
item.price,
|
item.price,
|
||||||
|
@ -299,7 +299,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
{item.price.currency?.data?.attributes?.code !== currency && (
|
{item.price.currency?.data?.attributes?.code !== currency && (
|
||||||
<p>
|
<p>
|
||||||
{prettyPrice(item.price, currencies, currency)} <br />(
|
{prettyPrice(item.price, currencies, currency)} <br />(
|
||||||
{langui.calculated?.toLowerCase()})
|
{format("calculated").toLowerCase()})
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -308,7 +308,9 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.categories && item.categories.data.length > 0 && (
|
{item.categories && item.categories.data.length > 0 && (
|
||||||
<div className="flex flex-col place-items-center gap-2">
|
<div className="flex flex-col place-items-center gap-2">
|
||||||
<h3 className="text-xl">{langui.categories}</h3>
|
<h3 className="text-xl">
|
||||||
|
{format("category", { count: item.categories.data.length })}
|
||||||
|
</h3>
|
||||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||||
{filterHasAttributes(item.categories.data, ["attributes"] as const).map(
|
{filterHasAttributes(item.categories.data, ["attributes"] as const).map(
|
||||||
(category) => (
|
(category) => (
|
||||||
|
@ -325,7 +327,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
"grid gap-4",
|
"grid gap-4",
|
||||||
cIf(!isContentPanelAtLeast3xl, "place-items-center")
|
cIf(!isContentPanelAtLeast3xl, "place-items-center")
|
||||||
)}>
|
)}>
|
||||||
<h3 className="text-xl">{langui.size}</h3>
|
<h3 className="text-xl">{format("size")}</h3>
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
"grid w-full",
|
"grid w-full",
|
||||||
|
@ -344,7 +346,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
"place-items-center"
|
"place-items-center"
|
||||||
)
|
)
|
||||||
)}>
|
)}>
|
||||||
<p className="font-bold">{langui.width}:</p>
|
<p className="font-bold">{format("width")}:</p>
|
||||||
<div>
|
<div>
|
||||||
<p>{item.size.width} mm</p>
|
<p>{item.size.width} mm</p>
|
||||||
<p>{convertMmToInch(item.size.width)} in</p>
|
<p>{convertMmToInch(item.size.width)} in</p>
|
||||||
|
@ -359,7 +361,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
"place-items-center"
|
"place-items-center"
|
||||||
)
|
)
|
||||||
)}>
|
)}>
|
||||||
<p className="font-bold">{langui.height}:</p>
|
<p className="font-bold">{format("height")}:</p>
|
||||||
<div>
|
<div>
|
||||||
<p>{item.size.height} mm</p>
|
<p>{item.size.height} mm</p>
|
||||||
<p>{convertMmToInch(item.size.height)} in</p>
|
<p>{convertMmToInch(item.size.height)} in</p>
|
||||||
|
@ -375,7 +377,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
"place-items-center"
|
"place-items-center"
|
||||||
)
|
)
|
||||||
)}>
|
)}>
|
||||||
<p className="font-bold">{langui.thickness}:</p>
|
<p className="font-bold">{format("thickness")}:</p>
|
||||||
<div>
|
<div>
|
||||||
<p>{item.size.thickness} mm</p>
|
<p>{item.size.thickness} mm</p>
|
||||||
<p>{convertMmToInch(item.size.thickness)} in</p>
|
<p>{convertMmToInch(item.size.thickness)} in</p>
|
||||||
|
@ -393,44 +395,53 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
"grid gap-4",
|
"grid gap-4",
|
||||||
cIf(!isContentPanelAtLeast3xl, "place-items-center")
|
cIf(!isContentPanelAtLeast3xl, "place-items-center")
|
||||||
)}>
|
)}>
|
||||||
<h3 className="text-xl">{langui.type_information}</h3>
|
<h3 className="text-xl">{format("type_information")}</h3>
|
||||||
<div className="flex flex-wrap place-content-between gap-x-8">
|
<div className="flex flex-wrap place-content-between gap-x-8">
|
||||||
{item.metadata?.[0]?.__typename === "ComponentMetadataBooks" && (
|
{item.metadata?.[0]?.__typename === "ComponentMetadataBooks" && (
|
||||||
<>
|
<>
|
||||||
|
{isDefined(item.metadata[0].page_count) && (
|
||||||
<div className="flex flex-row place-content-start gap-4">
|
<div className="flex flex-row place-content-start gap-4">
|
||||||
<p className="font-bold">{langui.pages}:</p>
|
<p className="font-bold">{format("page", { count: Infinity })}:</p>
|
||||||
<p>{item.metadata[0].page_count}</p>
|
<p>{item.metadata[0].page_count}</p>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex flex-row place-content-start gap-4">
|
<div className="flex flex-row place-content-start gap-4">
|
||||||
<p className="font-bold">{langui.binding}:</p>
|
<p className="font-bold">{format("binding")}:</p>
|
||||||
<p>
|
<p>
|
||||||
{item.metadata[0].binding_type ===
|
{item.metadata[0].binding_type ===
|
||||||
Enum_Componentmetadatabooks_Binding_Type.Paperback
|
Enum_Componentmetadatabooks_Binding_Type.Paperback
|
||||||
? langui.paperback
|
? format("paperback")
|
||||||
: item.metadata[0].binding_type ===
|
: item.metadata[0].binding_type ===
|
||||||
Enum_Componentmetadatabooks_Binding_Type.Hardcover
|
Enum_Componentmetadatabooks_Binding_Type.Hardcover
|
||||||
? langui.hardcover
|
? format("hardcover")
|
||||||
: ""}
|
: ""}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row place-content-start gap-4">
|
<div className="flex flex-row place-content-start gap-4">
|
||||||
<p className="font-bold">{langui.page_order}:</p>
|
<p className="font-bold">{format("page_order")}:</p>
|
||||||
<p>
|
<p>
|
||||||
{item.metadata[0].page_order ===
|
{item.metadata[0].page_order ===
|
||||||
Enum_Componentmetadatabooks_Page_Order.LeftToRight
|
Enum_Componentmetadatabooks_Page_Order.LeftToRight
|
||||||
? langui.left_to_right
|
? format("left_to_right")
|
||||||
: langui.right_to_left}
|
: format("right_to_left")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isDefined(item.metadata[0].languages) && (
|
||||||
<div className="flex flex-row place-content-start gap-4">
|
<div className="flex flex-row place-content-start gap-4">
|
||||||
<p className="font-bold">{langui.languages}:</p>
|
<p className="font-bold">
|
||||||
{item.metadata[0]?.languages?.data.map((lang) => (
|
{format("language", {
|
||||||
|
count: item.metadata[0].languages.data.length,
|
||||||
|
})}
|
||||||
|
:
|
||||||
|
</p>
|
||||||
|
{item.metadata[0].languages.data.map((lang) => (
|
||||||
<p key={lang.attributes?.code}>{lang.attributes?.name}</p>
|
<p key={lang.attributes?.code}>{lang.attributes?.name}</p>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -441,10 +452,12 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.subitems && item.subitems.data.length > 0 && (
|
{item.subitems && item.subitems.data.length > 0 && (
|
||||||
<div id={intersectionIds[3]} className="grid w-full place-items-center gap-8">
|
<div id={intersectionIds[3]} className="grid w-full place-items-center gap-8">
|
||||||
<h2 className="text-2xl">{isVariantSet ? langui.variants : langui.subitems}</h2>
|
<h2 className="text-2xl">
|
||||||
|
{format(isVariantSet ? "variant" : "subitem", { count: Infinity })}
|
||||||
|
</h2>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel label={langui.always_show_info}>
|
<WithLabel label={format("always_show_info")}>
|
||||||
<Switch onClick={toggleKeepInfoVisible} value={keepInfoVisible} />
|
<Switch onClick={toggleKeepInfoVisible} value={keepInfoVisible} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
)}
|
)}
|
||||||
|
@ -493,10 +506,10 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{item.contents && item.contents.data.length > 0 && (
|
{item.contents && item.contents.data.length > 0 && (
|
||||||
<div id={intersectionIds[4]} className="grid w-full place-items-center gap-8">
|
<div id={intersectionIds[4]} className="grid w-full place-items-center gap-8">
|
||||||
<h2 className="-mb-6 text-2xl">{langui.contents}</h2>
|
<h2 className="-mb-6 text-2xl">{format("contents")}</h2>
|
||||||
{displayOpenScans && (
|
{displayOpenScans && (
|
||||||
<div className="grid grid-flow-col gap-4">
|
<div className="grid grid-flow-col gap-4">
|
||||||
<Button href={`/library/${item.slug}/reader`} text={langui.view_scans} />
|
<Button href={`/library/${item.slug}/reader`} text={format("view_scans")} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="max-w- grid w-full gap-4">
|
<div className="max-w- grid w-full gap-4">
|
||||||
|
@ -576,7 +589,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
const description = getDescription(
|
const description = getDescription(
|
||||||
item.libraryItems.data[0].attributes.descriptions?.[0]?.description,
|
item.libraryItems.data[0].attributes.descriptions?.[0]?.description,
|
||||||
{
|
{
|
||||||
[langui.categories ?? "Categories"]: filterHasAttributes(
|
[langui.category ?? "Categories"]: filterHasAttributes(
|
||||||
item.libraryItems.data[0].attributes.categories?.data,
|
item.libraryItems.data[0].attributes.categories?.data,
|
||||||
["attributes.short"]
|
["attributes.short"]
|
||||||
).map((category) => category.attributes.short),
|
).map((category) => category.attributes.short),
|
||||||
|
@ -651,7 +664,7 @@ const ContentLine = ({
|
||||||
parentSlug,
|
parentSlug,
|
||||||
condensed,
|
condensed,
|
||||||
}: ContentLineProps): JSX.Element => {
|
}: ContentLineProps): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const { value: isOpened, toggle: toggleOpened } = useBoolean(false);
|
const { value: isOpened, toggle: toggleOpened } = useBoolean(false);
|
||||||
const [selectedTranslation] = useSmartLanguage({
|
const [selectedTranslation] = useSmartLanguage({
|
||||||
items: content?.translations ?? [],
|
items: content?.translations ?? [],
|
||||||
|
@ -692,15 +705,15 @@ const ContentLine = ({
|
||||||
{hasScanSet && (
|
{hasScanSet && (
|
||||||
<Button
|
<Button
|
||||||
href={`/library/${parentSlug}/reader?page=${rangeStart}`}
|
href={`/library/${parentSlug}/reader?page=${rangeStart}`}
|
||||||
text={langui.view_scans}
|
text={format("view_scans")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isDefined(content) && (
|
{isDefined(content) && (
|
||||||
<Button href={`/contents/${content.slug}`} text={langui.open_content} />
|
<Button href={`/contents/${content.slug}`} text={format("open_content")} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
langui.content_is_not_available
|
format("content_is_not_available")
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -747,15 +760,15 @@ const ContentLine = ({
|
||||||
{hasScanSet && (
|
{hasScanSet && (
|
||||||
<Button
|
<Button
|
||||||
href={`/library/${parentSlug}/reader?page=${rangeStart}`}
|
href={`/library/${parentSlug}/reader?page=${rangeStart}`}
|
||||||
text={langui.view_scans}
|
text={format("view_scans")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isDefined(content) && (
|
{isDefined(content) && (
|
||||||
<Button href={`/contents/${content.slug}`} text={langui.open_content} />
|
<Button href={`/contents/${content.slug}`} text={format("open_content")} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
langui.content_is_not_available
|
format("content_is_not_available")
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
UploadImageFragment,
|
UploadImageFragment,
|
||||||
} from "graphql/generated";
|
} from "graphql/generated";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { getStatusDescription, sortRangedContent } from "helpers/others";
|
import { sortRangedContent } from "helpers/others";
|
||||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
|
@ -40,6 +40,7 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
|
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
|
||||||
import { useIsWebkit } from "hooks/useIsWebkit";
|
import { useIsWebkit } from "hooks/useIsWebkit";
|
||||||
import { useTypedRouter } from "hooks/useTypedRouter";
|
import { useTypedRouter } from "hooks/useTypedRouter";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
type BookType = "book" | "manga";
|
type BookType = "book" | "manga";
|
||||||
type DisplayMode = "double" | "single";
|
type DisplayMode = "double" | "single";
|
||||||
|
@ -96,7 +97,7 @@ const LibrarySlug = ({
|
||||||
...otherProps
|
...otherProps
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const isDarkMode = useAtomGetter(atoms.settings.darkMode);
|
const isDarkMode = useAtomGetter(atoms.settings.darkMode);
|
||||||
const {
|
const {
|
||||||
filterSettings,
|
filterSettings,
|
||||||
|
@ -278,34 +279,34 @@ const LibrarySlug = ({
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton title={langui.item} href={`/library/${itemSlug}`} />
|
<ReturnButton title={format("item", { count: Infinity })} href={`/library/${itemSlug}`} />
|
||||||
|
|
||||||
<div className="mt-4 grid gap-2">
|
<div className="mt-4 grid gap-2">
|
||||||
<WithLabel label={langui.paper_texture}>
|
<WithLabel label={format("paper_texture")}>
|
||||||
<Switch value={filterSettings.paperTexture} onClick={togglePaperTexture} />
|
<Switch value={filterSettings.paperTexture} onClick={togglePaperTexture} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.book_fold}>
|
<WithLabel label={format("book_fold")}>
|
||||||
<Switch value={filterSettings.bookFold} onClick={toggleBookFold} />
|
<Switch value={filterSettings.bookFold} onClick={toggleBookFold} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.lighting}>
|
<WithLabel label={format("lighting")}>
|
||||||
<Switch value={filterSettings.lighting} onClick={toggleLighting} />
|
<Switch value={filterSettings.lighting} onClick={toggleLighting} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.side_pages}>
|
<WithLabel label={format("side_pages")}>
|
||||||
<Switch value={isSidePagesEnabled} onClick={toggleIsSidePagesEnabled} />
|
<Switch value={isSidePagesEnabled} onClick={toggleIsSidePagesEnabled} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
{!isWebkit && (
|
{!isWebkit && (
|
||||||
<WithLabel label={langui.shadow}>
|
<WithLabel label={format("shadow")}>
|
||||||
<Switch value={filterSettings.dropShadow} onClick={toggleDropShadow} />
|
<Switch value={filterSettings.dropShadow} onClick={toggleDropShadow} />
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 grid">
|
<div className="mt-4 grid">
|
||||||
<p>{langui.night_reader}:</p>
|
<p>{format("night_reader")}:</p>
|
||||||
<Slider
|
<Slider
|
||||||
min={0}
|
min={0}
|
||||||
max={10}
|
max={10}
|
||||||
|
@ -318,18 +319,18 @@ const LibrarySlug = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 grid gap-2">
|
<div className="mt-8 grid gap-2">
|
||||||
<p>{langui.reading_layout}:</p>
|
<p>{format("reading_layout")}:</p>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
buttonsProps={[
|
buttonsProps={[
|
||||||
{
|
{
|
||||||
icon: "description",
|
icon: "description",
|
||||||
tooltip: langui.single_page_view,
|
tooltip: format("single_page_view"),
|
||||||
active: displayMode === "single",
|
active: displayMode === "single",
|
||||||
onClick: () => changeDisplayMode("single"),
|
onClick: () => changeDisplayMode("single"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "auto_stories",
|
icon: "auto_stories",
|
||||||
tooltip: langui.double_page_view,
|
tooltip: format("double_page_view"),
|
||||||
active: displayMode === "double",
|
active: displayMode === "double",
|
||||||
onClick: () => changeDisplayMode("double"),
|
onClick: () => changeDisplayMode("double"),
|
||||||
},
|
},
|
||||||
|
@ -338,7 +339,7 @@ const LibrarySlug = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 grid gap-2">
|
<div className="mt-4 grid gap-2">
|
||||||
<p>{langui.quality}:</p>
|
<p>{format("quality")}:</p>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
buttonsProps={[
|
buttonsProps={[
|
||||||
{
|
{
|
||||||
|
@ -357,7 +358,7 @@ const LibrarySlug = ({
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
text={langui.reset_all_options}
|
text={format("reset_all_options")}
|
||||||
icon="settings_backup_restore"
|
icon="settings_backup_restore"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
resetReaderSettings();
|
resetReaderSettings();
|
||||||
|
@ -787,7 +788,7 @@ interface ScanSetProps {
|
||||||
|
|
||||||
const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps): JSX.Element => {
|
const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps): JSX.Element => {
|
||||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format, formatStatusDescription } = useFormat();
|
||||||
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: scanSet,
|
items: scanSet,
|
||||||
languageExtractor: useCallback(
|
languageExtractor: useCallback(
|
||||||
|
@ -844,8 +845,8 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
|
||||||
text={
|
text={
|
||||||
selectedScan.language?.data?.attributes?.code ===
|
selectedScan.language?.data?.attributes?.code ===
|
||||||
selectedScan.source_language?.data?.attributes?.code
|
selectedScan.source_language?.data?.attributes?.code
|
||||||
? langui.scan ?? "Scan"
|
? format("scan")
|
||||||
: langui.scanlation ?? "Scanlation"
|
: format("scanlation")
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -854,7 +855,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
|
||||||
{content?.data?.attributes && isDefinedAndNotEmpty(content.data.attributes.slug) && (
|
{content?.data?.attributes && isDefinedAndNotEmpty(content.data.attributes.slug) && (
|
||||||
<Button
|
<Button
|
||||||
href={`/contents/${content.data.attributes.slug}`}
|
href={`/contents/${content.data.attributes.slug}`}
|
||||||
text={langui.open_content}
|
text={format("open_content")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -863,17 +864,15 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid place-content-center place-items-center">
|
<div className="grid place-content-center place-items-center">
|
||||||
<p className="font-headers font-bold">{langui.status}:</p>
|
<p className="font-headers font-bold">{format("status")}:</p>
|
||||||
<ToolTip
|
<ToolTip content={formatStatusDescription(selectedScan.status)} maxWidth={"20rem"}>
|
||||||
content={getStatusDescription(selectedScan.status, langui)}
|
|
||||||
maxWidth={"20rem"}>
|
|
||||||
<Chip text={selectedScan.status} />
|
<Chip text={selectedScan.status} />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedScan.scanners && selectedScan.scanners.data.length > 0 && (
|
{selectedScan.scanners && selectedScan.scanners.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers font-bold">{langui.scanners}:</p>
|
<p className="font-headers font-bold">{format("scanners")}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.scanners.data, [
|
{filterHasAttributes(selectedScan.scanners.data, [
|
||||||
"id",
|
"id",
|
||||||
|
@ -889,7 +888,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
|
||||||
|
|
||||||
{selectedScan.cleaners && selectedScan.cleaners.data.length > 0 && (
|
{selectedScan.cleaners && selectedScan.cleaners.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers font-bold">{langui.cleaners}:</p>
|
<p className="font-headers font-bold">{format("cleaners")}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.cleaners.data, [
|
{filterHasAttributes(selectedScan.cleaners.data, [
|
||||||
"id",
|
"id",
|
||||||
|
@ -905,7 +904,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
|
||||||
|
|
||||||
{selectedScan.typesetters && selectedScan.typesetters.data.length > 0 && (
|
{selectedScan.typesetters && selectedScan.typesetters.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers font-bold">{langui.typesetters}:</p>
|
<p className="font-headers font-bold">{format("typesetters")}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.typesetters.data, [
|
{filterHasAttributes(selectedScan.typesetters.data, [
|
||||||
"id",
|
"id",
|
||||||
|
@ -921,7 +920,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
|
||||||
|
|
||||||
{isDefinedAndNotEmpty(selectedScan.notes) && (
|
{isDefinedAndNotEmpty(selectedScan.notes) && (
|
||||||
<ToolTip content={selectedScan.notes}>
|
<ToolTip content={selectedScan.notes}>
|
||||||
<Chip text={langui.notes ?? "Notes"} />
|
<Chip text={format("notes")} />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,8 +25,6 @@ import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { atoms } from "contexts/atoms";
|
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
||||||
import { MeiliIndices, MeiliLibraryItem } from "shared/meilisearch-graphql-typings/meiliTypes";
|
import { MeiliIndices, MeiliLibraryItem } from "shared/meilisearch-graphql-typings/meiliTypes";
|
||||||
import { useTypedRouter } from "hooks/useTypedRouter";
|
import { useTypedRouter } from "hooks/useTypedRouter";
|
||||||
|
@ -36,6 +34,7 @@ import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||||
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
|
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
|
||||||
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
||||||
import { Paginator } from "components/Containers/Paginator";
|
import { Paginator } from "components/Containers/Paginator";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -72,16 +71,16 @@ interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const Library = (props: Props): JSX.Element => {
|
const Library = (props: Props): JSX.Element => {
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const { libraryItemUserStatus } = useLibraryItemUserStatus();
|
const { libraryItemUserStatus } = useLibraryItemUserStatus();
|
||||||
|
|
||||||
const sortingMethods = useMemo(
|
const sortingMethods = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ meiliAttribute: "sortable_name:asc", displayedName: langui.name },
|
{ meiliAttribute: "sortable_name:asc", displayedName: format("name") },
|
||||||
{ meiliAttribute: "sortable_date:asc", displayedName: langui.release_date },
|
{ meiliAttribute: "sortable_date:asc", displayedName: format("release_date") },
|
||||||
{ meiliAttribute: "sortable_price:asc", displayedName: langui.price },
|
{ meiliAttribute: "sortable_price:asc", displayedName: format("price") },
|
||||||
],
|
],
|
||||||
[langui.name, langui.price, langui.release_date]
|
[format]
|
||||||
);
|
);
|
||||||
|
|
||||||
const router = useTypedRouter(queryParamSchema);
|
const router = useTypedRouter(queryParamSchema);
|
||||||
|
@ -249,15 +248,15 @@ const Library = (props: Props): JSX.Element => {
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
icon="auto_stories"
|
icon="auto_stories"
|
||||||
title={langui.library}
|
title={format("library")}
|
||||||
description={langui.library_description}
|
description={format("library_description")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
className="mb-6 w-full"
|
className="mb-6 w-full"
|
||||||
placeholder={langui.search_title ?? "Search..."}
|
placeholder={format("search_title")}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(name) => {
|
onChange={(name) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -270,10 +269,10 @@ const Library = (props: Props): JSX.Element => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WithLabel label={langui.order_by}>
|
<WithLabel label={format("order_by")}>
|
||||||
<Select
|
<Select
|
||||||
className="w-full"
|
className="w-full"
|
||||||
options={sortingMethods.map((item) => item.displayedName ?? "")}
|
options={sortingMethods.map((item) => item.displayedName)}
|
||||||
value={sortingMethod}
|
value={sortingMethod}
|
||||||
onChange={(newSort) => {
|
onChange={(newSort) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -286,7 +285,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.show_subitems}>
|
<WithLabel label={format("show_subitems")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={showSubitems}
|
value={showSubitems}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -297,7 +296,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.show_primary_items}>
|
<WithLabel label={format("show_primary_items")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={showPrimaryItems}
|
value={showPrimaryItems}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -308,7 +307,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
<WithLabel label={langui.show_secondary_items}>
|
<WithLabel label={format("show_secondary_items")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={showSecondaryItems}
|
value={showSecondaryItems}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -320,7 +319,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
</WithLabel>
|
</WithLabel>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel label={langui.always_show_info}>
|
<WithLabel label={format("always_show_info")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={keepInfoVisible}
|
value={keepInfoVisible}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -335,7 +334,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
buttonsProps={[
|
buttonsProps={[
|
||||||
{
|
{
|
||||||
tooltip: langui.only_display_items_i_want,
|
tooltip: format("only_display_items_i_want"),
|
||||||
icon: "favorite",
|
icon: "favorite",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -345,7 +344,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
active: filterUserStatus === LibraryItemUserStatus.Want,
|
active: filterUserStatus === LibraryItemUserStatus.Want,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tooltip: langui.only_display_items_i_have,
|
tooltip: format("only_display_items_i_have"),
|
||||||
icon: "back_hand",
|
icon: "back_hand",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -355,7 +354,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
active: filterUserStatus === LibraryItemUserStatus.Have,
|
active: filterUserStatus === LibraryItemUserStatus.Have,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tooltip: langui.only_display_unmarked_items,
|
tooltip: format("only_display_unmarked_items"),
|
||||||
icon: "nearby_off",
|
icon: "nearby_off",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -365,8 +364,8 @@ const Library = (props: Props): JSX.Element => {
|
||||||
active: filterUserStatus === LibraryItemUserStatus.None,
|
active: filterUserStatus === LibraryItemUserStatus.None,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tooltip: langui.only_display_unmarked_items,
|
tooltip: format("only_display_unmarked_items"),
|
||||||
text: langui.all,
|
text: format("all"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
setFilterUserStatus(undefined);
|
setFilterUserStatus(undefined);
|
||||||
|
@ -379,7 +378,7 @@ const Library = (props: Props): JSX.Element => {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
text={langui.reset_all_filters}
|
text={format("reset_all_filters")}
|
||||||
icon="settings_backup_restore"
|
icon="settings_backup_restore"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setQuery(DEFAULT_FILTERS_STATE.query);
|
setQuery(DEFAULT_FILTERS_STATE.query);
|
||||||
|
|
|
@ -4,8 +4,7 @@ import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import { SubPanel } from "components/Containers/SubPanel";
|
import { SubPanel } from "components/Containers/SubPanel";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -14,12 +13,16 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
interface Props extends AppLayoutRequired {}
|
interface Props extends AppLayoutRequired {}
|
||||||
const Merch = (props: Props): JSX.Element => {
|
const Merch = (props: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
subPanel={
|
subPanel={
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader icon="store" title={langui.merch} description={langui.merch_description} />
|
<PanelHeader
|
||||||
|
icon="store"
|
||||||
|
title={format("merch")}
|
||||||
|
description={format("merch_description")}
|
||||||
|
/>
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
}
|
}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { prettyTerminalBoxedTitle } from "helpers/terminal";
|
||||||
import { prettyMarkdown } from "helpers/description";
|
import { prettyMarkdown } from "helpers/description";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||||
|
@ -19,7 +20,7 @@ import { useAtomGetter } from "helpers/atoms";
|
||||||
interface Props extends PostStaticProps {}
|
interface Props extends PostStaticProps {}
|
||||||
|
|
||||||
const LibrarySlug = (props: Props): JSX.Element => {
|
const LibrarySlug = (props: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ const LibrarySlug = (props: Props): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<PostPage
|
<PostPage
|
||||||
returnHref="/news"
|
returnHref="/news"
|
||||||
returnTitle={langui.news}
|
returnTitle={format("news")}
|
||||||
displayCredits
|
displayCredits
|
||||||
displayThumbnailHeader
|
displayThumbnailHeader
|
||||||
displayToc
|
displayToc
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { MeiliIndices, MeiliPost } from "shared/meilisearch-graphql-typings/meil
|
||||||
import { useTypedRouter } from "hooks/useTypedRouter";
|
import { useTypedRouter } from "hooks/useTypedRouter";
|
||||||
import { prettySlug } from "helpers/formatters";
|
import { prettySlug } from "helpers/formatters";
|
||||||
import { Paginator } from "components/Containers/Paginator";
|
import { Paginator } from "components/Containers/Paginator";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -55,7 +56,7 @@ const queryParamSchema = z.object({
|
||||||
interface Props extends AppLayoutRequired {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const News = ({ ...otherProps }: Props): JSX.Element => {
|
const News = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const router = useTypedRouter(queryParamSchema);
|
const router = useTypedRouter(queryParamSchema);
|
||||||
|
|
||||||
|
@ -115,13 +116,17 @@ const News = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader icon="newspaper" title={langui.news} description={langui.news_description} />
|
<PanelHeader
|
||||||
|
icon="newspaper"
|
||||||
|
title={format("news")}
|
||||||
|
description={format("news_description")}
|
||||||
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
className="mb-6 w-full"
|
className="mb-6 w-full"
|
||||||
placeholder={langui.search_title ?? "Search..."}
|
placeholder={format("search_title")}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(name) => {
|
onChange={(name) => {
|
||||||
setQuery(name);
|
setQuery(name);
|
||||||
|
@ -134,7 +139,7 @@ const News = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel label={langui.always_show_info}>
|
<WithLabel label={format("always_show_info")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={keepInfoVisible}
|
value={keepInfoVisible}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -147,7 +152,7 @@ const News = ({ ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
text={langui.reset_all_filters}
|
text={format("reset_all_filters")}
|
||||||
icon="settings_backup_restore"
|
icon="settings_backup_restore"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setQuery(DEFAULT_FILTERS_STATE.query);
|
setQuery(DEFAULT_FILTERS_STATE.query);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { Terminal } from "components/Cli/Terminal";
|
||||||
import { prettyTerminalBoxedTitle, prettyTerminalUnderlinedTitle } from "helpers/terminal";
|
import { prettyTerminalBoxedTitle, prettyTerminalUnderlinedTitle } from "helpers/terminal";
|
||||||
import { atoms } from "contexts/atoms";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -35,7 +36,7 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||||
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||||
|
@ -51,7 +52,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton href={`/wiki`} title={langui.wiki} displayOnlyOn={"3ColumnsLayout"} />
|
<ReturnButton href={`/wiki`} title={format("wiki")} displayOnlyOn={"3ColumnsLayout"} />
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
<ContentPanel width={ContentPanelWidthSizes.Large}>
|
<ContentPanel width={ContentPanelWidthSizes.Large}>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
href={`/wiki`}
|
href={`/wiki`}
|
||||||
title={langui.wiki}
|
title={format("wiki")}
|
||||||
displayOnlyOn={"1ColumnLayout"}
|
displayOnlyOn={"1ColumnLayout"}
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
@ -98,7 +99,9 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
<div className="my-4 grid gap-4 p-4">
|
<div className="my-4 grid gap-4 p-4">
|
||||||
{page.categories?.data && page.categories.data.length > 0 && (
|
{page.categories?.data && page.categories.data.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<p className="font-headers text-xl font-bold">{langui.categories}</p>
|
<p className="font-headers text-xl font-bold">
|
||||||
|
{format("category", { count: page.categories.data.length })}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||||
{filterHasAttributes(page.categories.data, ["attributes"] as const).map(
|
{filterHasAttributes(page.categories.data, ["attributes"] as const).map(
|
||||||
|
@ -112,7 +115,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{page.tags?.data && page.tags.data.length > 0 && (
|
{page.tags?.data && page.tags.data.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<p className="font-headers text-xl font-bold">{langui.tags}</p>
|
<p className="font-headers text-xl font-bold">{format("tags")}</p>
|
||||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||||
{filterHasAttributes(page.tags.data, ["attributes"] as const).map((tag) => (
|
{filterHasAttributes(page.tags.data, ["attributes"] as const).map((tag) => (
|
||||||
<Chip
|
<Chip
|
||||||
|
@ -130,7 +133,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
{isDefinedAndNotEmpty(selectedTranslation.summary) && (
|
{isDefinedAndNotEmpty(selectedTranslation.summary) && (
|
||||||
<div className="mb-12">
|
<div className="mb-12">
|
||||||
<p className="font-headers text-lg font-bold">{langui.summary}</p>
|
<p className="font-headers text-lg font-bold">{format("summary")}</p>
|
||||||
<p>{selectedTranslation.summary}</p>
|
<p>{selectedTranslation.summary}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -188,13 +191,13 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
}`
|
}`
|
||||||
)}${
|
)}${
|
||||||
isDefinedAndNotEmpty(selectedTranslation?.summary)
|
isDefinedAndNotEmpty(selectedTranslation?.summary)
|
||||||
? `${prettyTerminalUnderlinedTitle(langui.summary)}${selectedTranslation?.summary}`
|
? `${prettyTerminalUnderlinedTitle(format("summary"))}${selectedTranslation?.summary}`
|
||||||
: ""
|
: ""
|
||||||
}${
|
}${
|
||||||
page.definitions && page.definitions.length > 0
|
page.definitions && page.definitions.length > 0
|
||||||
? `${filterHasAttributes(page.definitions, ["translations"] as const).map(
|
? `${filterHasAttributes(page.definitions, ["translations"] as const).map(
|
||||||
(definition, index) =>
|
(definition, index) =>
|
||||||
`${prettyTerminalUnderlinedTitle(`${langui.definition} ${index + 1}`)}${
|
`${prettyTerminalUnderlinedTitle(format("definition_x", { x: index + 1 }))}${
|
||||||
staticSmartLanguage({
|
staticSmartLanguage({
|
||||||
items: filterHasAttributes(definition.translations, [
|
items: filterHasAttributes(definition.translations, [
|
||||||
"language.data.attributes.code",
|
"language.data.attributes.code",
|
||||||
|
@ -244,7 +247,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
] as const).map(
|
] as const).map(
|
||||||
(tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug)
|
(tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug)
|
||||||
),
|
),
|
||||||
[langui.categories ?? "Categories"]: filterHasAttributes(
|
[langui.category ?? "Categories"]: filterHasAttributes(
|
||||||
page.wikiPages.data[0].attributes.categories?.data,
|
page.wikiPages.data[0].attributes.categories?.data,
|
||||||
["attributes"] as const
|
["attributes"] as const
|
||||||
).map((category) => category.attributes.short),
|
).map((category) => category.attributes.short),
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
} from "graphql/generated";
|
} from "graphql/generated";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { prettySlug } from "helpers/formatters";
|
import { prettySlug } from "helpers/formatters";
|
||||||
import { getStatusDescription } from "helpers/others";
|
|
||||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
|
@ -27,8 +26,7 @@ import { TranslatedNavOption } from "components/PanelComponents/NavOption";
|
||||||
import { useIntersectionList } from "hooks/useIntersectionList";
|
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { atoms } from "contexts/atoms";
|
import { useFormat } from "hooks/useFormat";
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -41,7 +39,7 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): JSX.Element => {
|
const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const ids = filterHasAttributes(chronologyEras, ["attributes"] as const).map(
|
const ids = filterHasAttributes(chronologyEras, ["attributes"] as const).map(
|
||||||
(era) => era.attributes.slug
|
(era) => era.attributes.slug
|
||||||
);
|
);
|
||||||
|
@ -50,7 +48,7 @@ const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props):
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton href="/wiki" title={langui.wiki} displayOnlyOn="3ColumnsLayout" />
|
<ReturnButton href="/wiki" title={format("wiki")} displayOnlyOn="3ColumnsLayout" />
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
|
@ -81,7 +79,7 @@ const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props):
|
||||||
<ContentPanel>
|
<ContentPanel>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
href="/wiki"
|
href="/wiki"
|
||||||
title={langui.wiki}
|
title={format("wiki")}
|
||||||
displayOnlyOn="1ColumnLayout"
|
displayOnlyOn="1ColumnLayout"
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
@ -302,7 +300,7 @@ interface ChronologyEventProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Element => {
|
export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Element => {
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format, formatStatusDescription } = useFormat();
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: event.translations ?? [],
|
items: event.translations ?? [],
|
||||||
languageExtractor: useCallback(
|
languageExtractor: useCallback(
|
||||||
|
@ -322,7 +320,7 @@ export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Elemen
|
||||||
{selectedTranslation.status !==
|
{selectedTranslation.status !==
|
||||||
Enum_Componenttranslationschronologyitem_Status.Done && (
|
Enum_Componenttranslationschronologyitem_Status.Done && (
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(selectedTranslation.status, langui)}
|
content={formatStatusDescription(selectedTranslation.status)}
|
||||||
maxWidth={"20rem"}>
|
maxWidth={"20rem"}>
|
||||||
<Chip text={selectedTranslation.status} />
|
<Chip text={selectedTranslation.status} />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
@ -334,7 +332,7 @@ export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Elemen
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Ico icon="warning" className="!text-sm" />
|
<Ico icon="warning" className="!text-sm" />
|
||||||
{langui.no_source_warning}
|
{format("no_source_warning")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
@ -354,7 +352,7 @@ export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Elemen
|
||||||
<p className="whitespace-pre-line">{selectedTranslation.description}</p>
|
<p className="whitespace-pre-line">{selectedTranslation.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedTranslation.note && <em>{`${langui.notes}: ${selectedTranslation.note}`}</em>}
|
{selectedTranslation.note && <em>{`${format("notes")}: ${selectedTranslation.note}`}</em>}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,12 +19,11 @@ import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { atoms } from "contexts/atoms";
|
|
||||||
import { useAtomGetter } from "helpers/atoms";
|
|
||||||
import { useTypedRouter } from "hooks/useTypedRouter";
|
import { useTypedRouter } from "hooks/useTypedRouter";
|
||||||
import { MeiliIndices, MeiliWikiPage } from "shared/meilisearch-graphql-typings/meiliTypes";
|
import { MeiliIndices, MeiliWikiPage } from "shared/meilisearch-graphql-typings/meiliTypes";
|
||||||
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
import { containsHighlight, CustomSearchResponse, meiliSearch } from "helpers/search";
|
||||||
import { Paginator } from "components/Containers/Paginator";
|
import { Paginator } from "components/Containers/Paginator";
|
||||||
|
import { useFormat } from "hooks/useFormat";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -51,7 +50,7 @@ interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const Wiki = (props: Props): JSX.Element => {
|
const Wiki = (props: Props): JSX.Element => {
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const langui = useAtomGetter(atoms.localData.langui);
|
const { format } = useFormat();
|
||||||
const router = useTypedRouter(queryParamSchema);
|
const router = useTypedRouter(queryParamSchema);
|
||||||
const [query, setQuery] = useState(router.query.query ?? DEFAULT_FILTERS_STATE.query);
|
const [query, setQuery] = useState(router.query.query ?? DEFAULT_FILTERS_STATE.query);
|
||||||
|
|
||||||
|
@ -103,15 +102,15 @@ const Wiki = (props: Props): JSX.Element => {
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<PanelHeader
|
<PanelHeader
|
||||||
icon="travel_explore"
|
icon="travel_explore"
|
||||||
title={langui.wiki}
|
title={format("wiki")}
|
||||||
description={langui.wiki_description}
|
description={format("wiki_description")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
className="mb-6 w-full"
|
className="mb-6 w-full"
|
||||||
placeholder={langui.search_title ?? "Search..."}
|
placeholder={format("search_title")}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(name) => {
|
onChange={(name) => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -125,7 +124,7 @@ const Wiki = (props: Props): JSX.Element => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel label={langui.always_show_info}>
|
<WithLabel label={format("always_show_info")}>
|
||||||
<Switch
|
<Switch
|
||||||
value={keepInfoVisible}
|
value={keepInfoVisible}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -138,7 +137,7 @@ const Wiki = (props: Props): JSX.Element => {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-8"
|
className="mt-8"
|
||||||
text={langui.reset_all_filters}
|
text={format("reset_all_filters")}
|
||||||
icon="settings_backup_restore"
|
icon="settings_backup_restore"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPage(1);
|
setPage(1);
|
||||||
|
@ -150,9 +149,9 @@ const Wiki = (props: Props): JSX.Element => {
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<p className="mb-4 font-headers text-xl font-bold">{langui.special_pages}</p>
|
<p className="mb-4 font-headers text-xl font-bold">{format("special_pages")}</p>
|
||||||
|
|
||||||
<NavOption title={langui.chronology} url="/wiki/chronology" border />
|
<NavOption title={format("chronology")} url="/wiki/chronology" border />
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue