diff --git a/README.md b/README.md
index 66c6045..6c605c1 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@
#### [Front](https://github.com/Accords-Library/accords-library.com) (this repository)
- Language: [TypeScript](https://www.typescriptlang.org/)
-- Framework: [Next.js 12](https://nextjs.org/) (React 18)
+- Framework: [Next.js 13](https://nextjs.org/) (React 18)
- Queries: [GraphQL Code Generator](https://www.graphql-code-generator.com/)
- Fetch the GraphQL schema from the GraphQL back-end endpoint
- Read the operations and fragments stored as graphql files in the `src/graphql` folder
diff --git a/next.config.js b/next.config.js
index 50cd568..2f7d648 100644
--- a/next.config.js
+++ b/next.config.js
@@ -6,7 +6,6 @@ const locales = ["en", "es", "fr", "pt-br", "ja"];
/* @type {import('next').NextConfig} */
module.exports = {
- swcMinify: true,
reactStrictMode: true,
poweredByHeader: false,
i18n: {
diff --git a/public/local-data/currencies.json b/public/local-data/currencies.json
index e9a8ad7..6395145 100644
--- a/public/local-data/currencies.json
+++ b/public/local-data/currencies.json
@@ -1 +1,91 @@
-{"currencies":{"data":[{"id":"1","attributes":{"code":"EUR","symbol":"€","rate_to_usd":1.036166,"display_decimals":true}},{"id":"2","attributes":{"code":"CAD","symbol":"$","rate_to_usd":0.79319156,"display_decimals":true}},{"id":"3","attributes":{"code":"USD","symbol":"$","rate_to_usd":1,"display_decimals":true}},{"id":"4","attributes":{"code":"JPY","symbol":"¥","rate_to_usd":0.0083864261,"display_decimals":false}},{"id":"5","attributes":{"code":"BRL","symbol":"R$","rate_to_usd":0.19904328,"display_decimals":true}},{"id":"6","attributes":{"code":"GBP","symbol":"£","rate_to_usd":1.3181323,"display_decimals":true}},{"id":"7","attributes":{"code":"AUD","symbol":"$","rate_to_usd":0.7422,"display_decimals":true}},{"id":"8","attributes":{"code":"INR","symbol":"₹","rate_to_usd":0.013162881,"display_decimals":false}},{"id":"9","attributes":{"code":"NZD","symbol":"$","rate_to_usd":0.69089984,"display_decimals":true}},{"id":"10","attributes":{"code":"CHF","symbol":"CHF","rate_to_usd":1.0728706,"display_decimals":true}}]}}
\ No newline at end of file
+{
+ "currencies": {
+ "data": [
+ {
+ "id": "1",
+ "attributes": {
+ "code": "EUR",
+ "symbol": "€",
+ "rate_to_usd": 1.036166,
+ "display_decimals": true
+ }
+ },
+ {
+ "id": "2",
+ "attributes": {
+ "code": "CAD",
+ "symbol": "$",
+ "rate_to_usd": 0.79319156,
+ "display_decimals": true
+ }
+ },
+ {
+ "id": "3",
+ "attributes": { "code": "USD", "symbol": "$", "rate_to_usd": 1, "display_decimals": true }
+ },
+ {
+ "id": "4",
+ "attributes": {
+ "code": "JPY",
+ "symbol": "¥",
+ "rate_to_usd": 0.0083864261,
+ "display_decimals": false
+ }
+ },
+ {
+ "id": "5",
+ "attributes": {
+ "code": "BRL",
+ "symbol": "R$",
+ "rate_to_usd": 0.19904328,
+ "display_decimals": true
+ }
+ },
+ {
+ "id": "6",
+ "attributes": {
+ "code": "GBP",
+ "symbol": "£",
+ "rate_to_usd": 1.3181323,
+ "display_decimals": true
+ }
+ },
+ {
+ "id": "7",
+ "attributes": {
+ "code": "AUD",
+ "symbol": "$",
+ "rate_to_usd": 0.7422,
+ "display_decimals": true
+ }
+ },
+ {
+ "id": "8",
+ "attributes": {
+ "code": "INR",
+ "symbol": "₹",
+ "rate_to_usd": 0.013162881,
+ "display_decimals": false
+ }
+ },
+ {
+ "id": "9",
+ "attributes": {
+ "code": "NZD",
+ "symbol": "$",
+ "rate_to_usd": 0.69089984,
+ "display_decimals": true
+ }
+ },
+ {
+ "id": "10",
+ "attributes": {
+ "code": "CHF",
+ "symbol": "CHF",
+ "rate_to_usd": 1.0728706,
+ "display_decimals": true
+ }
+ }
+ ]
+ }
+}
diff --git a/public/local-data/languages.json b/public/local-data/languages.json
index a4f33d1..cb3a003 100644
--- a/public/local-data/languages.json
+++ b/public/local-data/languages.json
@@ -1 +1,36 @@
-{"languages":{"data":[{"id":"1","attributes":{"name":"French","code":"fr","localized_name":"Français"}},{"id":"2","attributes":{"name":"English","code":"en","localized_name":"English"}},{"id":"3","attributes":{"name":"Japanese","code":"ja","localized_name":"日本語"}},{"id":"4","attributes":{"name":"Spanish","code":"es","localized_name":"Español"}},{"id":"6","attributes":{"name":"Portuguese (Brazil)","code":"pt-br","localized_name":"Português (Brasil)"}},{"id":"8","attributes":{"name":"German","code":"de","localized_name":"Deutsch"}},{"id":"9","attributes":{"name":"Italian","code":"it","localized_name":"Italiano"}},{"id":"10","attributes":{"name":"Russian","code":"ru","localized_name":"русский"}},{"id":"11","attributes":{"name":"Korean","code":"ko","localized_name":"한국어"}},{"id":"12","attributes":{"name":"Chinese (Traditional)","code":"zh-cht","localized_name":"中文(繁體)"}}]}}
\ No newline at end of file
+{
+ "languages": {
+ "data": [
+ { "id": "1", "attributes": { "name": "French", "code": "fr", "localized_name": "Français" } },
+ { "id": "2", "attributes": { "name": "English", "code": "en", "localized_name": "English" } },
+ { "id": "3", "attributes": { "name": "Japanese", "code": "ja", "localized_name": "日本語" } },
+ { "id": "4", "attributes": { "name": "Spanish", "code": "es", "localized_name": "Español" } },
+ {
+ "id": "6",
+ "attributes": {
+ "name": "Portuguese (Brazil)",
+ "code": "pt-br",
+ "localized_name": "Português (Brasil)"
+ }
+ },
+ { "id": "8", "attributes": { "name": "German", "code": "de", "localized_name": "Deutsch" } },
+ {
+ "id": "9",
+ "attributes": { "name": "Italian", "code": "it", "localized_name": "Italiano" }
+ },
+ {
+ "id": "10",
+ "attributes": { "name": "Russian", "code": "ru", "localized_name": "русский" }
+ },
+ { "id": "11", "attributes": { "name": "Korean", "code": "ko", "localized_name": "한국어" } },
+ {
+ "id": "12",
+ "attributes": {
+ "name": "Chinese (Traditional)",
+ "code": "zh-cht",
+ "localized_name": "中文(繁體)"
+ }
+ }
+ ]
+ }
+}
diff --git a/public/local-data/websiteInterfaces.json b/public/local-data/websiteInterfaces.json
index 2b4df32..2774ab3 100644
--- a/public/local-data/websiteInterfaces.json
+++ b/public/local-data/websiteInterfaces.json
@@ -1 +1,941 @@
-{"websiteInterfaces":{"data":[{"attributes":{"ui_language":{"data":{"attributes":{"code":"en"}}},"library":"Library","contents":"Contents","wiki":"Wiki","chronicles":"Chronicles","library_short_description":"Browse all physical and digital media","contents_short_description":"Explore all content and filter by type or category","wiki_short_description":"An encyclopedia for everything related to DrakeNieR","chronicles_short_description":"Experience all events and content in chronological order","news":"News","merch":"Merch","gallery":"Gallery","archives":"Archives","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.","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.","type":"Type","category":"Category","categories":"Categories","size":"Size","release_date":"Release date","release_year":"Release year","details":"Details","price":"Price","width":"Width","height":"Height","thickness":"Thickness","subitem":"Subitem","subitems":"Subitems","subitem_of":"Subitem of","variant":"Variant","variants":"Variants","variant_of":"Variant of","summary":"Summary","audio":"Audio","video":"Video","textual":"Textual","game":"Game","other":"Other","return_to":"Return to","left_to_right":"Left to right","right_to_left":"Right to left","page":"Page","pages":"Pages","page_order":"Page order","binding":"Binding","type_information":"Type information","front_matter":"Front matter","back_matter":"Back matter","open_content":"Open content","read_content":"Read content","watch_content":"Watch content","listen_content":"Listen to content","view_scans":"View scans","paperback":"Paperback","hardcover":"Hardcover","languages":"Languages","select_language":"Select a language","language":"Language","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!","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...","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.","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","default_description":"Accord's Library aims at gathering and archiving all of Yoko Taro’s work. Yoko Taro is a Japanese video game director and scenario writer.","name":"Name","show_subitems":"Show subitems","show_primary_items":"Show primary items","show_secondary_items":"Show secondary items","no_type":"No type","no_year":"No year","order_by":"Order by","group_by":"Group by","select_option_sidebar":"Select one of the options in the sidebar","group":"Group","settings":"Settings","theme":"Theme","light":"Light","auto":"Auto","dark":"Dark","font_size":"Font size","player_name":"Player name","currency":"Currency","font":"Font","calculated":"Calculated","status_incomplete":"This entry is only partially translated/transcribed.","status_draft":"This entry is just a draft. It usually means that this is a work-in-progress. Translation/transcription might be poor and/or computer-generated.","status_review":"This entry has not yet being proofread. The content should still be accurate.","status_done":"This entry has been checked and proofread. If you notice any translation errors or typos, please contact us so we can fix it!","incomplete":"Incomplete","draft":"Draft","review":"Review","done":"Done","status":"Status","transcribers":"Transcribers","translators":"Translators","proofreaders":"Proofreaders","transcript_notice":"This content is a transcript","translation_notice":"This content is a fan-translation","source_language":"Source language","pronouns":"Pronouns","no_category":"No category","item":"Item","items":"Items","content":"Content","result":"Result","results":"Results","language_switch_message":"","open_settings":"Open settings","change_language":"Change language","open_search":"Open search","chronology":"Chronology","accords_handbook":"Accord's Handbook","legality":"Legality","members":"Members","sharing_policy":"Sharing Policy","contact_us":"Contact us","email":"Email","email_gdpr_notice":"We only use your email in order to contact you in regard to your request. We do not share this email with anyone nor use it for any other purpose.","message":"Message","send":"Send","response_invalid_code":"Verification code is incorrect.","response_invalid_email":"Please enter a valid email address!","response_email_success":"Thank you for contacting us! We will be in touch with you shortly.","always_show_info":"Always show info","item_not_available":"This item is not for sale or is no longer available","primary_language":"Primary language","secondary_language":"Secondary languages","combine_related_contents":"Combine related contents","previous_content":"Previous content","followup_content":"Follow-up content","videos":"Videos","view_on":"View on","channel":"Channel","subscribers":"Subscribers","description":"Description","available_at":"Available at","search_title":"Search title...","want_it":"I want it!","have_it":"I have it!","source":"Source","reset_all_filters":"Reset all filters","only_display_items_i_have":"Only display items marked as “I have”","only_display_items_i_want":"Only display items marked as “I want”","only_display_unmarked_items":"Only display unmarked items","display_all_items":"Display all items","table_of_contents":"Table of Contents","definition":"Definition","no_results_message":"No results. You can try changing or resetting the search parameters.","all":"All","special_pages":"Special Pages","scan":"Scan","scanlation":"Scanlation","scanners":"Scanners","cleaners":"Cleaners","typesetters":"Typesetters","notes":"Notes","cover":"Cover","tags":"Tags","no_source_warning":"No source!","copy_anchor_link":"Click to copy the archor link","anchor_link_copied":"Copied! 👍","folders":"Folders","empty_folder_message":"This folder is empty","switch_to_grid_view":"Switch to grid view","switch_to_folder_view":"Switch to folder view","content_is_not_available":"This content is not available","paper_texture":"Paper texture","book_fold":"Book fold","lighting":"Lighting","side_pages":"Side pages","shadow":"Shadow","night_reader":"Night reader","single_page_view":"Single page view","double_page_view":"Double page view","reset_all_options":"Reset all options","reading_layout":"Reading layout","quality":"Quality"}},{"attributes":{"ui_language":{"data":{"attributes":{"code":"fr"}}},"library":"Bibliothèque","contents":"Contenus","wiki":"Wiki","chronicles":"Chroniques","library_short_description":"Explorer l'ensemble des médias physique ou numérique","contents_short_description":"Explorer tout les contenus et filtrer par type ou par catégorie","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","news":"News","merch":"Merch","gallery":"Galerie","archives":"Archives","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.","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":"","type":"Type","category":"Catégorie","categories":"Catégories","size":"Dimension","release_date":"Date de sortie","release_year":"Année de sortie","details":"Détails","price":"Prix","width":"Largeur","height":"Hauteur","thickness":"Épaisseur","subitem":"Sous-item","subitems":"Sous-items","subitem_of":"Sous-item de","variant":"Variante","variants":"Variantes","variant_of":"Variante de","summary":"Résumé","audio":"Audio","video":"Vidéo","textual":"Textuel","game":"Jeux","other":"Autre","return_to":"Retourner à ","left_to_right":"Gauche à droite","right_to_left":"Droite à gauche","page":"Page","pages":"Pages","page_order":"Order des pages","binding":"Brochure","type_information":"Information de type","front_matter":"Avant propos","back_matter":"Après propos","open_content":"Parcourir le contenu","read_content":"Lire le contenu","watch_content":"Regarder le contenu","listen_content":"Écouter le contenu","view_scans":"Voir les scans","paperback":"Broché","hardcover":"Relié","languages":"Langues","select_language":"Séléctionner la langue","language":"Langue","library_description":"","wiki_description":"","chronicles_description":"","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","gallery_description":"","archives_description":"","about_us_description":"","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.","name":"Nom","show_subitems":"Afficher les sous-items","show_primary_items":"Afficher les items primaires","show_secondary_items":"Afficher les items secondaires","no_type":"Pas de type","no_year":"Pas d'année","order_by":"Ordonné par","group_by":"Groupé par","select_option_sidebar":"Sélectionner l'une des options de la barre latérale","group":"Groupe","settings":"Paramètres","theme":"Thème","light":"Clair","auto":"Auto","dark":"Sombre","font_size":"Taille de la police","player_name":"Nom du joueur","currency":"Devise","font":"Police d'écriture","calculated":"calculé","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_review":"Cet entrée n'a pas encore été relue. Le contenu devrait néanmoins être correct.","status_done":"Cet entrée a été vérifiée et corrigée. Si vous remarquez des erreurs de traduction ou des fautes de frappe, veuillez nous contacter afin que nous puissions les corriger !","incomplete":"Incomplet","draft":"Ébauche","review":"Pour vérification","done":"Terminé","status":"Statut","transcribers":"Transcripteurs","translators":"Traducteurs","proofreaders":"Correcteurs","transcript_notice":"Ceci est une transcription","translation_notice":"Ceci est une traduction","source_language":"Langue source","pronouns":"Pronoms","no_category":"Pas de categorie","item":"Item","items":"Items","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","change_language":"Changer de langue","open_search":"Ouvrir le menu de recherche","chronology":"Chronologie","accords_handbook":"Le manuel de Accord","legality":"Légalité","members":"Membres","sharing_policy":"Politique de partage","contact_us":"Nous contacter","email":"Email","email_gdpr_notice":"Nous utilisons votre adresse électronique uniquement pour vous contacter au sujet de votre demande. Nous ne partageons cette adresse avec personne et ne l'utilisons pas à d'autres fins.","message":"Message","send":"Envoyer","response_invalid_code":"Le code de vérification est incorrect.","response_invalid_email":"Veuillez saisir une adresse électronique valide !","response_email_success":"Merci de nous avoir contactés ! Nous prendrons contact avec vous sous peu.","always_show_info":"Toujours montrer les informations","item_not_available":"Cet article n'est pas à vendre ou n'est plus disponible.","primary_language":"Langue principale","secondary_language":"Langues secondaires","combine_related_contents":"Combiner les contenus connexes","previous_content":"Contenu précédent","followup_content":"Contenu suivant","videos":"Videos","view_on":"Voir sur","channel":"Chaîne","subscribers":"Abonnés","description":"Description","available_at":"Disponible sur","search_title":"Rechercher un titre...","want_it":"Je le veux !","have_it":"Je l'ai !","source":"Source","reset_all_filters":"Réinitialiser les filtres","only_display_items_i_have":"Seulement afficher les items marqués avec \"je le veux\"","only_display_items_i_want":"Seulement afficher les items marqués avec \"je l'ai\"","only_display_unmarked_items":"Seulement afficher les items non-marqués","display_all_items":"Afficher tous les items","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.","all":"Tous","special_pages":"Pages spéciales","scan":"Scan","scanlation":"Scantrad","scanners":"Scanneurs","cleaners":"Nettoyeurs","typesetters":"Lettreurs","notes":"Notes","cover":"Couverture","tags":"Tags","no_source_warning":"Pas de source !","copy_anchor_link":"Cliquez pour copier le permalien","anchor_link_copied":"Copié ! 👍","folders":"Dossiers","empty_folder_message":"Ce dossier est vide","switch_to_grid_view":"Vue en grille","switch_to_folder_view":"Vue par dossier","content_is_not_available":"Ce contenu n'est pas disponible","paper_texture":"Texture de papier","book_fold":"Pliure du livre","lighting":"Effet de lumière","side_pages":"Tranche du livre","shadow":"Ombre portée","night_reader":"Mode nuit","single_page_view":"Vue 1 page","double_page_view":"Vue 2 pages","reset_all_options":"Réinitialiser les options","reading_layout":"Mode de lecture","quality":"Qualité"}},{"attributes":{"ui_language":{"data":{"attributes":{"code":"ja"}}},"library":"ライブラリー","contents":"コンテンツ","wiki":"ウィキ","chronicles":"クロニクル","library_short_description":"すべての物理メディアとデジタルメディアを見る","contents_short_description":"すべてのコンテンツを検索し、種類やカテゴリーで絞り込むことができます。","wiki_short_description":"ゲーム宇宙に関連するすべての百科事典です。","chronicles_short_description":"すべてのイベントとコンテンツを時系列で体験できる","news":"ニュース","merch":"マーチ","gallery":"ギャラリー","archives":"アーカイブス","about_us":"会社概要","licensing_notice":"このウェブサイトのコンテンツは、特に断りのない限り [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) で提供されています。","copyright_notice":"Accord's Libraryは、株式会社スクウェア・エニックスと提携、または推奨しているものではありません。株式会社スクウェア・エニックスの登録商標です。すべてのゲーム資産およびプロモーション素材は、© SQUARE ENIX CO. LTD.に帰属します。","contents_description":"図書館や他のオンラインソースのすべてのコンテンツ(テキスト、オーディオ、ビデオ)。","type":"タイプ","category":"カテゴリー","categories":"カテゴリー","size":"サイズ","release_date":"発売日","release_year":"発売年","details":"詳細","price":"価格","width":"幅","height":"高さ","thickness":"厚み","subitem":"サブアイテム","subitems":"サブアイテム","subitem_of":"のサブアイテム","variant":"バリアント","variants":"バリアント","variant_of":"のバリアント","summary":"概要","audio":"オーディオ","video":"ビデオ","textual":"テキスト","game":"ゲーム","other":"他","return_to":"戻る","left_to_right":"左から右へ","right_to_left":"右から左へ","page":"ページ","pages":"ページ","page_order":"ページ順序","binding":"製本","type_information":"タイプ情報","front_matter":"フロントマター","back_matter":"バックナンバー","open_content":"コンテンツを開放","read_content":"コンテンツを読む","watch_content":"コンテンツを見る","listen_content":"コンテンツを聴く","view_scans":"スキャンを開放","paperback":"ペーパーバック","hardcover":"ハードカバー","languages":"言語","select_language":"言語を選択する","language":"言語","library_description":"ヨコベースの副教材(書籍、小説、画集、舞台劇、漫画、ドラマCD、コミック)を網羅したリストです。それぞれについて、写真、スキャン、内容の書き起こし、どんなものなのか、いつ、どのように発売されたのか、サイズ、初回価格...などの情報を掲載しています。","wiki_description":"DrakeNieRに関連するすべての百科事典です。現在は年表のみですが、今後多くのページを公開予定です","chronicles_description":"Accord's Libraryは、ヨーコ・タローの全作品を収集・保存することを目的としています。ヨーコ・タローは、日本のゲームディレクター、シナリオライターです。","news_description":"レコーダーが書いたニュース記事です ここでは、新しい商品/アイテムのリリースに関するお知らせ、ガイド、セオリー、アンボックス、ショーケース...をご紹介しています。","merch_description":"","gallery_description":"","archives_description":"","about_us_description":"Accord's Libraryプロジェクトについては、以下のページで詳しくご紹介しています。","page_not_found":"ページが見つかりません","default_description":"Accord's Libraryは、ヨーコ・タローの全作品を収集・保存することを目的としています。ヨーコ・タローは、日本のゲームディレクター、シナリオライターです。","name":"名称","show_subitems":"サブアイテムをみせる","show_primary_items":"一次のイテムをみせる","show_secondary_items":"二次のイテムをみせる","no_type":"タイプなし","no_year":"年なし","order_by":"注文する","group_by":"グループ化する","select_option_sidebar":"サイドバーのオプションを選択します","group":"グループ","settings":"設定","theme":"テーマ","light":"光","auto":"オート","dark":"暗","font_size":"文字サイズ","player_name":"プレイヤー名","currency":"通貨","font":"文字","calculated":"計算された","status_incomplete":"このエントリーは一部のみ翻訳/転記されています。","status_draft":"このエントリーはあくまで下書きです。通常、これは作業中であることを意味します。翻訳/転写は稚拙であったり、コンピュータで作成されたものであったりするかもしれません。","status_review":"このエントリーはまだ校正されていません。内容はまだ正確であるはずです。","status_done":"このエントリーは、チェックと校正を行いました。もし、翻訳ミスや誤字脱字にお気づきの際は、修正いたしますので、ご連絡ください","incomplete":"未完成","draft":"ドラフト","review":"レビュー","done":"完了","status":"状況","transcribers":"トランスクライバー","translators":"翻訳者","proofreaders":"校正者","transcript_notice":"このコンテンツは転写です","translation_notice":"このコンテンツはファンによる翻訳です","source_language":"ソース言語","pronouns":"代名詞","no_category":"カテゴリーなし","item":"項目","items":"項目","content":"コンテンツ","result":"結果","results":"結果","language_switch_message":null,"open_settings":"オープン設定","change_language":"言語を変更する","open_search":"オープンサーチ","chronology":"年表","accords_handbook":"アコードの手引き","legality":"合法性","members":"メンバー紹介","sharing_policy":"共有ポリシー","contact_us":"お問い合わせ","email":"電子メール","email_gdpr_notice":"お客様の電子メールは、お客様のご要望に関してご連絡するためにのみ使用します。この電子メールを誰かと共有したり、他の目的で使用することはありません。","message":"メッセージ","send":"送信","response_invalid_code":"検証コードが正しくありません。","response_invalid_email":"有効なEメールアドレスを入力してください","response_email_success":"お問い合わせありがとうございます。折り返しご連絡させていただきます。","always_show_info":"常に情報を表示する","item_not_available":"この商品は非売品です","primary_language":"主要言語","secondary_language":"二次言語","combine_related_contents":"関連するコンテンツを組み合わせる","previous_content":"前のコンテンツ","followup_content":"フォローアップコンテンツ","videos":"動画","view_on":"見る","channel":"チャンネル","subscribers":"サブスクライバー","description":"説明","available_at":"でご覧いただけます。","search_title":"検索タイトル...","want_it":"欲しいです!","have_it":"持ってます!","source":"出典","reset_all_filters":"すべてのフィルタをリセットする","only_display_items_i_have":"\"持ってる \"と表示されているもののみ表示","only_display_items_i_want":"\"欲しい \"とマークされたものだけを表示する","only_display_unmarked_items":"無印のアイテムのみ表示","display_all_items":"すべての項目を表示する","table_of_contents":"目次","definition":"定義","no_results_message":"結果が出ません。検索条件を変更またはリセットしてみてください。","all":"すべて","special_pages":"特設ページ","scan":null,"scanlation":null,"scanners":null,"cleaners":null,"typesetters":null,"notes":null,"cover":null,"tags":null,"no_source_warning":null,"copy_anchor_link":null,"anchor_link_copied":null,"folders":null,"empty_folder_message":null,"switch_to_grid_view":null,"switch_to_folder_view":null,"content_is_not_available":null,"paper_texture":null,"book_fold":null,"lighting":null,"side_pages":null,"shadow":null,"night_reader":null,"single_page_view":null,"double_page_view":null,"reset_all_options":null,"reading_layout":null,"quality":null}},{"attributes":{"ui_language":{"data":{"attributes":{"code":"es"}}},"library":"Librería","contents":"Contenidos","wiki":"Wiki","chronicles":"Crónicas","library_short_description":"Explora todos los medios físicos y digitales","contents_short_description":"Explora todo el contenido y filtra por tipo o categoría","wiki_short_description":"Una enciclopedia para todo lo relacionado con DrakeNieR","chronicles_short_description":"Experimenta todos los eventos y contenidos en orden cronológico","news":"Novedades","merch":"Merch","gallery":"Galería","archives":"Archivos","about_us":"Sobre nosotros","licensing_notice":"El contenido de este sitio web está disponible bajo [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) a menos que se indique lo contrario.","copyright_notice":"Accord's Library no está afiliada ni respaldada por SQUARE ENIX CO. LTD. Todos los archivos de los juegos y material promocional pertenecen a © SQUARE ENIX CO. LTD.","contents_description":"Todo el contenido (textual, audio y video) de la Biblioteca u otras fuentes en línea.","type":"Tipo","category":"Categoría","categories":"Categorías","size":"Tamaño","release_date":"Fecha de lanzamiento","release_year":"Año de lanzamiento","details":"Detalles","price":"Precio","width":"Ancho","height":"Altura","thickness":"Grosor","subitem":"Sub-item","subitems":"Sub-items","subitem_of":"Sub-item de","variant":"Variante","variants":"Variantes","variant_of":"Variante de","summary":"Sumario","audio":"Audio","video":"Video","textual":"Textual","game":"Juego","other":"Otros","return_to":"Volver a","left_to_right":"Izquierda a derecha","right_to_left":"Derecha a izquierda","page":"Página","pages":"Páginas","page_order":"Orden de las páginas","binding":"Encuadernación","type_information":"Tipo de información","front_matter":"Anteportada","back_matter":"Portada anterior","open_content":"Abrir contenido","read_content":"Leer contenido","watch_content":"Ver contenido","listen_content":"Escuchar contenido","view_scans":"Ver escaneos","paperback":"Tapa blanda","hardcover":"Tapa dura","languages":"Idiomas","select_language":"Seleccionar 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...","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":"","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":"","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","default_description":"Accord's Library tiene como objetivo recopilar y archivar todo el trabajo de Yoko Taro. Yoko Taro es un director de videojuegos y escritor de escenarios japonés.","name":"Nombre","show_subitems":"Mostrar sub-items","show_primary_items":"Mostrar items principales","show_secondary_items":"Mostrar items secundarios","no_type":"Ningún tipo","no_year":"Ningún año","order_by":"Ordenar por","group_by":"Agrupar por","select_option_sidebar":"Selecciona una de las opciones en la barra lateral","group":"Grupo","settings":"Ajustes","theme":"Tema","light":"Claro","auto":"Auto","dark":"Oscuro","font_size":"Tamaño de la fuente","player_name":"Nombre del jugador/a","currency":"Divisa","font":"Fuente","calculated":"Calculada","status_incomplete":"Esta entrada está solo parcialmente traducida/transcrita.","status_draft":"Esta entrada es solo un borrador. Por lo general, significa que se trata de un trabajo en curso. La traducción/transcripción puede ser deficiente y/o generada por ordenador.","status_review":"Esta entrada aún no ha sido corregida. No obstante, el contenido debería ser preciso.","status_done":"Esta entrada ha sido revisada y corregida. Si notas algún error de traducción o error tipográfico, contáctanos para que podamos solucionarlo!","incomplete":"Incompleto","draft":"Borrador","review":"Revisado","done":"Completado","status":"Estado","transcribers":"Transcriptores/as","translators":"Traductores/as","proofreaders":"Correctores/as","transcript_notice":"Este contenido es una transcripción","translation_notice":"Este contenido es una traducción de fans","source_language":"Idioma original","pronouns":"Pronombres","no_category":"Ningún categoría","item":null,"items":null,"content":null,"result":null,"results":null,"language_switch_message":null,"open_settings":null,"change_language":null,"open_search":null,"chronology":null,"accords_handbook":null,"legality":null,"members":null,"sharing_policy":null,"contact_us":null,"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.","message":null,"send":null,"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_email_success":"¡Gracias por contactarnos! Nos pondremos en contacto contigo en breve.","always_show_info":null,"item_not_available":null,"primary_language":null,"secondary_language":null,"combine_related_contents":null,"previous_content":null,"followup_content":null,"videos":null,"view_on":null,"channel":null,"subscribers":null,"description":null,"available_at":null,"search_title":null,"want_it":null,"have_it":null,"source":null,"reset_all_filters":null,"only_display_items_i_have":null,"only_display_items_i_want":null,"only_display_unmarked_items":null,"display_all_items":null,"table_of_contents":null,"definition":null,"no_results_message":null,"all":null,"special_pages":null,"scan":null,"scanlation":null,"scanners":null,"cleaners":null,"typesetters":null,"notes":null,"cover":null,"tags":null,"no_source_warning":null,"copy_anchor_link":null,"anchor_link_copied":null,"folders":null,"empty_folder_message":null,"switch_to_grid_view":null,"switch_to_folder_view":null,"content_is_not_available":null,"paper_texture":null,"book_fold":null,"lighting":null,"side_pages":null,"shadow":null,"night_reader":null,"single_page_view":null,"double_page_view":null,"reset_all_options":null,"reading_layout":null,"quality":null}},{"attributes":{"ui_language":{"data":{"attributes":{"code":"pt-br"}}},"library":"Coleção","contents":"Conteúdos","wiki":"Wiki","chronicles":"Crônicas","library_short_description":"Procure por todas mídias digitais e físicas","contents_short_description":"Explore todo o conteúdo e filtre por categorias e tipos","wiki_short_description":"Uma enciclopédia com tudo relacionado a DrakeNieR","chronicles_short_description":"Explore as crônicas de DrakeNieR em ordem cronológica.","news":"Notícias","merch":"Mercadorias","gallery":"Galeria","archives":"Arquivos","about_us":"Sobre Nós","licensing_notice":"O conteúdo nesse site está disponível pela CC-BY-SA, a não ser que esteja anotado.","copyright_notice":"Accord's Library não é afiliada ou reconhecida pela SQUARE ENIX CO. LTD. Todos assets de jogos e materiais promocionais pertencem a © SQUARE ENIX CO. LTD.\n\n","contents_description":"","type":"Tipo","category":"Categoria","categories":"Categorias","size":"Tamanho","release_date":"Dia de lançamento","release_year":"Ano de lançamento","details":"Detalhes","price":"Preço","width":"Largura","height":"Altura","thickness":"Grossura","subitem":"Subitem","subitems":"Subitens","subitem_of":"Subitem de","variant":"Variante","variants":"Variantes","variant_of":"Variante de","summary":"Sumário","audio":"Audio","video":"Video","textual":"Textos","game":"Jogos","other":"Outros","return_to":"Voltar para","left_to_right":"Esquerda para direita","right_to_left":"Direita para esquerda","page":"Página","pages":"Páginas","page_order":"Ordem de páginas","binding":"Encadernação","type_information":"Informação do tipo","front_matter":"Pré textual","back_matter":"Pós textual","open_content":"Abrir conteúdo","read_content":"Ler o conteúdo","watch_content":"Assistir o conteúdo","listen_content":"Ouvir o conteúdo","view_scans":"Ver scans","paperback":"Brochura","hardcover":"Capa dura","languages":"Línguas","select_language":"Selecionar língua","language":"Língua","library_description":"","wiki_description":null,"chronicles_description":null,"news_description":"","merch_description":"","gallery_description":"","archives_description":"","about_us_description":"","page_not_found":"Página não encontrada","default_description":null,"name":"Nome","show_subitems":"Mostrar subitens","show_primary_items":"Mostrar itens primários","show_secondary_items":"Mostrar itens secundários","no_type":"Sem tipo","no_year":"Sem ano","order_by":"Ordenar por","group_by":"Agrupar por","select_option_sidebar":"Selecione uma opção na aba lateral","group":"Grupo","settings":"Configurações","theme":"Tema","light":"Claro","auto":"Automático","dark":"Escuro","font_size":"Tamanho da fonte","player_name":"Nome do jogador","currency":"Moeda","font":"Fonte","calculated":"Calculado","status_incomplete":"Este conteúdo está incompleto e não foi traduzido/transcrito completamente.","status_draft":"A tradução/transcrição selecionada é um Rascunho. Isso significa que a tradução pode estar fraca e/ou ter sido gerada por uma inteligência artificial.","status_review":"Este conteúdo ainda não foi Revisado, erros gramaticais podem ser encontrados uma vez que os revisores ainda não leram a tradução.","status_done":"O conteúdo foi completamente traduzido e revisado.","incomplete":"Incompleto","draft":"Rascunho","review":"Review","done":"Concluido","status":"Status","transcribers":"Transcritores","translators":"Tradutores","proofreaders":"Revisores","transcript_notice":"Este conteúdo foi transcrito.","translation_notice":"Este conteúdo é uma tradução de fã.","source_language":"Língua original","pronouns":"Pronomes","no_category":"Sem Categoria","item":"Item","items":"Itens","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","change_language":"Mudar língua","open_search":"Abrir pesquisa","chronology":"Cronologia","accords_handbook":"Livro de mão da Accord","legality":"Legalidade","members":"Membros","sharing_policy":"Política de compartilhamento","contact_us":"Fale conosco","email":null,"email_gdpr_notice":null,"message":null,"send":null,"response_invalid_code":null,"response_invalid_email":null,"response_email_success":null,"always_show_info":"Mostrar informações","item_not_available":"Item indisponível","primary_language":"Língua primaria","secondary_language":"Línguas secundárias","combine_related_contents":"Combinar relacionados","previous_content":"Conteúdo anterior:","followup_content":"Próximo conteúdo:","videos":"Videos:","view_on":"Ver no:","channel":"Canal","subscribers":"Inscritos","description":"Descrição","available_at":"Disponível no:","search_title":"Pesquisar","want_it":null,"have_it":null,"source":null,"reset_all_filters":null,"only_display_items_i_have":null,"only_display_items_i_want":null,"only_display_unmarked_items":null,"display_all_items":null,"table_of_contents":null,"definition":null,"no_results_message":null,"all":null,"special_pages":null,"scan":null,"scanlation":null,"scanners":null,"cleaners":null,"typesetters":null,"notes":null,"cover":null,"tags":null,"no_source_warning":null,"copy_anchor_link":null,"anchor_link_copied":null,"folders":null,"empty_folder_message":null,"switch_to_grid_view":null,"switch_to_folder_view":null,"content_is_not_available":null,"paper_texture":null,"book_fold":null,"lighting":null,"side_pages":null,"shadow":null,"night_reader":null,"single_page_view":null,"double_page_view":null,"reset_all_options":null,"reading_layout":null,"quality":null}}]}}
\ No newline at end of file
+{
+ "websiteInterfaces": {
+ "data": [
+ {
+ "attributes": {
+ "ui_language": { "data": { "attributes": { "code": "en" } } },
+ "library": "Library",
+ "contents": "Contents",
+ "wiki": "Wiki",
+ "chronicles": "Chronicles",
+ "library_short_description": "Browse all physical and digital media",
+ "contents_short_description": "Explore all content and filter by type or category",
+ "wiki_short_description": "An encyclopedia for everything related to DrakeNieR",
+ "chronicles_short_description": "Experience all events and content in chronological order",
+ "news": "News",
+ "merch": "Merch",
+ "gallery": "Gallery",
+ "archives": "Archives",
+ "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.",
+ "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.",
+ "type": "Type",
+ "category": "Category",
+ "categories": "Categories",
+ "size": "Size",
+ "release_date": "Release date",
+ "release_year": "Release year",
+ "details": "Details",
+ "price": "Price",
+ "width": "Width",
+ "height": "Height",
+ "thickness": "Thickness",
+ "subitem": "Subitem",
+ "subitems": "Subitems",
+ "subitem_of": "Subitem of",
+ "variant": "Variant",
+ "variants": "Variants",
+ "variant_of": "Variant of",
+ "summary": "Summary",
+ "audio": "Audio",
+ "video": "Video",
+ "textual": "Textual",
+ "game": "Game",
+ "other": "Other",
+ "return_to": "Return to",
+ "left_to_right": "Left to right",
+ "right_to_left": "Right to left",
+ "page": "Page",
+ "pages": "Pages",
+ "page_order": "Page order",
+ "binding": "Binding",
+ "type_information": "Type information",
+ "front_matter": "Front matter",
+ "back_matter": "Back matter",
+ "open_content": "Open content",
+ "read_content": "Read content",
+ "watch_content": "Watch content",
+ "listen_content": "Listen to content",
+ "view_scans": "View scans",
+ "paperback": "Paperback",
+ "hardcover": "Hardcover",
+ "languages": "Languages",
+ "select_language": "Select a language",
+ "language": "Language",
+ "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!",
+ "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...",
+ "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.",
+ "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",
+ "default_description": "Accord's Library aims at gathering and archiving all of Yoko Taro’s work. Yoko Taro is a Japanese video game director and scenario writer.",
+ "name": "Name",
+ "show_subitems": "Show subitems",
+ "show_primary_items": "Show primary items",
+ "show_secondary_items": "Show secondary items",
+ "no_type": "No type",
+ "no_year": "No year",
+ "order_by": "Order by",
+ "group_by": "Group by",
+ "select_option_sidebar": "Select one of the options in the sidebar",
+ "group": "Group",
+ "settings": "Settings",
+ "theme": "Theme",
+ "light": "Light",
+ "auto": "Auto",
+ "dark": "Dark",
+ "font_size": "Font size",
+ "player_name": "Player name",
+ "currency": "Currency",
+ "font": "Font",
+ "calculated": "Calculated",
+ "status_incomplete": "This entry is only partially translated/transcribed.",
+ "status_draft": "This entry is just a draft. It usually means that this is a work-in-progress. Translation/transcription might be poor and/or computer-generated.",
+ "status_review": "This entry has not yet being proofread. The content should still be accurate.",
+ "status_done": "This entry has been checked and proofread. If you notice any translation errors or typos, please contact us so we can fix it!",
+ "incomplete": "Incomplete",
+ "draft": "Draft",
+ "review": "Review",
+ "done": "Done",
+ "status": "Status",
+ "transcribers": "Transcribers",
+ "translators": "Translators",
+ "proofreaders": "Proofreaders",
+ "transcript_notice": "This content is a transcript",
+ "translation_notice": "This content is a fan-translation",
+ "source_language": "Source language",
+ "pronouns": "Pronouns",
+ "no_category": "No category",
+ "item": "Item",
+ "items": "Items",
+ "content": "Content",
+ "result": "Result",
+ "results": "Results",
+ "language_switch_message": "",
+ "open_settings": "Open settings",
+ "change_language": "Change language",
+ "open_search": "Open search",
+ "chronology": "Chronology",
+ "accords_handbook": "Accord's Handbook",
+ "legality": "Legality",
+ "members": "Members",
+ "sharing_policy": "Sharing Policy",
+ "contact_us": "Contact us",
+ "email": "Email",
+ "email_gdpr_notice": "We only use your email in order to contact you in regard to your request. We do not share this email with anyone nor use it for any other purpose.",
+ "message": "Message",
+ "send": "Send",
+ "response_invalid_code": "Verification code is incorrect.",
+ "response_invalid_email": "Please enter a valid email address!",
+ "response_email_success": "Thank you for contacting us! We will be in touch with you shortly.",
+ "always_show_info": "Always show info",
+ "item_not_available": "This item is not for sale or is no longer available",
+ "primary_language": "Primary language",
+ "secondary_language": "Secondary languages",
+ "combine_related_contents": "Combine related contents",
+ "previous_content": "Previous content",
+ "followup_content": "Follow-up content",
+ "videos": "Videos",
+ "view_on": "View on",
+ "channel": "Channel",
+ "subscribers": "Subscribers",
+ "description": "Description",
+ "available_at": "Available at",
+ "search_title": "Search title...",
+ "want_it": "I want it!",
+ "have_it": "I have it!",
+ "source": "Source",
+ "reset_all_filters": "Reset all filters",
+ "only_display_items_i_have": "Only display items marked as “I have”",
+ "only_display_items_i_want": "Only display items marked as “I want”",
+ "only_display_unmarked_items": "Only display unmarked items",
+ "display_all_items": "Display all items",
+ "table_of_contents": "Table of Contents",
+ "definition": "Definition",
+ "no_results_message": "No results. You can try changing or resetting the search parameters.",
+ "all": "All",
+ "special_pages": "Special Pages",
+ "scan": "Scan",
+ "scanlation": "Scanlation",
+ "scanners": "Scanners",
+ "cleaners": "Cleaners",
+ "typesetters": "Typesetters",
+ "notes": "Notes",
+ "cover": "Cover",
+ "tags": "Tags",
+ "no_source_warning": "No source!",
+ "copy_anchor_link": "Click to copy the archor link",
+ "anchor_link_copied": "Copied! 👍",
+ "folders": "Folders",
+ "empty_folder_message": "This folder is empty",
+ "switch_to_grid_view": "Switch to grid view",
+ "switch_to_folder_view": "Switch to folder view",
+ "content_is_not_available": "This content is not available",
+ "paper_texture": "Paper texture",
+ "book_fold": "Book fold",
+ "lighting": "Lighting",
+ "side_pages": "Side pages",
+ "shadow": "Shadow",
+ "night_reader": "Night reader",
+ "single_page_view": "Single page view",
+ "double_page_view": "Double page view",
+ "reset_all_options": "Reset all options",
+ "reading_layout": "Reading layout",
+ "quality": "Quality"
+ }
+ },
+ {
+ "attributes": {
+ "ui_language": { "data": { "attributes": { "code": "fr" } } },
+ "library": "Bibliothèque",
+ "contents": "Contenus",
+ "wiki": "Wiki",
+ "chronicles": "Chroniques",
+ "library_short_description": "Explorer l'ensemble des médias physique ou numérique",
+ "contents_short_description": "Explorer tout les contenus et filtrer par type ou par catégorie",
+ "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",
+ "news": "News",
+ "merch": "Merch",
+ "gallery": "Galerie",
+ "archives": "Archives",
+ "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.",
+ "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": "",
+ "type": "Type",
+ "category": "Catégorie",
+ "categories": "Catégories",
+ "size": "Dimension",
+ "release_date": "Date de sortie",
+ "release_year": "Année de sortie",
+ "details": "Détails",
+ "price": "Prix",
+ "width": "Largeur",
+ "height": "Hauteur",
+ "thickness": "Épaisseur",
+ "subitem": "Sous-item",
+ "subitems": "Sous-items",
+ "subitem_of": "Sous-item de",
+ "variant": "Variante",
+ "variants": "Variantes",
+ "variant_of": "Variante de",
+ "summary": "Résumé",
+ "audio": "Audio",
+ "video": "Vidéo",
+ "textual": "Textuel",
+ "game": "Jeux",
+ "other": "Autre",
+ "return_to": "Retourner à ",
+ "left_to_right": "Gauche à droite",
+ "right_to_left": "Droite à gauche",
+ "page": "Page",
+ "pages": "Pages",
+ "page_order": "Order des pages",
+ "binding": "Brochure",
+ "type_information": "Information de type",
+ "front_matter": "Avant propos",
+ "back_matter": "Après propos",
+ "open_content": "Parcourir le contenu",
+ "read_content": "Lire le contenu",
+ "watch_content": "Regarder le contenu",
+ "listen_content": "Écouter le contenu",
+ "view_scans": "Voir les scans",
+ "paperback": "Broché",
+ "hardcover": "Relié",
+ "languages": "Langues",
+ "select_language": "Séléctionner la langue",
+ "language": "Langue",
+ "library_description": "",
+ "wiki_description": "",
+ "chronicles_description": "",
+ "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",
+ "gallery_description": "",
+ "archives_description": "",
+ "about_us_description": "",
+ "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.",
+ "name": "Nom",
+ "show_subitems": "Afficher les sous-items",
+ "show_primary_items": "Afficher les items primaires",
+ "show_secondary_items": "Afficher les items secondaires",
+ "no_type": "Pas de type",
+ "no_year": "Pas d'année",
+ "order_by": "Ordonné par",
+ "group_by": "Groupé par",
+ "select_option_sidebar": "Sélectionner l'une des options de la barre latérale",
+ "group": "Groupe",
+ "settings": "Paramètres",
+ "theme": "Thème",
+ "light": "Clair",
+ "auto": "Auto",
+ "dark": "Sombre",
+ "font_size": "Taille de la police",
+ "player_name": "Nom du joueur",
+ "currency": "Devise",
+ "font": "Police d'écriture",
+ "calculated": "calculé",
+ "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_review": "Cet entrée n'a pas encore été relue. Le contenu devrait néanmoins être correct.",
+ "status_done": "Cet entrée a été vérifiée et corrigée. Si vous remarquez des erreurs de traduction ou des fautes de frappe, veuillez nous contacter afin que nous puissions les corriger !",
+ "incomplete": "Incomplet",
+ "draft": "Ébauche",
+ "review": "Pour vérification",
+ "done": "Terminé",
+ "status": "Statut",
+ "transcribers": "Transcripteurs",
+ "translators": "Traducteurs",
+ "proofreaders": "Correcteurs",
+ "transcript_notice": "Ceci est une transcription",
+ "translation_notice": "Ceci est une traduction",
+ "source_language": "Langue source",
+ "pronouns": "Pronoms",
+ "no_category": "Pas de categorie",
+ "item": "Item",
+ "items": "Items",
+ "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",
+ "change_language": "Changer de langue",
+ "open_search": "Ouvrir le menu de recherche",
+ "chronology": "Chronologie",
+ "accords_handbook": "Le manuel de Accord",
+ "legality": "Légalité",
+ "members": "Membres",
+ "sharing_policy": "Politique de partage",
+ "contact_us": "Nous contacter",
+ "email": "Email",
+ "email_gdpr_notice": "Nous utilisons votre adresse électronique uniquement pour vous contacter au sujet de votre demande. Nous ne partageons cette adresse avec personne et ne l'utilisons pas à d'autres fins.",
+ "message": "Message",
+ "send": "Envoyer",
+ "response_invalid_code": "Le code de vérification est incorrect.",
+ "response_invalid_email": "Veuillez saisir une adresse électronique valide !",
+ "response_email_success": "Merci de nous avoir contactés ! Nous prendrons contact avec vous sous peu.",
+ "always_show_info": "Toujours montrer les informations",
+ "item_not_available": "Cet article n'est pas à vendre ou n'est plus disponible.",
+ "primary_language": "Langue principale",
+ "secondary_language": "Langues secondaires",
+ "combine_related_contents": "Combiner les contenus connexes",
+ "previous_content": "Contenu précédent",
+ "followup_content": "Contenu suivant",
+ "videos": "Videos",
+ "view_on": "Voir sur",
+ "channel": "Chaîne",
+ "subscribers": "Abonnés",
+ "description": "Description",
+ "available_at": "Disponible sur",
+ "search_title": "Rechercher un titre...",
+ "want_it": "Je le veux !",
+ "have_it": "Je l'ai !",
+ "source": "Source",
+ "reset_all_filters": "Réinitialiser les filtres",
+ "only_display_items_i_have": "Seulement afficher les items marqués avec \"je le veux\"",
+ "only_display_items_i_want": "Seulement afficher les items marqués avec \"je l'ai\"",
+ "only_display_unmarked_items": "Seulement afficher les items non-marqués",
+ "display_all_items": "Afficher tous les items",
+ "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.",
+ "all": "Tous",
+ "special_pages": "Pages spéciales",
+ "scan": "Scan",
+ "scanlation": "Scantrad",
+ "scanners": "Scanneurs",
+ "cleaners": "Nettoyeurs",
+ "typesetters": "Lettreurs",
+ "notes": "Notes",
+ "cover": "Couverture",
+ "tags": "Tags",
+ "no_source_warning": "Pas de source !",
+ "copy_anchor_link": "Cliquez pour copier le permalien",
+ "anchor_link_copied": "Copié ! 👍",
+ "folders": "Dossiers",
+ "empty_folder_message": "Ce dossier est vide",
+ "switch_to_grid_view": "Vue en grille",
+ "switch_to_folder_view": "Vue par dossier",
+ "content_is_not_available": "Ce contenu n'est pas disponible",
+ "paper_texture": "Texture de papier",
+ "book_fold": "Pliure du livre",
+ "lighting": "Effet de lumière",
+ "side_pages": "Tranche du livre",
+ "shadow": "Ombre portée",
+ "night_reader": "Mode nuit",
+ "single_page_view": "Vue 1 page",
+ "double_page_view": "Vue 2 pages",
+ "reset_all_options": "Réinitialiser les options",
+ "reading_layout": "Mode de lecture",
+ "quality": "Qualité"
+ }
+ },
+ {
+ "attributes": {
+ "ui_language": { "data": { "attributes": { "code": "ja" } } },
+ "library": "ライブラリー",
+ "contents": "コンテンツ",
+ "wiki": "ウィキ",
+ "chronicles": "クロニクル",
+ "library_short_description": "すべての物理メディアとデジタルメディアを見る",
+ "contents_short_description": "すべてのコンテンツを検索し、種類やカテゴリーで絞り込むことができます。",
+ "wiki_short_description": "ゲーム宇宙に関連するすべての百科事典です。",
+ "chronicles_short_description": "すべてのイベントとコンテンツを時系列で体験できる",
+ "news": "ニュース",
+ "merch": "マーチ",
+ "gallery": "ギャラリー",
+ "archives": "アーカイブス",
+ "about_us": "会社概要",
+ "licensing_notice": "このウェブサイトのコンテンツは、特に断りのない限り [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) で提供されています。",
+ "copyright_notice": "Accord's Libraryは、株式会社スクウェア・エニックスと提携、または推奨しているものではありません。株式会社スクウェア・エニックスの登録商標です。すべてのゲーム資産およびプロモーション素材は、© SQUARE ENIX CO. LTD.に帰属します。",
+ "contents_description": "図書館や他のオンラインソースのすべてのコンテンツ(テキスト、オーディオ、ビデオ)。",
+ "type": "タイプ",
+ "category": "カテゴリー",
+ "categories": "カテゴリー",
+ "size": "サイズ",
+ "release_date": "発売日",
+ "release_year": "発売年",
+ "details": "詳細",
+ "price": "価格",
+ "width": "幅",
+ "height": "高さ",
+ "thickness": "厚み",
+ "subitem": "サブアイテム",
+ "subitems": "サブアイテム",
+ "subitem_of": "のサブアイテム",
+ "variant": "バリアント",
+ "variants": "バリアント",
+ "variant_of": "のバリアント",
+ "summary": "概要",
+ "audio": "オーディオ",
+ "video": "ビデオ",
+ "textual": "テキスト",
+ "game": "ゲーム",
+ "other": "他",
+ "return_to": "戻る",
+ "left_to_right": "左から右へ",
+ "right_to_left": "右から左へ",
+ "page": "ページ",
+ "pages": "ページ",
+ "page_order": "ページ順序",
+ "binding": "製本",
+ "type_information": "タイプ情報",
+ "front_matter": "フロントマター",
+ "back_matter": "バックナンバー",
+ "open_content": "コンテンツを開放",
+ "read_content": "コンテンツを読む",
+ "watch_content": "コンテンツを見る",
+ "listen_content": "コンテンツを聴く",
+ "view_scans": "スキャンを開放",
+ "paperback": "ペーパーバック",
+ "hardcover": "ハードカバー",
+ "languages": "言語",
+ "select_language": "言語を選択する",
+ "language": "言語",
+ "library_description": "ヨコベースの副教材(書籍、小説、画集、舞台劇、漫画、ドラマCD、コミック)を網羅したリストです。それぞれについて、写真、スキャン、内容の書き起こし、どんなものなのか、いつ、どのように発売されたのか、サイズ、初回価格...などの情報を掲載しています。",
+ "wiki_description": "DrakeNieRに関連するすべての百科事典です。現在は年表のみですが、今後多くのページを公開予定です",
+ "chronicles_description": "Accord's Libraryは、ヨーコ・タローの全作品を収集・保存することを目的としています。ヨーコ・タローは、日本のゲームディレクター、シナリオライターです。",
+ "news_description": "レコーダーが書いたニュース記事です ここでは、新しい商品/アイテムのリリースに関するお知らせ、ガイド、セオリー、アンボックス、ショーケース...をご紹介しています。",
+ "merch_description": "",
+ "gallery_description": "",
+ "archives_description": "",
+ "about_us_description": "Accord's Libraryプロジェクトについては、以下のページで詳しくご紹介しています。",
+ "page_not_found": "ページが見つかりません",
+ "default_description": "Accord's Libraryは、ヨーコ・タローの全作品を収集・保存することを目的としています。ヨーコ・タローは、日本のゲームディレクター、シナリオライターです。",
+ "name": "名称",
+ "show_subitems": "サブアイテムをみせる",
+ "show_primary_items": "一次のイテムをみせる",
+ "show_secondary_items": "二次のイテムをみせる",
+ "no_type": "タイプなし",
+ "no_year": "年なし",
+ "order_by": "注文する",
+ "group_by": "グループ化する",
+ "select_option_sidebar": "サイドバーのオプションを選択します",
+ "group": "グループ",
+ "settings": "設定",
+ "theme": "テーマ",
+ "light": "光",
+ "auto": "オート",
+ "dark": "暗",
+ "font_size": "文字サイズ",
+ "player_name": "プレイヤー名",
+ "currency": "通貨",
+ "font": "文字",
+ "calculated": "計算された",
+ "status_incomplete": "このエントリーは一部のみ翻訳/転記されています。",
+ "status_draft": "このエントリーはあくまで下書きです。通常、これは作業中であることを意味します。翻訳/転写は稚拙であったり、コンピュータで作成されたものであったりするかもしれません。",
+ "status_review": "このエントリーはまだ校正されていません。内容はまだ正確であるはずです。",
+ "status_done": "このエントリーは、チェックと校正を行いました。もし、翻訳ミスや誤字脱字にお気づきの際は、修正いたしますので、ご連絡ください",
+ "incomplete": "未完成",
+ "draft": "ドラフト",
+ "review": "レビュー",
+ "done": "完了",
+ "status": "状況",
+ "transcribers": "トランスクライバー",
+ "translators": "翻訳者",
+ "proofreaders": "校正者",
+ "transcript_notice": "このコンテンツは転写です",
+ "translation_notice": "このコンテンツはファンによる翻訳です",
+ "source_language": "ソース言語",
+ "pronouns": "代名詞",
+ "no_category": "カテゴリーなし",
+ "item": "項目",
+ "items": "項目",
+ "content": "コンテンツ",
+ "result": "結果",
+ "results": "結果",
+ "language_switch_message": null,
+ "open_settings": "オープン設定",
+ "change_language": "言語を変更する",
+ "open_search": "オープンサーチ",
+ "chronology": "年表",
+ "accords_handbook": "アコードの手引き",
+ "legality": "合法性",
+ "members": "メンバー紹介",
+ "sharing_policy": "共有ポリシー",
+ "contact_us": "お問い合わせ",
+ "email": "電子メール",
+ "email_gdpr_notice": "お客様の電子メールは、お客様のご要望に関してご連絡するためにのみ使用します。この電子メールを誰かと共有したり、他の目的で使用することはありません。",
+ "message": "メッセージ",
+ "send": "送信",
+ "response_invalid_code": "検証コードが正しくありません。",
+ "response_invalid_email": "有効なEメールアドレスを入力してください",
+ "response_email_success": "お問い合わせありがとうございます。折り返しご連絡させていただきます。",
+ "always_show_info": "常に情報を表示する",
+ "item_not_available": "この商品は非売品です",
+ "primary_language": "主要言語",
+ "secondary_language": "二次言語",
+ "combine_related_contents": "関連するコンテンツを組み合わせる",
+ "previous_content": "前のコンテンツ",
+ "followup_content": "フォローアップコンテンツ",
+ "videos": "動画",
+ "view_on": "見る",
+ "channel": "チャンネル",
+ "subscribers": "サブスクライバー",
+ "description": "説明",
+ "available_at": "でご覧いただけます。",
+ "search_title": "検索タイトル...",
+ "want_it": "欲しいです!",
+ "have_it": "持ってます!",
+ "source": "出典",
+ "reset_all_filters": "すべてのフィルタをリセットする",
+ "only_display_items_i_have": "\"持ってる \"と表示されているもののみ表示",
+ "only_display_items_i_want": "\"欲しい \"とマークされたものだけを表示する",
+ "only_display_unmarked_items": "無印のアイテムのみ表示",
+ "display_all_items": "すべての項目を表示する",
+ "table_of_contents": "目次",
+ "definition": "定義",
+ "no_results_message": "結果が出ません。検索条件を変更またはリセットしてみてください。",
+ "all": "すべて",
+ "special_pages": "特設ページ",
+ "scan": null,
+ "scanlation": null,
+ "scanners": null,
+ "cleaners": null,
+ "typesetters": null,
+ "notes": null,
+ "cover": null,
+ "tags": null,
+ "no_source_warning": null,
+ "copy_anchor_link": null,
+ "anchor_link_copied": null,
+ "folders": null,
+ "empty_folder_message": null,
+ "switch_to_grid_view": null,
+ "switch_to_folder_view": null,
+ "content_is_not_available": null,
+ "paper_texture": null,
+ "book_fold": null,
+ "lighting": null,
+ "side_pages": null,
+ "shadow": null,
+ "night_reader": null,
+ "single_page_view": null,
+ "double_page_view": null,
+ "reset_all_options": null,
+ "reading_layout": null,
+ "quality": null
+ }
+ },
+ {
+ "attributes": {
+ "ui_language": { "data": { "attributes": { "code": "es" } } },
+ "library": "Librería",
+ "contents": "Contenidos",
+ "wiki": "Wiki",
+ "chronicles": "Crónicas",
+ "library_short_description": "Explora todos los medios físicos y digitales",
+ "contents_short_description": "Explora todo el contenido y filtra por tipo o categoría",
+ "wiki_short_description": "Una enciclopedia para todo lo relacionado con DrakeNieR",
+ "chronicles_short_description": "Experimenta todos los eventos y contenidos en orden cronológico",
+ "news": "Novedades",
+ "merch": "Merch",
+ "gallery": "Galería",
+ "archives": "Archivos",
+ "about_us": "Sobre nosotros",
+ "licensing_notice": "El contenido de este sitio web está disponible bajo [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) a menos que se indique lo contrario.",
+ "copyright_notice": "Accord's Library no está afiliada ni respaldada por SQUARE ENIX CO. LTD. Todos los archivos de los juegos y material promocional pertenecen a © SQUARE ENIX CO. LTD.",
+ "contents_description": "Todo el contenido (textual, audio y video) de la Biblioteca u otras fuentes en línea.",
+ "type": "Tipo",
+ "category": "Categoría",
+ "categories": "Categorías",
+ "size": "Tamaño",
+ "release_date": "Fecha de lanzamiento",
+ "release_year": "Año de lanzamiento",
+ "details": "Detalles",
+ "price": "Precio",
+ "width": "Ancho",
+ "height": "Altura",
+ "thickness": "Grosor",
+ "subitem": "Sub-item",
+ "subitems": "Sub-items",
+ "subitem_of": "Sub-item de",
+ "variant": "Variante",
+ "variants": "Variantes",
+ "variant_of": "Variante de",
+ "summary": "Sumario",
+ "audio": "Audio",
+ "video": "Video",
+ "textual": "Textual",
+ "game": "Juego",
+ "other": "Otros",
+ "return_to": "Volver a",
+ "left_to_right": "Izquierda a derecha",
+ "right_to_left": "Derecha a izquierda",
+ "page": "Página",
+ "pages": "Páginas",
+ "page_order": "Orden de las páginas",
+ "binding": "Encuadernación",
+ "type_information": "Tipo de información",
+ "front_matter": "Anteportada",
+ "back_matter": "Portada anterior",
+ "open_content": "Abrir contenido",
+ "read_content": "Leer contenido",
+ "watch_content": "Ver contenido",
+ "listen_content": "Escuchar contenido",
+ "view_scans": "Ver escaneos",
+ "paperback": "Tapa blanda",
+ "hardcover": "Tapa dura",
+ "languages": "Idiomas",
+ "select_language": "Seleccionar 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...",
+ "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": "",
+ "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": "",
+ "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",
+ "default_description": "Accord's Library tiene como objetivo recopilar y archivar todo el trabajo de Yoko Taro. Yoko Taro es un director de videojuegos y escritor de escenarios japonés.",
+ "name": "Nombre",
+ "show_subitems": "Mostrar sub-items",
+ "show_primary_items": "Mostrar items principales",
+ "show_secondary_items": "Mostrar items secundarios",
+ "no_type": "Ningún tipo",
+ "no_year": "Ningún año",
+ "order_by": "Ordenar por",
+ "group_by": "Agrupar por",
+ "select_option_sidebar": "Selecciona una de las opciones en la barra lateral",
+ "group": "Grupo",
+ "settings": "Ajustes",
+ "theme": "Tema",
+ "light": "Claro",
+ "auto": "Auto",
+ "dark": "Oscuro",
+ "font_size": "Tamaño de la fuente",
+ "player_name": "Nombre del jugador/a",
+ "currency": "Divisa",
+ "font": "Fuente",
+ "calculated": "Calculada",
+ "status_incomplete": "Esta entrada está solo parcialmente traducida/transcrita.",
+ "status_draft": "Esta entrada es solo un borrador. Por lo general, significa que se trata de un trabajo en curso. La traducción/transcripción puede ser deficiente y/o generada por ordenador.",
+ "status_review": "Esta entrada aún no ha sido corregida. No obstante, el contenido debería ser preciso.",
+ "status_done": "Esta entrada ha sido revisada y corregida. Si notas algún error de traducción o error tipográfico, contáctanos para que podamos solucionarlo!",
+ "incomplete": "Incompleto",
+ "draft": "Borrador",
+ "review": "Revisado",
+ "done": "Completado",
+ "status": "Estado",
+ "transcribers": "Transcriptores/as",
+ "translators": "Traductores/as",
+ "proofreaders": "Correctores/as",
+ "transcript_notice": "Este contenido es una transcripción",
+ "translation_notice": "Este contenido es una traducción de fans",
+ "source_language": "Idioma original",
+ "pronouns": "Pronombres",
+ "no_category": "Ningún categoría",
+ "item": null,
+ "items": null,
+ "content": null,
+ "result": null,
+ "results": null,
+ "language_switch_message": null,
+ "open_settings": null,
+ "change_language": null,
+ "open_search": null,
+ "chronology": null,
+ "accords_handbook": null,
+ "legality": null,
+ "members": null,
+ "sharing_policy": null,
+ "contact_us": null,
+ "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.",
+ "message": null,
+ "send": null,
+ "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_email_success": "¡Gracias por contactarnos! Nos pondremos en contacto contigo en breve.",
+ "always_show_info": null,
+ "item_not_available": null,
+ "primary_language": null,
+ "secondary_language": null,
+ "combine_related_contents": null,
+ "previous_content": null,
+ "followup_content": null,
+ "videos": null,
+ "view_on": null,
+ "channel": null,
+ "subscribers": null,
+ "description": null,
+ "available_at": null,
+ "search_title": null,
+ "want_it": null,
+ "have_it": null,
+ "source": null,
+ "reset_all_filters": null,
+ "only_display_items_i_have": null,
+ "only_display_items_i_want": null,
+ "only_display_unmarked_items": null,
+ "display_all_items": null,
+ "table_of_contents": null,
+ "definition": null,
+ "no_results_message": null,
+ "all": null,
+ "special_pages": null,
+ "scan": null,
+ "scanlation": null,
+ "scanners": null,
+ "cleaners": null,
+ "typesetters": null,
+ "notes": null,
+ "cover": null,
+ "tags": null,
+ "no_source_warning": null,
+ "copy_anchor_link": null,
+ "anchor_link_copied": null,
+ "folders": null,
+ "empty_folder_message": null,
+ "switch_to_grid_view": null,
+ "switch_to_folder_view": null,
+ "content_is_not_available": null,
+ "paper_texture": null,
+ "book_fold": null,
+ "lighting": null,
+ "side_pages": null,
+ "shadow": null,
+ "night_reader": null,
+ "single_page_view": null,
+ "double_page_view": null,
+ "reset_all_options": null,
+ "reading_layout": null,
+ "quality": null
+ }
+ },
+ {
+ "attributes": {
+ "ui_language": { "data": { "attributes": { "code": "pt-br" } } },
+ "library": "Coleção",
+ "contents": "Conteúdos",
+ "wiki": "Wiki",
+ "chronicles": "Crônicas",
+ "library_short_description": "Procure por todas mídias digitais e físicas",
+ "contents_short_description": "Explore todo o conteúdo e filtre por categorias e tipos",
+ "wiki_short_description": "Uma enciclopédia com tudo relacionado a DrakeNieR",
+ "chronicles_short_description": "Explore as crônicas de DrakeNieR em ordem cronológica.",
+ "news": "Notícias",
+ "merch": "Mercadorias",
+ "gallery": "Galeria",
+ "archives": "Arquivos",
+ "about_us": "Sobre Nós",
+ "licensing_notice": "O conteúdo nesse site está disponível pela CC-BY-SA, a não ser que esteja anotado.",
+ "copyright_notice": "Accord's Library não é afiliada ou reconhecida pela SQUARE ENIX CO. LTD. Todos assets de jogos e materiais promocionais pertencem a © SQUARE ENIX CO. LTD.\n\n",
+ "contents_description": "",
+ "type": "Tipo",
+ "category": "Categoria",
+ "categories": "Categorias",
+ "size": "Tamanho",
+ "release_date": "Dia de lançamento",
+ "release_year": "Ano de lançamento",
+ "details": "Detalhes",
+ "price": "Preço",
+ "width": "Largura",
+ "height": "Altura",
+ "thickness": "Grossura",
+ "subitem": "Subitem",
+ "subitems": "Subitens",
+ "subitem_of": "Subitem de",
+ "variant": "Variante",
+ "variants": "Variantes",
+ "variant_of": "Variante de",
+ "summary": "Sumário",
+ "audio": "Audio",
+ "video": "Video",
+ "textual": "Textos",
+ "game": "Jogos",
+ "other": "Outros",
+ "return_to": "Voltar para",
+ "left_to_right": "Esquerda para direita",
+ "right_to_left": "Direita para esquerda",
+ "page": "Página",
+ "pages": "Páginas",
+ "page_order": "Ordem de páginas",
+ "binding": "Encadernação",
+ "type_information": "Informação do tipo",
+ "front_matter": "Pré textual",
+ "back_matter": "Pós textual",
+ "open_content": "Abrir conteúdo",
+ "read_content": "Ler o conteúdo",
+ "watch_content": "Assistir o conteúdo",
+ "listen_content": "Ouvir o conteúdo",
+ "view_scans": "Ver scans",
+ "paperback": "Brochura",
+ "hardcover": "Capa dura",
+ "languages": "Línguas",
+ "select_language": "Selecionar língua",
+ "language": "Língua",
+ "library_description": "",
+ "wiki_description": null,
+ "chronicles_description": null,
+ "news_description": "",
+ "merch_description": "",
+ "gallery_description": "",
+ "archives_description": "",
+ "about_us_description": "",
+ "page_not_found": "Página não encontrada",
+ "default_description": null,
+ "name": "Nome",
+ "show_subitems": "Mostrar subitens",
+ "show_primary_items": "Mostrar itens primários",
+ "show_secondary_items": "Mostrar itens secundários",
+ "no_type": "Sem tipo",
+ "no_year": "Sem ano",
+ "order_by": "Ordenar por",
+ "group_by": "Agrupar por",
+ "select_option_sidebar": "Selecione uma opção na aba lateral",
+ "group": "Grupo",
+ "settings": "Configurações",
+ "theme": "Tema",
+ "light": "Claro",
+ "auto": "Automático",
+ "dark": "Escuro",
+ "font_size": "Tamanho da fonte",
+ "player_name": "Nome do jogador",
+ "currency": "Moeda",
+ "font": "Fonte",
+ "calculated": "Calculado",
+ "status_incomplete": "Este conteúdo está incompleto e não foi traduzido/transcrito completamente.",
+ "status_draft": "A tradução/transcrição selecionada é um Rascunho. Isso significa que a tradução pode estar fraca e/ou ter sido gerada por uma inteligência artificial.",
+ "status_review": "Este conteúdo ainda não foi Revisado, erros gramaticais podem ser encontrados uma vez que os revisores ainda não leram a tradução.",
+ "status_done": "O conteúdo foi completamente traduzido e revisado.",
+ "incomplete": "Incompleto",
+ "draft": "Rascunho",
+ "review": "Review",
+ "done": "Concluido",
+ "status": "Status",
+ "transcribers": "Transcritores",
+ "translators": "Tradutores",
+ "proofreaders": "Revisores",
+ "transcript_notice": "Este conteúdo foi transcrito.",
+ "translation_notice": "Este conteúdo é uma tradução de fã.",
+ "source_language": "Língua original",
+ "pronouns": "Pronomes",
+ "no_category": "Sem Categoria",
+ "item": "Item",
+ "items": "Itens",
+ "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",
+ "change_language": "Mudar língua",
+ "open_search": "Abrir pesquisa",
+ "chronology": "Cronologia",
+ "accords_handbook": "Livro de mão da Accord",
+ "legality": "Legalidade",
+ "members": "Membros",
+ "sharing_policy": "Política de compartilhamento",
+ "contact_us": "Fale conosco",
+ "email": null,
+ "email_gdpr_notice": null,
+ "message": null,
+ "send": null,
+ "response_invalid_code": null,
+ "response_invalid_email": null,
+ "response_email_success": null,
+ "always_show_info": "Mostrar informações",
+ "item_not_available": "Item indisponível",
+ "primary_language": "Língua primaria",
+ "secondary_language": "Línguas secundárias",
+ "combine_related_contents": "Combinar relacionados",
+ "previous_content": "Conteúdo anterior:",
+ "followup_content": "Próximo conteúdo:",
+ "videos": "Videos:",
+ "view_on": "Ver no:",
+ "channel": "Canal",
+ "subscribers": "Inscritos",
+ "description": "Descrição",
+ "available_at": "Disponível no:",
+ "search_title": "Pesquisar",
+ "want_it": null,
+ "have_it": null,
+ "source": null,
+ "reset_all_filters": null,
+ "only_display_items_i_have": null,
+ "only_display_items_i_want": null,
+ "only_display_unmarked_items": null,
+ "display_all_items": null,
+ "table_of_contents": null,
+ "definition": null,
+ "no_results_message": null,
+ "all": null,
+ "special_pages": null,
+ "scan": null,
+ "scanlation": null,
+ "scanners": null,
+ "cleaners": null,
+ "typesetters": null,
+ "notes": null,
+ "cover": null,
+ "tags": null,
+ "no_source_warning": null,
+ "copy_anchor_link": null,
+ "anchor_link_copied": null,
+ "folders": null,
+ "empty_folder_message": null,
+ "switch_to_grid_view": null,
+ "switch_to_folder_view": null,
+ "content_is_not_available": null,
+ "paper_texture": null,
+ "book_fold": null,
+ "lighting": null,
+ "side_pages": null,
+ "shadow": null,
+ "night_reader": null,
+ "single_page_view": null,
+ "double_page_view": null,
+ "reset_all_options": null,
+ "reading_layout": null,
+ "quality": null
+ }
+ }
+ ]
+ }
+}
diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx
index efd932d..df788cf 100644
--- a/src/components/AppLayout.tsx
+++ b/src/components/AppLayout.tsx
@@ -1,10 +1,8 @@
import Head from "next/head";
-import { useMemo } from "react";
import { useSwipeable } from "react-swipeable";
import { layout } from "../../design.config";
import { Ico, Icon } from "./Ico";
import { MainPanel } from "./Panels/MainPanel";
-import { SafariPopup } from "./Panels/SafariPopup";
import { isDefined, isUndefined } from "helpers/others";
import { cIf, cJoin } from "helpers/className";
import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
@@ -77,10 +75,7 @@ export const AppLayout = ({
},
});
- const turnSubIntoContent = useMemo(
- () => isDefined(subPanel) && isUndefined(contentPanel),
- [contentPanel, subPanel]
- );
+ const turnSubIntoContent = isDefined(subPanel) && isUndefined(contentPanel);
return (
)}
-
);
};
diff --git a/src/components/Markdown/Markdawn.tsx b/src/components/Markdown/Markdawn.tsx
index 9bb5a84..0b2dc89 100644
--- a/src/components/Markdown/Markdawn.tsx
+++ b/src/components/Markdown/Markdawn.tsx
@@ -1,6 +1,6 @@
import Markdown from "markdown-to-jsx";
import { useRouter } from "next/router";
-import React, { Fragment, useMemo } from "react";
+import React, { Fragment } from "react";
import ReactDOMServer from "react-dom/server";
import { HorizontalLine } from "components/HorizontalLine";
import { Img } from "components/Img";
@@ -35,11 +35,8 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
const { showLightBox } = useAtomGetter(atoms.lightBox);
/* eslint-disable no-irregular-whitespace */
- const text = useMemo(
- () => `${preprocessMarkDawn(rawText, playerName)}
- `,
- [playerName, rawText]
- );
+ const text = `${preprocessMarkDawn(rawText, playerName)}
+ `;
/* eslint-enable no-irregular-whitespace */
if (isUndefined(text) || text === "") {
@@ -219,19 +216,17 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
interface TableOfContentsProps {
text: string;
title?: string;
-
horizontalLine?: boolean;
}
export const TableOfContents = ({
text,
title,
-
horizontalLine = false,
}: TableOfContentsProps): JSX.Element => {
const router = useRouter();
const langui = useAtomGetter(atoms.localData.langui);
- const toc = useMemo(() => getTocFromMarkdawn(preprocessMarkDawn(text), title), [text, title]);
+ const toc = getTocFromMarkdawn(preprocessMarkDawn(text), title);
return (
<>
@@ -268,27 +263,24 @@ interface HeaderProps {
const Header = ({ level, title, slug }: HeaderProps): JSX.Element => {
const isHoverable = useDeviceSupportsHover();
- const innerComponent = useMemo(
- () => (
- <>
-
- {title === "* * *" ? (
-
-
-
-
-
- ) : (
-
{title}
- )}
-
-
- >
- ),
- [isHoverable, slug, title]
+ const innerComponent = (
+ <>
+
+ {title === "* * *" ? (
+
+
+
+
+
+ ) : (
+
{title}
+ )}
+
+
+ >
);
switch (level) {
@@ -349,8 +341,7 @@ const TocLevel = ({
allowIntersection = true,
}: LevelProps): JSX.Element => {
const router = useRouter();
-
- const ids = useMemo(() => tocchildren.map((child) => child.slug), [tocchildren]);
+ const ids = tocchildren.map((child) => child.slug);
const currentIntersection = useIntersectionList(ids);
return (
diff --git a/src/components/PanelComponents/NavOption.tsx b/src/components/PanelComponents/NavOption.tsx
index d9ecaa8..d343120 100644
--- a/src/components/PanelComponents/NavOption.tsx
+++ b/src/components/PanelComponents/NavOption.tsx
@@ -1,5 +1,5 @@
import { useRouter } from "next/router";
-import { MouseEventHandler, useCallback, useMemo } from "react";
+import { MouseEventHandler, useCallback } from "react";
import { Ico, Icon } from "components/Ico";
import { ToolTip } from "components/ToolTip";
import { cIf, cJoin } from "helpers/className";
@@ -39,10 +39,7 @@ export const NavOption = ({
onClick,
}: Props): JSX.Element => {
const router = useRouter();
- const isActive = useMemo(
- () => active || router.asPath.startsWith(url),
- [active, router.asPath, url]
- );
+ const isActive = active || router.asPath.startsWith(url);
return (
{
- const [hasDisgardedSafariWarning, setHasDisgardedSafariWarning] = useSessionStorage(
- "hasDisgardedSafariWarning",
- false
- );
-
- const isClient = useIsClient();
- const isSafari = useMemo(() => {
- if (isClient) {
- const parser = new UAParser();
- return parser.getBrowser().name === "Safari" || parser.getOS().name === "iOS";
- }
- return false;
- }, [isClient]);
-
- return (
-
- Hi, you are using Safari!
-
- In most cases this wouldn’t be a problem but our website is—for some obscure
- reason—performing terribly on Safari (WebKit). Because of that, we have decided to display
- this message instead of letting you have a slow and painful experience. We are looking into
- the problem, and are hoping to fix this soon.
-
- In the meanwhile, if you are using an iPhone/iPad, please try using another device.
- If you are on macOS, please use another browser such as Firefox or Chrome.
-
- {
- setHasDisgardedSafariWarning(true);
- sendAnalytics("Safari", "Disgard warning");
- }}
- />
-
- );
-};
diff --git a/src/components/Panels/SettingsPopup.tsx b/src/components/Panels/SettingsPopup.tsx
index 3d27986..79a1c0c 100644
--- a/src/components/Panels/SettingsPopup.tsx
+++ b/src/components/Panels/SettingsPopup.tsx
@@ -1,5 +1,5 @@
import { useRouter } from "next/router";
-import { useEffect, useMemo, useState } from "react";
+import { useEffect, useState } from "react";
import { Icon } from "components/Ico";
import { Button } from "components/Inputs/Button";
import { ButtonGroup } from "components/Inputs/ButtonGroup";
@@ -34,12 +34,8 @@ export const SettingsPopup = (): JSX.Element => {
const router = useRouter();
- const currencyOptions = useMemo(
- () =>
- filterHasAttributes(currencies, ["attributes"] as const).map(
- (currentCurrency) => currentCurrency.attributes.code
- ),
- [currencies]
+ const currencyOptions = filterHasAttributes(currencies, ["attributes"] as const).map(
+ (currentCurrency) => currentCurrency.attributes.code
);
const [currencySelect, setCurrencySelect] = useState(-1);
diff --git a/src/components/PostPage.tsx b/src/components/PostPage.tsx
index 03af512..20dffda 100644
--- a/src/components/PostPage.tsx
+++ b/src/components/PostPage.tsx
@@ -1,4 +1,4 @@
-import { Fragment, useCallback, useMemo } from "react";
+import { Fragment, useCallback } from "react";
import { AppLayout, AppLayoutRequired } from "./AppLayout";
import { Chip } from "./Chip";
import { HorizontalLine } from "./HorizontalLine";
@@ -59,140 +59,104 @@ export const PostPage = ({
),
});
- const { thumbnail, body, title, excerpt } = useMemo(
- () => ({
- thumbnail:
- selectedTranslation?.thumbnail?.data?.attributes ?? post.thumbnail?.data?.attributes,
- body: selectedTranslation?.body ?? "",
- title: selectedTranslation?.title ?? prettySlug(post.slug),
- excerpt: selectedTranslation?.excerpt ?? "",
- }),
- [post.slug, post.thumbnail, selectedTranslation]
- );
+ const thumbnail =
+ selectedTranslation?.thumbnail?.data?.attributes ?? post.thumbnail?.data?.attributes;
+ const body = selectedTranslation?.body ?? "";
+ const title = selectedTranslation?.title ?? prettySlug(post.slug);
+ const excerpt = selectedTranslation?.excerpt ?? "";
- const subPanel = useMemo(
- () =>
- returnHref || returnTitle || displayCredits || displayToc ? (
-
- {returnHref && returnTitle && (
-
- )}
-
- {displayCredits && (
- <>
-
-
- {selectedTranslation && (
-
-
{langui.status}:
-
-
-
-
-
- )}
-
- {post.authors && post.authors.data.length > 0 && (
-
-
{"Authors"}:
-
- {filterHasAttributes(post.authors.data, ["id", "attributes"] as const).map(
- (author) => (
-
-
-
- )
- )}
-
-
- )}
- >
- )}
-
- {displayToc && }
-
- ) : undefined,
- [
- body,
- displayCredits,
- displayToc,
- langui,
- post.authors,
- returnHref,
- returnTitle,
- selectedTranslation,
- title,
- ]
- );
-
- const contentPanel = useMemo(
- () => (
-
+ const subPanel =
+ returnHref || returnTitle || displayCredits || displayToc ? (
+
{returnHref && returnTitle && (
-
+
)}
- {displayThumbnailHeader ? (
+ {displayCredits && (
<>
- 1 ? (
-
- ) : undefined
- }
- />
- >
- ) : (
- <>
- {displayLanguageSwitcher && (
-
-
+
+
+ {selectedTranslation && (
+
+
{langui.status}:
+
+
+
+
)}
- {displayTitle && (
-
{title}
+
+ {post.authors && post.authors.data.length > 0 && (
+
+
{"Authors"}:
+
+ {filterHasAttributes(post.authors.data, ["id", "attributes"] as const).map(
+ (author) => (
+
+
+
+ )
+ )}
+
+
)}
>
)}
- {prependBody}
- {body && (
- <>
- {displayThumbnailHeader &&
}
-
- >
- )}
+ {displayToc &&
}
+
+ ) : undefined;
- {appendBody}
-
- ),
- [
- LanguageSwitcher,
- appendBody,
- body,
- displayLanguageSwitcher,
- displayThumbnailHeader,
- displayTitle,
- excerpt,
- languageSwitcherProps,
- post.categories,
- prependBody,
- returnHref,
- returnTitle,
- thumbnail,
- title,
- ]
+ const contentPanel = (
+
+ {returnHref && returnTitle && (
+
+ )}
+
+ {displayThumbnailHeader ? (
+ <>
+ 1 ? (
+
+ ) : undefined
+ }
+ />
+ >
+ ) : (
+ <>
+ {displayLanguageSwitcher && (
+
+
+
+ )}
+ {displayTitle && (
+ {title}
+ )}
+ >
+ )}
+
+ {prependBody}
+ {body && (
+ <>
+ {displayThumbnailHeader && }
+
+ >
+ )}
+
+ {appendBody}
+
);
return
;
diff --git a/src/components/PreviewCard.tsx b/src/components/PreviewCard.tsx
index 367bcc8..bdb91bc 100644
--- a/src/components/PreviewCard.tsx
+++ b/src/components/PreviewCard.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useMemo } from "react";
+import { useCallback } from "react";
import { useRouter } from "next/router";
import { Chip } from "./Chip";
import { Ico, Icon } from "./Ico";
@@ -75,40 +75,37 @@ export const PreviewCard = ({
const isHoverable = useDeviceSupportsHover();
const router = useRouter();
- const metadataJSX = useMemo(
- () => (
- <>
- {metadata && (metadata.releaseDate || metadata.price) && (
-
- {metadata.releaseDate && (
-
-
- {prettyDate(metadata.releaseDate, router.locale)}
-
- )}
- {metadata.price && (
-
-
- {prettyPrice(metadata.price, currencies, currency)}
-
- )}
- {metadata.views && (
-
-
- {prettyShortenNumber(metadata.views)}
-
- )}
- {metadata.author && (
-
-
- {metadata.author}
-
- )}
-
- )}
- >
- ),
- [currencies, currency, metadata, router.locale]
+ const metadataJSX = (
+ <>
+ {metadata && (metadata.releaseDate || metadata.price) && (
+
+ {metadata.releaseDate && (
+
+
+ {prettyDate(metadata.releaseDate, router.locale)}
+
+ )}
+ {metadata.price && (
+
+
+ {prettyPrice(metadata.price, currencies, currency)}
+
+ )}
+ {metadata.views && (
+
+
+ {prettyShortenNumber(metadata.views)}
+
+ )}
+ {metadata.author && (
+
+
+ {metadata.author}
+
+ )}
+
+ )}
+ >
);
return (
diff --git a/src/components/SmartList.tsx b/src/components/SmartList.tsx
index e081d45..b994936 100644
--- a/src/components/SmartList.tsx
+++ b/src/components/SmartList.tsx
@@ -1,4 +1,4 @@
-import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
+import { Fragment, useCallback, useEffect, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import naturalCompare from "string-natural-compare";
import { Chip } from "./Chip";
@@ -87,17 +87,11 @@ export const SmartList =
({
return items;
}, [items, searchingBy, searchingCaseInsensitive, searchingTerm]);
- const filteredItems = useMemo(() => {
- const filteredBySearch = searchFilter();
- return filteredBySearch.filter(filteringFunction);
- }, [filteringFunction, searchFilter]);
+ const filteredItems = searchFilter().filter(filteringFunction);
- const sortedItem = useMemo(
- () => filteredItems.sort(sortingFunction),
- [filteredItems, sortingFunction]
- );
+ const sortedItem = filteredItems.sort(sortingFunction);
- const groups = useMemo(() => {
+ const groups = (() => {
const memo: Group[] = [];
sortedItem.forEach((item) => {
@@ -116,9 +110,9 @@ export const SmartList = ({
});
});
return memo.sort(groupSortingFunction);
- }, [groupCountingFunction, groupSortingFunction, groupingFunction, sortedItem]);
+ })();
- const pages = useMemo(() => {
+ const pages = (() => {
const memo: Group[][] = [];
let currentPage: Group[] = [];
let remainingSlots = paginationItemPerPage;
@@ -162,7 +156,7 @@ export const SmartList = ({
}
return memo;
- }, [groups, paginationItemPerPage]);
+ })();
useHotkeys("left", () => setPage((current) => current - 1), { enabled: page > 0 });
useHotkeys("right", () => setPage((current) => current + 1), {
diff --git a/src/contexts/settings.ts b/src/contexts/settings.ts
index fe98769..d505a29 100644
--- a/src/contexts/settings.ts
+++ b/src/contexts/settings.ts
@@ -84,6 +84,7 @@ export const useSettings = (): void => {
useEffect(() => {
if (preferredLanguages.length === 0) {
if (isDefinedAndNotEmpty(router.locale) && router.locales) {
+ console.log(router.locale, getDefaultPreferredLanguages(router.locale, router.locales));
setPreferredLanguages(getDefaultPreferredLanguages(router.locale, router.locales));
}
} else if (router.locale !== preferredLanguages[0]) {
diff --git a/src/contexts/webkitFixes.ts b/src/contexts/webkitFixes.ts
new file mode 100644
index 0000000..fdaae91
--- /dev/null
+++ b/src/contexts/webkitFixes.ts
@@ -0,0 +1,18 @@
+import { useLayoutEffect } from "react";
+import { isDefined } from "helpers/others";
+import { useIsWebkit } from "hooks/useIsWebkit";
+
+export const useWebkitFixes = (): void => {
+ const isWebkit = useIsWebkit();
+
+ useLayoutEffect(() => {
+ const next = document.getElementById("__next");
+ if (isDefined(next)) {
+ if (isWebkit) {
+ next.classList.add("webkit-fixes");
+ } else {
+ next.classList.remove("webkit-fixes");
+ }
+ }
+ }, [isWebkit]);
+};
diff --git a/src/hooks/useFullscreen.ts b/src/hooks/useFullscreen.ts
index 1e2f740..a9fd4a0 100644
--- a/src/hooks/useFullscreen.ts
+++ b/src/hooks/useFullscreen.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import { useIsClient } from "usehooks-ts";
import { isDefined } from "helpers/others";
@@ -13,7 +13,7 @@ export const useFullscreen = (
const [isFullscreen, setIsFullscreen] = useState(false);
const isClient = useIsClient();
- const elem = useMemo(() => (isClient ? document.querySelector(`#${id}`) : null), [id, isClient]);
+ const elem = isClient ? document.querySelector(`#${id}`) : null;
const requestFullscreen = useCallback(() => elem?.requestFullscreen(), [elem]);
const exitFullscreen = useCallback(
diff --git a/src/hooks/useIntersectionList.ts b/src/hooks/useIntersectionList.ts
index 65ac24c..04e44a4 100644
--- a/src/hooks/useIntersectionList.ts
+++ b/src/hooks/useIntersectionList.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import { throttle } from "throttle-debounce";
import { useIsClient } from "usehooks-ts";
import { useOnScroll } from "./useOnScroll";
@@ -10,10 +10,7 @@ export const useIntersectionList = (ids: string[]): number => {
const isClient = useIsClient();
- const contentPanel = useMemo(
- () => (isClient ? document.getElementById(Ids.ContentPanel) : null),
- [isClient]
- );
+ const contentPanel = isClient ? document.getElementById(Ids.ContentPanel) : null;
const refreshCurrentIntersection = useCallback(
(scroll: number) => {
diff --git a/src/hooks/useIsWebkit.ts b/src/hooks/useIsWebkit.ts
new file mode 100644
index 0000000..5d69454
--- /dev/null
+++ b/src/hooks/useIsWebkit.ts
@@ -0,0 +1,14 @@
+import { useMemo } from "react";
+import UAParser from "ua-parser-js";
+import { useIsClient } from "usehooks-ts";
+
+export const useIsWebkit = (): boolean => {
+ const isClient = useIsClient();
+ return useMemo(() => {
+ if (isClient) {
+ const parser = new UAParser();
+ return parser.getBrowser().name === "Safari" || parser.getOS().name === "iOS";
+ }
+ return false;
+ }, [isClient]);
+};
diff --git a/src/hooks/useOnScroll.ts b/src/hooks/useOnScroll.ts
index ec5f90e..7d9d480 100644
--- a/src/hooks/useOnScroll.ts
+++ b/src/hooks/useOnScroll.ts
@@ -1,10 +1,10 @@
-import { useMemo, useCallback, useEffect } from "react";
+import { useCallback, useEffect } from "react";
import { useIsClient } from "usehooks-ts";
import { Ids } from "types/ids";
export const useOnScroll = (id: Ids, onScroll: (scroll: number) => void): void => {
const isClient = useIsClient();
- const elem = useMemo(() => (isClient ? document.querySelector(`#${id}`) : null), [id, isClient]);
+ const elem = isClient ? document.querySelector(`#${id}`) : null;
const listener = useCallback(() => {
if (elem?.scrollTop) {
onScroll(elem.scrollTop);
diff --git a/src/hooks/useReaderSettings.ts b/src/hooks/useReaderSettings.ts
index 672e06b..ac58a21 100644
--- a/src/hooks/useReaderSettings.ts
+++ b/src/hooks/useReaderSettings.ts
@@ -1,4 +1,4 @@
-import { Dispatch, SetStateAction, useCallback, useMemo } from "react";
+import { Dispatch, SetStateAction, useCallback } from "react";
import { useLocalStorage } from "usehooks-ts";
import { ImageQuality } from "helpers/img";
@@ -94,13 +94,8 @@ export const useReaderSettings = (): {
setTeint,
]);
- const filterSettings = useMemo(
- () => ({ bookFold, lighting, paperTexture, teint, dropShadow }),
- [bookFold, dropShadow, lighting, paperTexture, teint]
- );
-
return {
- filterSettings,
+ filterSettings: { bookFold, lighting, paperTexture, teint, dropShadow },
isSidePagesEnabled,
pageQuality,
toggleBookFold,
diff --git a/src/hooks/useScrollIntoView.ts b/src/hooks/useScrollIntoView.ts
index 4f5a504..5b44dc3 100644
--- a/src/hooks/useScrollIntoView.ts
+++ b/src/hooks/useScrollIntoView.ts
@@ -3,30 +3,34 @@ import { useEffect, useState } from "react";
import { useCounter } from "usehooks-ts";
import { isDefined } from "helpers/others";
+const NUM_RETRIES = 10;
+
export const useScrollIntoView = (): void => {
const router = useRouter();
const { count, increment } = useCounter(0);
const [hasReachedElem, setHasReachedElem] = useState(false);
useEffect(() => {
- if (!hasReachedElem) {
- const indexHash = router.asPath.indexOf("#");
- if (indexHash > 0) {
- const hash = router.asPath.slice(indexHash + 1);
- const element = document.getElementById(hash);
- console.log(element);
- if (isDefined(element)) {
- console.log(`[useScrollIntoView] ${hash} found`);
- element.scrollIntoView();
- setHasReachedElem(true);
- } else {
- console.log(`[useScrollIntoView] ${hash} not found`);
- setTimeout(() => {
- increment();
- }, 100);
+ if (count < NUM_RETRIES)
+ if (!hasReachedElem) {
+ const indexHash = router.asPath.indexOf("#");
+ if (indexHash > 0) {
+ const hash = router.asPath.slice(indexHash + 1);
+ if (hash !== "") {
+ const element = document.getElementById(hash);
+ if (isDefined(element)) {
+ console.log(`[useScrollIntoView] ${hash} found`);
+ element.scrollIntoView();
+ setHasReachedElem(true);
+ } else {
+ console.log(`[useScrollIntoView] ${hash} not found`);
+ setTimeout(() => {
+ increment();
+ }, 200);
+ }
+ }
}
}
- }
- }, [increment, router.asPath, count, hasReachedElem, setHasReachedElem]);
+ }, [router.asPath, hasReachedElem, setHasReachedElem, increment, count]);
useEffect(() => setHasReachedElem(false), [router.asPath]);
};
diff --git a/src/hooks/useSmartLanguage.ts b/src/hooks/useSmartLanguage.ts
index 3f290af..2e65aa5 100644
--- a/src/hooks/useSmartLanguage.ts
+++ b/src/hooks/useSmartLanguage.ts
@@ -36,7 +36,7 @@ export const useSmartLanguage = ({
setSelectedTranslationIndex(getPreferredLanguage(preferredLanguages, availableLocales));
}, [preferredLanguages, availableLocales, router.locale]);
- const selectedTranslation = useMemo(() => {
+ const selectedTranslation = (() => {
if (isDefined(selectedTranslationIndex)) {
const item = items[selectedTranslationIndex];
if (isDefined(item)) {
@@ -44,7 +44,7 @@ export const useSmartLanguage = ({
}
}
return undefined;
- }, [items, selectedTranslationIndex, transform]);
+ })();
const languageSwitcherProps = {
languages: languages,
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 0121a98..c5c93d8 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -22,12 +22,14 @@ import { LightBoxProvider } from "contexts/LightBoxProvider";
import { SettingsPopup } from "components/Panels/SettingsPopup";
import { useSettings } from "contexts/settings";
import { useContainerQueries } from "contexts/containerQueries";
+import { useWebkitFixes } from "contexts/webkitFixes";
const AccordsLibraryApp = (props: AppProps): JSX.Element => {
useLocalData();
useAppLayout();
useSettings();
useContainerQueries();
+ useWebkitFixes();
return (
<>
diff --git a/src/pages/archives/index.tsx b/src/pages/archives/index.tsx
index e6cd447..23c3b1e 100644
--- a/src/pages/archives/index.tsx
+++ b/src/pages/archives/index.tsx
@@ -1,5 +1,4 @@
import { GetStaticProps } from "next";
-import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { NavOption } from "components/PanelComponents/NavOption";
import { PanelHeader } from "components/PanelComponents/PanelHeader";
@@ -20,20 +19,18 @@ interface Props extends AppLayoutRequired {}
const Archives = (props: Props): JSX.Element => {
const langui = useAtomGetter(atoms.localData.langui);
- const subPanel = useMemo(
- () => (
-
-
-
-
-
- ),
- [langui]
+ const subPanel = (
+
+
+
+
+
);
+
return ;
};
export default Archives;
diff --git a/src/pages/archives/videos/c/[uid].tsx b/src/pages/archives/videos/c/[uid].tsx
index 41e0709..4fe2507 100644
--- a/src/pages/archives/videos/c/[uid].tsx
+++ b/src/pages/archives/videos/c/[uid].tsx
@@ -1,5 +1,5 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
-import { useMemo, useState } from "react";
+import { useState } from "react";
import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Switch } from "components/Inputs/Switch";
@@ -51,80 +51,74 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
-
+
-
+
- {hoverable && (
-
-
-
- )}
-
- ),
- [hoverable, keepInfoVisible, langui, searchName, toggleKeepInfoVisible]
+ {hoverable && (
+
+
+
+ )}
+
);
- const contentPanel = useMemo(
- () => (
-
- item.id}
- renderItem={({ item }) => (
-
- )}
- className={cIf(
- isContentPanelAtLeast4xl,
- "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
- "grid-cols-2 gap-x-3 gap-y-5"
- )}
- groupingFunction={() => [channel?.title ?? ""]}
- paginationItemPerPage={25}
- searchingTerm={searchName}
- searchingBy={(item) => item.attributes.title}
- />
-
- ),
- [channel?.title, channel?.videos?.data, isContentPanelAtLeast4xl, keepInfoVisible, searchName]
+ const contentPanel = (
+
+ item.id}
+ renderItem={({ item }) => (
+
+ )}
+ className={cIf(
+ isContentPanelAtLeast4xl,
+ "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
+ "grid-cols-2 gap-x-3 gap-y-5"
+ )}
+ groupingFunction={() => [channel?.title ?? ""]}
+ paginationItemPerPage={25}
+ searchingTerm={searchName}
+ searchingBy={(item) => item.attributes.title}
+ />
+
);
return ;
diff --git a/src/pages/archives/videos/index.tsx b/src/pages/archives/videos/index.tsx
index 1dd29e3..1f5fee0 100644
--- a/src/pages/archives/videos/index.tsx
+++ b/src/pages/archives/videos/index.tsx
@@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
-import { useMemo, useState } from "react";
+import { useState } from "react";
import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { SmartList } from "components/SmartList";
@@ -52,75 +52,69 @@ const Videos = ({ videos, ...otherProps }: Props): JSX.Element => {
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
-
+
-
+
- {hoverable && (
-
-
-
- )}
-
- ),
- [hoverable, keepInfoVisible, langui, searchName, toggleKeepInfoVisible]
+ {hoverable && (
+
+
+
+ )}
+
);
- const contentPanel = useMemo(
- () => (
-
- item.id}
- renderItem={({ item }) => (
-
- )}
- className={cIf(
- isContentPanelAtLeast4xl,
- "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
- "grid-cols-2 gap-x-3 gap-y-5"
- )}
- paginationItemPerPage={25}
- searchingTerm={searchName}
- searchingBy={(item) => item.attributes.title}
- />
-
- ),
- [isContentPanelAtLeast4xl, keepInfoVisible, searchName, videos]
+ const contentPanel = (
+
+ item.id}
+ renderItem={({ item }) => (
+
+ )}
+ className={cIf(
+ isContentPanelAtLeast4xl,
+ "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
+ "grid-cols-2 gap-x-3 gap-y-5"
+ )}
+ paginationItemPerPage={25}
+ searchingTerm={searchName}
+ searchingBy={(item) => item.attributes.title}
+ />
+
);
return ;
};
diff --git a/src/pages/archives/videos/v/[uid].tsx b/src/pages/archives/videos/v/[uid].tsx
index e0115a8..a7eca34 100644
--- a/src/pages/archives/videos/v/[uid].tsx
+++ b/src/pages/archives/videos/v/[uid].tsx
@@ -1,5 +1,4 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
-import { useMemo } from "react";
import { useRouter } from "next/router";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { HorizontalLine } from "components/HorizontalLine";
@@ -34,120 +33,101 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
const langui = useAtomGetter(atoms.localData.langui);
const router = useRouter();
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
-
-
-
-
- ),
- [langui]
+
+
+
+
);
- const contentPanel = useMemo(
- () => (
-
-
+ const contentPanel = (
+
+
-
-
- {video.gone ? (
-
- ) : (
-
-
- ),
- [
- isContentPanelAtLeast4xl,
- langui,
- router.locale,
- video.channel?.data?.attributes,
- video.description,
- video.gone,
- video.likes,
- video.published_date,
- video.source,
- video.title,
- video.uid,
- video.views,
- ]
+ )}
+
+
+
+
{langui.description}
+
{video.description}
+
+
+
+
);
return ;
diff --git a/src/pages/chronicles/[slug]/index.tsx b/src/pages/chronicles/[slug]/index.tsx
index 84b2850..b3e3c80 100644
--- a/src/pages/chronicles/[slug]/index.tsx
+++ b/src/pages/chronicles/[slug]/index.tsx
@@ -67,105 +67,80 @@ const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element =
),
});
- const contentPanel = useMemo(
- () => (
-
-
+ const contentPanel = (
+
+
- {isDefined(selectedTranslation) ? (
- <>
- {selectedTranslation.title}
+ {isDefined(selectedTranslation) ? (
+ <>
+ {selectedTranslation.title}
- {languageSwitcherProps.locales.size > 1 && (
-
- )}
+ {languageSwitcherProps.locales.size > 1 && (
+
+ )}
- {isDefined(selectedTranslation.body) && (
-
- )}
- >
- ) : (
- <>
- {selectedContentTranslation && (
- <>
- 1 ? (
-
- ) : undefined
- }
- categories={primaryContent?.categories}
- type={primaryContent?.type}
- description={selectedContentTranslation.description}
- thumbnail={primaryContent?.thumbnail?.data?.attributes}
- />
+ {isDefined(selectedTranslation.body) && }
+ >
+ ) : (
+ <>
+ {selectedContentTranslation && (
+ <>
+ 1 ? (
+
+ ) : undefined
+ }
+ categories={primaryContent?.categories}
+ type={primaryContent?.type}
+ description={selectedContentTranslation.description}
+ thumbnail={primaryContent?.thumbnail?.data?.attributes}
+ />
- {selectedContentTranslation.text_set?.text && (
- <>
-
-
- >
- )}
- >
- )}
- >
- )}
-
- ),
- [
- selectedTranslation,
- languageSwitcherProps,
- LanguageSwitcher,
- selectedContentTranslation,
- ContentLanguageSwitcherProps,
- ContentLanguageSwitcher,
- primaryContent?.categories,
- primaryContent?.type,
- primaryContent?.thumbnail?.data?.attributes,
- langui,
- ]
+ {selectedContentTranslation.text_set?.text && (
+ <>
+
+
+ >
+ )}
+ >
+ )}
+ >
+ )}
+
);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
-
- {filterHasAttributes(chapters, ["attributes.chronicles", "id"] as const).map(
- (chapter) => (
- ({
- title: translation.title,
- language: translation.language.data.attributes.code,
- }))}
- fallback={{ title: prettySlug(chapter.attributes.slug) }}
- currentSlug={chronicle.slug}
- />
- )
- )}
-
-
- ),
- [chapters, chronicle.slug, langui]
+
+ {filterHasAttributes(chapters, ["attributes.chronicles", "id"] as const).map((chapter) => (
+ ({
+ title: translation.title,
+ language: translation.language.data.attributes.code,
+ }))}
+ fallback={{ title: prettySlug(chapter.attributes.slug) }}
+ currentSlug={chronicle.slug}
+ />
+ ))}
+
+
);
return (
diff --git a/src/pages/chronicles/index.tsx b/src/pages/chronicles/index.tsx
index 208b214..52ff422 100644
--- a/src/pages/chronicles/index.tsx
+++ b/src/pages/chronicles/index.tsx
@@ -1,5 +1,4 @@
import { GetStaticProps } from "next";
-import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { PanelHeader } from "components/PanelComponents/PanelHeader";
import { SubPanel } from "components/Containers/SubPanel";
@@ -26,37 +25,32 @@ interface Props extends AppLayoutRequired {
const Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
const langui = useAtomGetter(atoms.localData.langui);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
-
- {filterHasAttributes(chapters, ["attributes.chronicles", "id"] as const).map(
- (chapter) => (
- ({
- title: translation.title,
- language: translation.language.data.attributes.code,
- }))}
- fallback={{ title: prettySlug(chapter.attributes.slug) }}
- />
- )
- )}
-
-
- ),
- [chapters, langui]
+
+ {filterHasAttributes(chapters, ["attributes.chronicles", "id"] as const).map((chapter) => (
+ ({
+ title: translation.title,
+ language: translation.language.data.attributes.code,
+ }))}
+ fallback={{ title: prettySlug(chapter.attributes.slug) }}
+ />
+ ))}
+
+
);
return ;
diff --git a/src/pages/contents/[slug].tsx b/src/pages/contents/[slug].tsx
index cb32da4..b79bdab 100644
--- a/src/pages/contents/[slug].tsx
+++ b/src/pages/contents/[slug].tsx
@@ -1,5 +1,5 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
-import { Fragment, useCallback, useMemo } from "react";
+import { Fragment, useCallback } from "react";
import naturalCompare from "string-natural-compare";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Chip } from "components/Chip";
@@ -62,344 +62,305 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => {
useScrollTopOnChange(Ids.ContentPanel, [selectedTranslation]);
- const { previousContent, nextContent } = useMemo(
- () => ({
- previousContent:
- content.folder?.data?.attributes?.contents && content.folder.data.attributes.sequence
- ? getPreviousContent(content.folder.data.attributes.contents.data, content.slug)
- : undefined,
- nextContent:
- content.folder?.data?.attributes?.contents && content.folder.data.attributes.sequence
- ? getNextContent(content.folder.data.attributes.contents.data, content.slug)
- : undefined,
- }),
- [content.folder, content.slug]
- );
+ const previousContent =
+ content.folder?.data?.attributes?.contents && content.folder.data.attributes.sequence
+ ? getPreviousContent(content.folder.data.attributes.contents.data, content.slug)
+ : undefined;
+ const nextContent =
+ content.folder?.data?.attributes?.contents && content.folder.data.attributes.sequence
+ ? getNextContent(content.folder.data.attributes.contents.data, content.slug)
+ : undefined;
- const returnButtonProps = useMemo(
- () => ({
- href: content.folder?.data?.attributes
- ? `/contents/folder/${content.folder.data.attributes.slug}`
- : "/contents",
+ const returnButtonProps = {
+ href: content.folder?.data?.attributes
+ ? `/contents/folder/${content.folder.data.attributes.slug}`
+ : "/contents",
- translations: filterHasAttributes(content.folder?.data?.attributes?.titles, [
- "language.data.attributes.code",
- ] as const).map((title) => ({
- language: title.language.data.attributes.code,
- title: title.title,
- })),
- fallback: {
- title: content.folder?.data?.attributes
- ? prettySlug(content.folder.data.attributes.slug)
- : langui.contents,
- },
- langui,
- }),
- [content.folder?.data?.attributes, langui]
- );
+ translations: filterHasAttributes(content.folder?.data?.attributes?.titles, [
+ "language.data.attributes.code",
+ ] as const).map((title) => ({
+ language: title.language.data.attributes.code,
+ title: title.title,
+ })),
+ fallback: {
+ title: content.folder?.data?.attributes
+ ? prettySlug(content.folder.data.attributes.slug)
+ : langui.contents,
+ },
+ langui,
+ };
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
- {selectedTranslation?.text_set?.source_language?.data?.attributes?.code !== undefined && (
- <>
-
-
-
- {selectedTranslation.text_set.source_language.data.attributes.code ===
- selectedTranslation.language?.data?.attributes?.code
- ? langui.transcript_notice
- : langui.translation_notice}
-
+ {selectedTranslation?.text_set?.source_language?.data?.attributes?.code !== undefined && (
+ <>
+
+
+
+ {selectedTranslation.text_set.source_language.data.attributes.code ===
+ selectedTranslation.language?.data?.attributes?.code
+ ? langui.transcript_notice
+ : langui.translation_notice}
+
- {selectedTranslation.text_set.source_language.data.attributes.code !==
- selectedTranslation.language?.data?.attributes?.code && (
-
-
{langui.source_language}:
-
-
- )}
-
-
-
{langui.status}:
-
-
-
-
+ {selectedTranslation.text_set.source_language.data.attributes.code !==
+ selectedTranslation.language?.data?.attributes?.code && (
+
+
{langui.source_language}:
+
+ )}
- {selectedTranslation.text_set.transcribers &&
- selectedTranslation.text_set.transcribers.data.length > 0 && (
-
-
{langui.transcribers}:
-
- {filterHasAttributes(selectedTranslation.text_set.transcribers.data, [
- "attributes",
- "id",
- ] as const).map((recorder) => (
-
-
-
- ))}
-
-
- )}
+
+
{langui.status}:
- {selectedTranslation.text_set.translators &&
- selectedTranslation.text_set.translators.data.length > 0 && (
-
-
{langui.translators}:
-
- {filterHasAttributes(selectedTranslation.text_set.translators.data, [
- "attributes",
- "id",
- ] as const).map((recorder) => (
-
-
-
- ))}
-
-
- )}
+
+
+
+
- {selectedTranslation.text_set.proofreaders &&
- selectedTranslation.text_set.proofreaders.data.length > 0 && (
-
-
{langui.proofreaders}:
-
- {filterHasAttributes(selectedTranslation.text_set.proofreaders.data, [
- "attributes",
- "id",
- ] as const).map((recorder) => (
-
-
-
- ))}
-
-
- )}
-
- {isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (
+ {selectedTranslation.text_set.transcribers &&
+ selectedTranslation.text_set.transcribers.data.length > 0 && (
-
{langui.notes}:
+
{langui.transcribers}:
-
+ {filterHasAttributes(selectedTranslation.text_set.transcribers.data, [
+ "attributes",
+ "id",
+ ] as const).map((recorder) => (
+
+
+
+ ))}
)}
+
+ {selectedTranslation.text_set.translators &&
+ selectedTranslation.text_set.translators.data.length > 0 && (
+
+
{langui.translators}:
+
+ {filterHasAttributes(selectedTranslation.text_set.translators.data, [
+ "attributes",
+ "id",
+ ] as const).map((recorder) => (
+
+
+
+ ))}
+
+
+ )}
+
+ {selectedTranslation.text_set.proofreaders &&
+ selectedTranslation.text_set.proofreaders.data.length > 0 && (
+
+
{langui.proofreaders}:
+
+ {filterHasAttributes(selectedTranslation.text_set.proofreaders.data, [
+ "attributes",
+ "id",
+ ] as const).map((recorder) => (
+
+
+
+ ))}
+
+
+ )}
+
+ {isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (
+
+
{langui.notes}:
+
+
+
+
+ )}
+
+ >
+ )}
+
+ {selectedTranslation?.text_set?.text && (
+ <>
+
+ >
+ )}
+
+ {content.ranged_contents?.data && content.ranged_contents.data.length > 0 && (
+ <>
+
+
+
{langui.source}
+
+ {filterHasAttributes(content.ranged_contents.data, [
+ "attributes.library_item.data.attributes",
+ "attributes.library_item.data.id",
+ ] as const).map((rangedContent) => {
+ const libraryItem = rangedContent.attributes.library_item.data;
+ return (
+
+
0 &&
+ libraryItem.attributes.metadata[0]
+ ? [prettyItemSubType(libraryItem.attributes.metadata[0])]
+ : []
+ }
+ bottomChips={filterHasAttributes(libraryItem.attributes.categories?.data, [
+ "attributes",
+ ] as const).map((category) => category.attributes.short)}
+ metadata={{
+ releaseDate: libraryItem.attributes.release_date,
+ price: libraryItem.attributes.price,
+ position: "Bottom",
+ }}
+ infoAppend={
+ !isUntangibleGroupItem(libraryItem.attributes.metadata?.[0]) && (
+
+ )
+ }
+ />
+
+ );
+ })}
- >
+
+ >
+ )}
+
+ );
+
+ const contentPanel = (
+
+
+
+
+
1 ? (
+
+ ) : undefined
+ }
+ />
+
+ {previousContent?.attributes && (
+
+
{langui.previous_content}
+ ({
+ pre_title: translation.pre_title,
+ title: translation.title,
+ subtitle: translation.subtitle,
+ language: translation.language.data.attributes.code,
+ }))}
+ fallback={{
+ title: prettySlug(previousContent.attributes.slug),
+ }}
+ thumbnail={previousContent.attributes.thumbnail?.data?.attributes}
+ topChips={
+ isContentPanelAtLeast2xl && previousContent.attributes.type?.data?.attributes
+ ? [
+ previousContent.attributes.type.data.attributes.titles?.[0]
+ ? previousContent.attributes.type.data.attributes.titles[0]?.title
+ : prettySlug(previousContent.attributes.type.data.attributes.slug),
+ ]
+ : undefined
+ }
+ bottomChips={
+ isContentPanelAtLeast2xl
+ ? previousContent.attributes.categories?.data.map(
+ (category) => category.attributes?.short ?? ""
+ )
+ : undefined
+ }
+ />
+
)}
{selectedTranslation?.text_set?.text && (
<>
-
+
+ >
+ )}
+
+ {nextContent?.attributes && (
+ <>
+
+ {langui.followup_content}
+ ({
+ pre_title: translation.pre_title,
+ title: translation.title,
+ subtitle: translation.subtitle,
+ language: translation.language.data.attributes.code,
+ }))}
+ fallback={{ title: nextContent.attributes.slug }}
+ thumbnail={nextContent.attributes.thumbnail?.data?.attributes}
+ topChips={
+ isContentPanelAtLeast2xl && nextContent.attributes.type?.data?.attributes
+ ? [
+ nextContent.attributes.type.data.attributes.titles?.[0]
+ ? nextContent.attributes.type.data.attributes.titles[0]?.title
+ : prettySlug(nextContent.attributes.type.data.attributes.slug),
+ ]
+ : undefined
+ }
+ bottomChips={
+ isContentPanelAtLeast2xl
+ ? nextContent.attributes.categories?.data.map(
+ (category) => category.attributes?.short ?? ""
+ )
+ : undefined
+ }
/>
>
)}
-
- {content.ranged_contents?.data && content.ranged_contents.data.length > 0 && (
- <>
-
-
-
{langui.source}
-
- {filterHasAttributes(content.ranged_contents.data, [
- "attributes.library_item.data.attributes",
- "attributes.library_item.data.id",
- ] as const).map((rangedContent) => {
- const libraryItem = rangedContent.attributes.library_item.data;
- return (
-
-
0 &&
- libraryItem.attributes.metadata[0]
- ? [prettyItemSubType(libraryItem.attributes.metadata[0])]
- : []
- }
- bottomChips={filterHasAttributes(libraryItem.attributes.categories?.data, [
- "attributes",
- ] as const).map((category) => category.attributes.short)}
- metadata={{
- releaseDate: libraryItem.attributes.release_date,
- price: libraryItem.attributes.price,
- position: "Bottom",
- }}
- infoAppend={
- !isUntangibleGroupItem(libraryItem.attributes.metadata?.[0]) && (
-
- )
- }
- />
-
- );
- })}
-
-
- >
- )}
-
- ),
- [
- content.ranged_contents?.data,
- languages,
- langui,
- returnButtonProps,
- selectedTranslation,
- is1ColumnLayout,
- ]
- );
-
- const contentPanel = useMemo(
- () => (
-
-
-
-
-
1 ? (
-
- ) : undefined
- }
- />
-
- {previousContent?.attributes && (
-
-
{langui.previous_content}
- ({
- pre_title: translation.pre_title,
- title: translation.title,
- subtitle: translation.subtitle,
- language: translation.language.data.attributes.code,
- }))}
- fallback={{
- title: prettySlug(previousContent.attributes.slug),
- }}
- thumbnail={previousContent.attributes.thumbnail?.data?.attributes}
- topChips={
- isContentPanelAtLeast2xl && previousContent.attributes.type?.data?.attributes
- ? [
- previousContent.attributes.type.data.attributes.titles?.[0]
- ? previousContent.attributes.type.data.attributes.titles[0]?.title
- : prettySlug(previousContent.attributes.type.data.attributes.slug),
- ]
- : undefined
- }
- bottomChips={
- isContentPanelAtLeast2xl
- ? previousContent.attributes.categories?.data.map(
- (category) => category.attributes?.short ?? ""
- )
- : undefined
- }
- />
-
- )}
-
- {selectedTranslation?.text_set?.text && (
- <>
-
-
- >
- )}
-
- {nextContent?.attributes && (
- <>
-
- {langui.followup_content}
- ({
- pre_title: translation.pre_title,
- title: translation.title,
- subtitle: translation.subtitle,
- language: translation.language.data.attributes.code,
- }))}
- fallback={{ title: nextContent.attributes.slug }}
- thumbnail={nextContent.attributes.thumbnail?.data?.attributes}
- topChips={
- isContentPanelAtLeast2xl && nextContent.attributes.type?.data?.attributes
- ? [
- nextContent.attributes.type.data.attributes.titles?.[0]
- ? nextContent.attributes.type.data.attributes.titles[0]?.title
- : prettySlug(nextContent.attributes.type.data.attributes.slug),
- ]
- : undefined
- }
- bottomChips={
- isContentPanelAtLeast2xl
- ? nextContent.attributes.categories?.data.map(
- (category) => category.attributes?.short ?? ""
- )
- : undefined
- }
- />
- >
- )}
-
-
- ),
- [
- LanguageSwitcher,
- content.categories,
- content.thumbnail?.data?.attributes,
- content.type,
- isContentPanelAtLeast2xl,
- languageSwitcherProps,
- langui,
- nextContent?.attributes,
- previousContent?.attributes,
- returnButtonProps,
- selectedTranslation?.description,
- selectedTranslation?.pre_title,
- selectedTranslation?.subtitle,
- selectedTranslation?.text_set?.text,
- selectedTranslation?.title,
- ]
+
+
);
return
;
diff --git a/src/pages/contents/all.tsx b/src/pages/contents/all.tsx
index 1525854..057cebd 100644
--- a/src/pages/contents/all.tsx
+++ b/src/pages/contents/all.tsx
@@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
-import { useState, useMemo, useCallback } from "react";
+import { useState, useCallback } from "react";
import { useBoolean } from "usehooks-ts";
import naturalCompare from "string-natural-compare";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
@@ -116,163 +116,134 @@ const Contents = ({ contents, ...otherProps }: Props): JSX.Element => {
[searchName]
);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
-
+
-
+
- {
- setSearchName(name);
- if (isDefinedAndNotEmpty(name)) {
- sendAnalytics("Contents/All", "Change search term");
- } else {
- sendAnalytics("Contents/All", "Clear search term");
- }
+ {
+ setSearchName(name);
+ if (isDefinedAndNotEmpty(name)) {
+ sendAnalytics("Contents/All", "Change search term");
+ } else {
+ sendAnalytics("Contents/All", "Clear search term");
+ }
+ }}
+ />
+
+
+ {
+ setGroupingMethod(value);
+ sendAnalytics(
+ "Contents/All",
+ `Change grouping method (${["none", "category", "type"][value + 1]})`
+ );
}}
+ allowEmpty
/>
+
-
- {
- setGroupingMethod(value);
- sendAnalytics(
- "Contents/All",
- `Change grouping method (${["none", "category", "type"][value + 1]})`
- );
+ {hoverable && (
+
+ {
+ toggleKeepInfoVisible();
+ sendAnalytics("Contents/All", `Always ${keepInfoVisible ? "hide" : "show"} info`);
}}
- allowEmpty
/>
+ )}
- {hoverable && (
-
- {
- toggleKeepInfoVisible();
- sendAnalytics("Contents/All", `Always ${keepInfoVisible ? "hide" : "show"} info`);
- }}
- />
-
- )}
-
- {
- setSearchName(DEFAULT_FILTERS_STATE.searchName);
- setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
- setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
- sendAnalytics("Contents/All", "Reset all filters");
- }}
- />
-
- ),
- [
- groupingMethod,
- hoverable,
- keepInfoVisible,
- langui.always_show_info,
- langui.category,
- langui.contents,
- langui.contents_description,
- langui.group_by,
- langui.reset_all_filters,
- langui.search_title,
- langui.switch_to_folder_view,
- langui.type,
- searchName,
- setKeepInfoVisible,
- toggleKeepInfoVisible,
- ]
+ {
+ setSearchName(DEFAULT_FILTERS_STATE.searchName);
+ setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
+ setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
+ sendAnalytics("Contents/All", "Reset all filters");
+ }}
+ />
+
);
- const contentPanel = useMemo(
- () => (
-
- item.id}
- renderItem={({ item }) => (
- ({
- pre_title: translation.pre_title,
- title: translation.title,
- subtitle: translation.subtitle,
- language: translation.language.data.attributes.code,
- }))}
- fallback={{ title: prettySlug(item.attributes.slug) }}
- thumbnail={item.attributes.thumbnail?.data?.attributes}
- thumbnailAspectRatio="3/2"
- thumbnailForceAspectRatio
- topChips={
- item.attributes.type?.data?.attributes
- ? [
- item.attributes.type.data.attributes.titles?.[0]
- ? item.attributes.type.data.attributes.titles[0]?.title
- : prettySlug(item.attributes.type.data.attributes.slug),
- ]
- : undefined
- }
- bottomChips={item.attributes.categories?.data.map(
- (category) => category.attributes?.short ?? ""
- )}
- keepInfoVisible={keepInfoVisible}
- />
- )}
- className={cJoin(
- "items-end",
- cIf(
- isContentPanelAtLeast4xl,
- "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
- "grid-cols-2 gap-x-3 gap-y-5"
- )
- )}
- groupingFunction={groupingFunction}
- filteringFunction={filteringFunction}
- searchingTerm={searchName}
- searchingBy={(item) =>
- `
+ const contentPanel = (
+
+ item.id}
+ renderItem={({ item }) => (
+ ({
+ pre_title: translation.pre_title,
+ title: translation.title,
+ subtitle: translation.subtitle,
+ language: translation.language.data.attributes.code,
+ }))}
+ fallback={{ title: prettySlug(item.attributes.slug) }}
+ thumbnail={item.attributes.thumbnail?.data?.attributes}
+ thumbnailAspectRatio="3/2"
+ thumbnailForceAspectRatio
+ topChips={
+ item.attributes.type?.data?.attributes
+ ? [
+ item.attributes.type.data.attributes.titles?.[0]
+ ? item.attributes.type.data.attributes.titles[0]?.title
+ : prettySlug(item.attributes.type.data.attributes.slug),
+ ]
+ : undefined
+ }
+ bottomChips={item.attributes.categories?.data.map(
+ (category) => category.attributes?.short ?? ""
+ )}
+ keepInfoVisible={keepInfoVisible}
+ />
+ )}
+ className={cJoin(
+ "items-end",
+ cIf(
+ isContentPanelAtLeast4xl,
+ "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
+ "grid-cols-2 gap-x-3 gap-y-5"
+ )
+ )}
+ groupingFunction={groupingFunction}
+ filteringFunction={filteringFunction}
+ searchingTerm={searchName}
+ searchingBy={(item) =>
+ `
${item.attributes.slug}
${filterDefined(item.attributes.translations)
.map((translation) =>
prettyInlineTitle(translation.pre_title, translation.title, translation.subtitle)
)
.join(" ")}`
- }
- paginationItemPerPage={50}
- />
-
- ),
- [
- isContentPanelAtLeast4xl,
- contents,
- filteringFunction,
- groupingFunction,
- keepInfoVisible,
- searchName,
- ]
+ }
+ paginationItemPerPage={50}
+ />
+
);
return (
diff --git a/src/pages/contents/folder/[slug].tsx b/src/pages/contents/folder/[slug].tsx
index 4eea4f0..5974f34 100644
--- a/src/pages/contents/folder/[slug].tsx
+++ b/src/pages/contents/folder/[slug].tsx
@@ -1,5 +1,4 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
-import { useMemo } from "react";
import naturalCompare from "string-natural-compare";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
@@ -37,150 +36,136 @@ const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Elemen
const langui = useAtomGetter(atoms.localData.langui);
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
-
-
- ),
- [langui.contents, langui.contents_description, langui.switch_to_grid_view]
+
+
);
- const contentPanel = useMemo(
- () => (
-
-
- {folder.parent_folder?.data?.attributes && (
- <>
- {folder.parent_folder.data.attributes.slug === "root" ? (
-
- ) : (
-
({
- language: title.language.data.attributes.code,
- text: title.title,
- }))}
- fallback={{
- text: prettySlug(folder.parent_folder.data.attributes.slug),
- }}
- />
- )}
-
- >
- )}
+ const contentPanel = (
+
+
+ {folder.parent_folder?.data?.attributes && (
+ <>
+ {folder.parent_folder.data.attributes.slug === "root" ? (
+
+ ) : (
+ ({
+ language: title.language.data.attributes.code,
+ text: title.title,
+ }))}
+ fallback={{
+ text: prettySlug(folder.parent_folder.data.attributes.slug),
+ }}
+ />
+ )}
+
+ >
+ )}
- {folder.slug === "root" ? (
-
- ) : (
- ({
- language: title.language.data.attributes.code,
- text: title.title,
- }))}
- fallback={{
- text: prettySlug(folder.slug),
- }}
- active
- />
- )}
-
+ {folder.slug === "root" ? (
+
+ ) : (
+ ({
+ language: title.language.data.attributes.code,
+ text: title.title,
+ }))}
+ fallback={{
+ text: prettySlug(folder.slug),
+ }}
+ active
+ />
+ )}
+
- item.id}
- renderItem={({ item }) => (
- ({
- title: title.title,
- language: title.language.data.attributes.code,
- }))}
- fallback={{ title: prettySlug(item.attributes.slug) }}
- />
- )}
- className={cJoin(
- "items-end",
- cIf(
- isContentPanelAtLeast4xl,
- "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
- "grid-cols-2 gap-4"
- )
- )}
- renderWhenEmpty={() => <>>}
- groupingFunction={() => [langui.folders ?? "Folders"]}
- />
-
- item.id}
- renderItem={({ item }) => (
- ({
- pre_title: translation.pre_title,
- title: translation.title,
- subtitle: translation.subtitle,
- language: translation.language.data.attributes.code,
- }))}
- fallback={{ title: prettySlug(item.attributes.slug) }}
- thumbnail={item.attributes.thumbnail?.data?.attributes}
- thumbnailAspectRatio="3/2"
- thumbnailForceAspectRatio
- topChips={
- item.attributes.type?.data?.attributes
- ? [
- item.attributes.type.data.attributes.titles?.[0]
- ? item.attributes.type.data.attributes.titles[0]?.title
- : prettySlug(item.attributes.type.data.attributes.slug),
- ]
- : undefined
- }
- bottomChips={item.attributes.categories?.data.map(
- (category) => category.attributes?.short ?? ""
- )}
- keepInfoVisible
- />
- )}
- className={cIf(
+ item.id}
+ renderItem={({ item }) => (
+ ({
+ title: title.title,
+ language: title.language.data.attributes.code,
+ }))}
+ fallback={{ title: prettySlug(item.attributes.slug) }}
+ />
+ )}
+ className={cJoin(
+ "items-end",
+ cIf(
isContentPanelAtLeast4xl,
"grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
- "grid-cols-2 gap-x-3 gap-y-5"
- )}
- renderWhenEmpty={() => <>>}
- groupingFunction={() => [langui.contents ?? "Contents"]}
- />
-
- {folder.contents?.data.length === 0 && folder.subfolders?.data.length === 0 && (
-
+ "grid-cols-2 gap-4"
+ )
)}
-
- ),
- [
- folder.contents?.data,
- folder.parent_folder?.data?.attributes,
- folder.slug,
- folder.subfolders?.data,
- folder.titles,
- isContentPanelAtLeast4xl,
- langui,
- ]
+ renderWhenEmpty={() => <>>}
+ groupingFunction={() => [langui.folders ?? "Folders"]}
+ />
+
+
item.id}
+ renderItem={({ item }) => (
+ ({
+ pre_title: translation.pre_title,
+ title: translation.title,
+ subtitle: translation.subtitle,
+ language: translation.language.data.attributes.code,
+ }))}
+ fallback={{ title: prettySlug(item.attributes.slug) }}
+ thumbnail={item.attributes.thumbnail?.data?.attributes}
+ thumbnailAspectRatio="3/2"
+ thumbnailForceAspectRatio
+ topChips={
+ item.attributes.type?.data?.attributes
+ ? [
+ item.attributes.type.data.attributes.titles?.[0]
+ ? item.attributes.type.data.attributes.titles[0]?.title
+ : prettySlug(item.attributes.type.data.attributes.slug),
+ ]
+ : undefined
+ }
+ bottomChips={item.attributes.categories?.data.map(
+ (category) => category.attributes?.short ?? ""
+ )}
+ keepInfoVisible
+ />
+ )}
+ className={cIf(
+ isContentPanelAtLeast4xl,
+ "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
+ "grid-cols-2 gap-x-3 gap-y-5"
+ )}
+ renderWhenEmpty={() => <>>}
+ groupingFunction={() => [langui.contents ?? "Contents"]}
+ />
+
+ {folder.contents?.data.length === 0 && folder.subfolders?.data.length === 0 && (
+
+ )}
+
);
return (
diff --git a/src/pages/dev/checkup/contents.tsx b/src/pages/dev/checkup/contents.tsx
index 5758208..30df295 100644
--- a/src/pages/dev/checkup/contents.tsx
+++ b/src/pages/dev/checkup/contents.tsx
@@ -1,5 +1,4 @@
import { GetStaticProps } from "next";
-import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Chip } from "components/Chip";
import { Button } from "components/Inputs/Button";
@@ -23,56 +22,53 @@ interface Props extends AppLayoutRequired {
}
const CheckupContents = ({ contents, ...otherProps }: Props): JSX.Element => {
- const testReport = useMemo(() => testingContent(contents), [contents]);
+ const testReport = testingContent(contents);
- const contentPanel = useMemo(
- () => (
-
- {{testReport.title} }
+ const contentPanel = (
+
+ {{testReport.title} }
-
-
-
-
Ref
-
Name
-
Type
-
Severity
-
Description
-
+
+
+
+
Ref
+
Name
+
Type
+
Severity
+
Description
+
- {testReport.lines
- .sort((a, b) => a.name.localeCompare(b.name))
- .sort((a, b) => b.severity - a.severity)
- .map((line, index) => (
-
-
-
-
{line.subitems.join(" -> ")}
-
{line.name}
-
-
-
- {line.description}
-
-
- ))}
-
- ),
- [testReport.lines, testReport.title]
+
+
+ {line.subitems.join(" -> ")}
+ {line.name}
+
+
+
+ {line.description}
+
+
+ ))}
+
);
return
;
diff --git a/src/pages/dev/checkup/libraryitems.tsx b/src/pages/dev/checkup/libraryitems.tsx
index 38ab8b1..d43315f 100644
--- a/src/pages/dev/checkup/libraryitems.tsx
+++ b/src/pages/dev/checkup/libraryitems.tsx
@@ -1,5 +1,4 @@
import { GetStaticProps } from "next";
-import { useMemo } from "react";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Chip } from "components/Chip";
import { Button } from "components/Inputs/Button";
@@ -27,54 +26,51 @@ interface Props extends AppLayoutRequired {
const CheckupLibraryItems = ({ libraryItems, ...otherProps }: Props): JSX.Element => {
const testReport = testingLibraryItem(libraryItems);
- const contentPanel = useMemo(
- () => (
-
- {{testReport.title} }
+ const contentPanel = (
+
+ {{testReport.title} }
-
-
-
-
Ref
-
Name
-
Type
-
Severity
-
Description
-
+
+
+
+
Ref
+
Name
+
Type
+
Severity
+
Description
+
- {testReport.lines
- .sort((a, b) => a.name.localeCompare(b.name))
- .sort((a, b) => b.severity - a.severity)
- .map((line, index) => (
-
-
-
-
{line.subitems.join(" -> ")}
-
{line.name}
-
-
-
- {line.description}
-
-
- ))}
-
- ),
- [testReport.lines, testReport.title]
+
+
+ {line.subitems.join(" -> ")}
+ {line.name}
+
+
+
+ {line.description}
+
+
+ ))}
+
);
return ;
diff --git a/src/pages/dev/editor.tsx b/src/pages/dev/editor.tsx
index 61f023b..48e2f95 100644
--- a/src/pages/dev/editor.tsx
+++ b/src/pages/dev/editor.tsx
@@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
-import { useCallback, useMemo, useRef, useState } from "react";
+import { useCallback, useRef, useState } from "react";
import TurndownService from "turndown";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Button } from "components/Inputs/Button";
@@ -156,250 +156,243 @@ const Editor = (props: Props): JSX.Element => {
[transformationWrapper]
);
- const contentPanel = useMemo(
- () => (
-
- setConverterOpened(false)}>
-
-
Convert HTML to markdown
-
- Copy and paste any HTML content (content from web pages) here.
-
- The text will immediatly be converted to valid Markdown.
-
- You can then copy the converted text and paste it anywhere you want in the editor
-
-
-
-
-
-
- Headers
- preline("# ")} text={"H1"} />
- preline("## ")} text={"H2"} />
- preline("### ")} text={"H3"} />
- preline("#### ")} text={"H4"} />
- preline("##### ")} text={"H5"} />
- preline("###### ")} text={"H6"} />
-
- }>
-
-
-
- Toggle Bold}>
- toggleWrap("**")} icon={Icon.FormatBold} />
-
-
- Toggle Italic}>
- toggleWrap("_")} icon={Icon.FormatItalic} />
-
-
-
- Toggle Inline Code
-
- Makes the text monospace (like text from a computer terminal). Usually used for
- stylistic purposes in transcripts.
-
- >
- }>
- toggleWrap("`")} icon={Icon.Code} />
-
-
-
- Insert footnote
- When inserted “x”
- >
- }>
- {
- insert("[^x]");
- appendDoc("\n\n[^x]: This is a footnote.");
- }}
- icon={Icon.Superscript}
- />
-
-
-
- Transcripts
-
- Use this to create dialogues and transcripts. Start by adding a container, then
- add transcript speech line within.
-
-
-
- Transcript container
- >
- }>
- wrap("Transcript", {}, true)} icon={Icon.AddBox} />
-
-
- Transcript speech line
-
- Use to add a dialogue/transcript line. Change the name property
- to chang the name of the speaker
-
- >
- }>
- wrap("Line", { name: "speaker" })}
- icon={Icon.RecordVoiceOver}
- />
-
-
- >
- }>
-
-
-
- Inset box}>
- wrap("InsetBox", {}, true)} icon={Icon.CheckBoxOutlineBlank} />
-
- Scene break}>
- insert("\n* * *\n")} icon={Icon.MoreHoriz} />
-
-
- Links
-
- External Link
- Provides a link to another webpage / website
- >
- }>
- insert("[Link name](https://domain.com)")}
- icon={Icon.Link}
- text={"External"}
- />
-
-
-
- Intralink
-
- Interlinks are used to add links to a header within the same document
-
- >
- }>
- wrap("IntraLink", {})}
- icon={Icon.Link}
- text={"Internal"}
- />
-
-
- Intralink (with target) {" "}
-
- Use this one if you want the intralink text to be different from the target
- header’s name.
-
- >
- }>
- wrap("IntraLink", { target: "target" })}
- icon={Icon.Link}
- text="Internal (w/ target)"
- />
-
-
- }>
-
-
-
- Player’s name placeholder}>
- insert("@player")} icon={Icon.Person} />
-
-
- Open HTML Converter}>
- {
- setConverterOpened(true);
- }}
- icon={Icon.Html}
- />
-
+ const contentPanel = (
+
+ setConverterOpened(false)}>
+
+
Convert HTML to markdown
+
+ Copy and paste any HTML content (content from web pages) here.
+
+ The text will immediatly be converted to valid Markdown.
+
+ You can then copy the converted text and paste it anywhere you want in the editor
+
+
);
return ;
diff --git a/src/pages/library/[slug]/index.tsx b/src/pages/library/[slug]/index.tsx
index 4c53865..080330c 100644
--- a/src/pages/library/[slug]/index.tsx
+++ b/src/pages/library/[slug]/index.tsx
@@ -1,4 +1,4 @@
-import { Fragment, useCallback, useMemo } from "react";
+import { Fragment, useCallback } from "react";
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import { useRouter } from "next/router";
import { useBoolean } from "usehooks-ts";
@@ -88,511 +88,467 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
useScrollTopOnChange(Ids.ContentPanel, [item]);
const currentIntersection = useIntersectionList(intersectionIds);
- const isVariantSet = useMemo(
- () =>
- item.metadata?.[0]?.__typename === "ComponentMetadataGroup" &&
- item.metadata[0].subtype?.data?.attributes?.slug === "variant-set",
- [item.metadata]
+ const isVariantSet =
+ item.metadata?.[0]?.__typename === "ComponentMetadataGroup" &&
+ item.metadata[0].subtype?.data?.attributes?.slug === "variant-set";
+
+ const displayOpenScans = item.contents?.data.some(
+ (content) => content.attributes?.scan_set && content.attributes.scan_set.length > 0
);
- const displayOpenScans = useMemo(
- () =>
- item.contents?.data.some(
- (content) => content.attributes?.scan_set && content.attributes.scan_set.length > 0
- ),
- [item.contents?.data]
+ const subPanel = (
+
+
+
+
+
+
+
+
+ {item.gallery && item.gallery.data.length > 0 && (
+
+ )}
+
+
+
+ {item.subitems && item.subitems.data.length > 0 && (
+
+ )}
+
+ {item.contents && item.contents.data.length > 0 && (
+
+ )}
+
+
);
- const subPanel = useMemo(
- () => (
-
-
-
-
-
-
-
-
- {item.gallery && item.gallery.data.length > 0 && (
-
- )}
-
-
-
- {item.subitems && item.subitems.data.length > 0 && (
-
- )}
-
- {item.contents && item.contents.data.length > 0 && (
-
+
+
+
+ {item.thumbnail?.data?.attributes ? (
+
{
+ showLightBox([item.thumbnail?.data?.attributes]);
+ }}
/>
+ ) : (
+
)}
-
- ),
- [currentIntersection, isVariantSet, item.contents, item.gallery, item.subitems, langui]
- );
- const contentPanel = useMemo(
- () => (
-
-
-
-
- {item.thumbnail?.data?.attributes ? (
-
{
- showLightBox([item.thumbnail?.data?.attributes]);
- }}
- />
- ) : (
-
+
+
+ {item.subitem_of?.data[0]?.attributes && (
+
+
{langui.subitem_of}
+
+
+ )}
+
+
{item.title}
+ {isDefinedAndNotEmpty(item.subtitle) && {item.subtitle} }
+
+
+ {!isUntangibleGroupItem(item.metadata?.[0]) && isDefinedAndNotEmpty(itemId) && (
+
+ )}
+
+ {item.descriptions?.[0] && (
+
{item.descriptions[0].description}
+ )}
+ {!(
+ item.metadata &&
+ item.metadata[0]?.__typename === "ComponentMetadataGroup" &&
+ (item.metadata[0].subtype?.data?.attributes?.slug === "variant-set" ||
+ item.metadata[0].subtype?.data?.attributes?.slug === "relation-set")
+ ) && (
+ <>
+ {item.urls?.length ? (
+
+
{langui.available_at}
+ {filterHasAttributes(item.urls, ["url"] as const).map((url, index) => (
+
+
+
+ ))}
+
+ ) : (
+
{langui.item_not_available}
+ )}
+ >
)}
+
-
-
- {item.subitem_of?.data[0]?.attributes && (
-
-
{langui.subitem_of}
-
+ {item.gallery && item.gallery.data.length > 0 && (
+
+
{langui.gallery}
+
+ {filterHasAttributes(item.gallery.data, ["id", "attributes"] as const).map(
+ (galleryItem, index) => (
+
+ {
+ showLightBox(
+ filterHasAttributes(item.gallery?.data, ["attributes"] as const).map(
+ (image) => image.attributes
+ ),
+ index
+ );
+ }}>
+
+
+
+ )
+ )}
+
+
+ )}
+
+
+
+
{langui.details}
+
+ {item.metadata?.[0] && (
+
+
{langui.type}
+
+
+ {"›"}
+
+
)}
-
-
{item.title}
- {isDefinedAndNotEmpty(item.subtitle) && (
- {item.subtitle}
- )}
-
- {!isUntangibleGroupItem(item.metadata?.[0]) && isDefinedAndNotEmpty(itemId) && (
-
+ {item.release_date && (
+
+
{langui.release_date}
+
{prettyDate(item.release_date, router.locale)}
+
)}
- {item.descriptions?.[0] && (
-
{item.descriptions[0].description}
- )}
- {!(
- item.metadata &&
- item.metadata[0]?.__typename === "ComponentMetadataGroup" &&
- (item.metadata[0].subtype?.data?.attributes?.slug === "variant-set" ||
- item.metadata[0].subtype?.data?.attributes?.slug === "relation-set")
- ) && (
- <>
- {item.urls?.length ? (
-
-
{langui.available_at}
- {filterHasAttributes(item.urls, ["url"] as const).map((url, index) => (
-
-
-
- ))}
-
- ) : (
-
{langui.item_not_available}
+ {item.price && (
+
+
{langui.price}
+
+ {prettyPrice(
+ item.price,
+ currencies,
+ item.price.currency?.data?.attributes?.code
+ )}
+
+ {item.price.currency?.data?.attributes?.code !== currency && (
+
+ {prettyPrice(item.price, currencies, currency)} (
+ {langui.calculated?.toLowerCase()})
+
)}
- >
+
)}
-
- {item.gallery && item.gallery.data.length > 0 && (
-
-
{langui.gallery}
-
- {filterHasAttributes(item.gallery.data, ["id", "attributes"] as const).map(
- (galleryItem, index) => (
-
- {
- showLightBox(
- filterHasAttributes(item.gallery?.data, ["attributes"] as const).map(
- (image) => image.attributes
- ),
- index
- );
- }}>
-
-
-
- )
- )}
+ {item.categories && item.categories.data.length > 0 && (
+
+
{langui.categories}
+
+ {filterHasAttributes(item.categories.data, ["attributes"] as const).map(
+ (category) => (
+
+ )
+ )}
+
-
- )}
+ )}
-
-
-
{langui.details}
+ {item.size && (
- {item.metadata?.[0] && (
-
-
{langui.type}
-
-
- {"›"}
-
+
{langui.size}
+
+
+
{langui.width}:
+
+
{item.size.width} mm
+
{convertMmToInch(item.size.width)} in
- )}
-
- {item.release_date && (
-
-
{langui.release_date}
-
{prettyDate(item.release_date, router.locale)}
-
- )}
-
- {item.price && (
-
-
{langui.price}
-
- {prettyPrice(
- item.price,
- currencies,
- item.price.currency?.data?.attributes?.code
- )}
-
- {item.price.currency?.data?.attributes?.code !== currency && (
-
- {prettyPrice(item.price, currencies, currency)} (
- {langui.calculated?.toLowerCase()})
-
- )}
-
- )}
-
-
- {item.categories && item.categories.data.length > 0 && (
-
-
{langui.categories}
-
- {filterHasAttributes(item.categories.data, ["attributes"] as const).map(
- (category) => (
-
+
+
{langui.height}:
+
+
{item.size.height} mm
+
{convertMmToInch(item.size.height)} in
+
+ {isDefined(item.size.thickness) && (
+
+
{langui.thickness}:
+
+
{item.size.thickness} mm
+
{convertMmToInch(item.size.thickness)} in
+
+
+ )}
- )}
+
+ )}
- {item.size && (
+ {item.metadata?.[0]?.__typename !== "ComponentMetadataGroup" &&
+ item.metadata?.[0]?.__typename !== "ComponentMetadataOther" && (
-
{langui.size}
-
-
-
{langui.width}:
-
-
{item.size.width} mm
-
{convertMmToInch(item.size.width)} in
-
-
-
-
{langui.height}:
-
-
{item.size.height} mm
-
{convertMmToInch(item.size.height)} in
-
-
- {isDefined(item.size.thickness) && (
-
-
{langui.thickness}:
-
-
{item.size.thickness} mm
-
{convertMmToInch(item.size.thickness)} in
+
{langui.type_information}
+
+ {item.metadata?.[0]?.__typename === "ComponentMetadataBooks" && (
+ <>
+
+
{langui.pages}:
+
{item.metadata[0].page_count}
-
+
+
+
{langui.binding}:
+
+ {item.metadata[0].binding_type ===
+ Enum_Componentmetadatabooks_Binding_Type.Paperback
+ ? langui.paperback
+ : item.metadata[0].binding_type ===
+ Enum_Componentmetadatabooks_Binding_Type.Hardcover
+ ? langui.hardcover
+ : ""}
+
+
+
+
+
{langui.page_order}:
+
+ {item.metadata[0].page_order ===
+ Enum_Componentmetadatabooks_Page_Order.LeftToRight
+ ? langui.left_to_right
+ : langui.right_to_left}
+
+
+
+
+
{langui.languages}:
+ {item.metadata[0]?.languages?.data.map((lang) => (
+
{lang.attributes?.name}
+ ))}
+
+ >
)}
)}
+
+
- {item.metadata?.[0]?.__typename !== "ComponentMetadataGroup" &&
- item.metadata?.[0]?.__typename !== "ComponentMetadataOther" && (
-
-
{langui.type_information}
-
- {item.metadata?.[0]?.__typename === "ComponentMetadataBooks" && (
- <>
-
-
{langui.pages}:
-
{item.metadata[0].page_count}
-
+ {item.subitems && item.subitems.data.length > 0 && (
+
+
{isVariantSet ? langui.variants : langui.subitems}
-
-
{langui.binding}:
-
- {item.metadata[0].binding_type ===
- Enum_Componentmetadatabooks_Binding_Type.Paperback
- ? langui.paperback
- : item.metadata[0].binding_type ===
- Enum_Componentmetadatabooks_Binding_Type.Hardcover
- ? langui.hardcover
- : ""}
-
-
+ {hoverable && (
+
+
+
+ )}
-
-
{langui.page_order}:
-
- {item.metadata[0].page_order ===
- Enum_Componentmetadatabooks_Page_Order.LeftToRight
- ? langui.left_to_right
- : langui.right_to_left}
-
-
-
-
-
{langui.languages}:
- {item.metadata[0]?.languages?.data.map((lang) => (
-
{lang.attributes?.name}
- ))}
-
- >
- )}
-
-
- )}
-
-
-
- {item.subitems && item.subitems.data.length > 0 && (
-
-
{isVariantSet ? langui.variants : langui.subitems}
-
- {hoverable && (
-
-
-
- )}
-
-
- {filterHasAttributes(item.subitems.data, ["id", "attributes"] as const).map(
- (subitem) => (
-
- 0 &&
- subitem.attributes.metadata[0]
- ? [prettyItemSubType(subitem.attributes.metadata[0])]
- : []
- }
- bottomChips={subitem.attributes.categories?.data.map(
- (category) => category.attributes?.short ?? ""
- )}
- metadata={{
- releaseDate: subitem.attributes.release_date,
- price: subitem.attributes.price,
- position: "Bottom",
- }}
- infoAppend={
- !isUntangibleGroupItem(subitem.attributes.metadata?.[0]) && (
-
- )
- }
- />
-
- )
- )}
-
-
- )}
-
- {item.contents && item.contents.data.length > 0 && (
-
-
{langui.contents}
- {displayOpenScans && (
-
-
-
- )}
-
- {filterHasAttributes(item.contents.data, ["attributes"] as const).map(
- (rangedContent) => (
-
({
- pre_title: translation.pre_title,
- title: translation.title,
- subtitle: translation.subtitle,
- language: translation.language?.data?.attributes?.code,
- })),
- categories: filterHasAttributes(
- rangedContent.attributes.content.data.attributes.categories?.data,
- ["attributes"]
- ).map((category) => category.attributes.short),
- type:
- rangedContent.attributes.content.data.attributes.type?.data
- ?.attributes?.titles?.[0]?.title ??
- prettySlug(
- rangedContent.attributes.content.data.attributes.type?.data
- ?.attributes?.slug
- ),
- slug: rangedContent.attributes.content.data.attributes.slug,
- }
- : undefined
+ {filterHasAttributes(item.subitems.data, ["id", "attributes"] as const).map(
+ (subitem) => (
+
+ 0 &&
+ subitem.attributes.metadata[0]
+ ? [prettyItemSubType(subitem.attributes.metadata[0])]
+ : []
}
- rangeStart={
- rangedContent.attributes.range[0]?.__typename === "ComponentRangePageRange"
- ? `${rangedContent.attributes.range[0].starting_page}`
- : ""
+ bottomChips={subitem.attributes.categories?.data.map(
+ (category) => category.attributes?.short ?? ""
+ )}
+ metadata={{
+ releaseDate: subitem.attributes.release_date,
+ price: subitem.attributes.price,
+ position: "Bottom",
+ }}
+ infoAppend={
+ !isUntangibleGroupItem(subitem.attributes.metadata?.[0]) && (
+
+ )
}
- slug={rangedContent.attributes.slug}
- parentSlug={item.slug}
- key={rangedContent.id}
- hasScanSet={
- isDefined(rangedContent.attributes.scan_set) &&
- rangedContent.attributes.scan_set.length > 0
- }
- condensed={!isContentPanelAtLeast3xl}
/>
- )
- )}
-
+
+ )
+ )}
- )}
-
-
- ),
- [
- langui,
- isContentPanelAtLeast3xl,
- item.thumbnail?.data?.attributes,
- item.subitem_of?.data,
- item.title,
- item.subtitle,
- item.metadata,
- item.descriptions,
- item.urls,
- item.gallery,
- item.release_date,
- item.price,
- item.categories,
- item.size,
- item.subitems,
- item.contents,
- item.slug,
- itemId,
- router.locale,
- currencies,
- currency,
- isContentPanelAtLeastSm,
- isVariantSet,
- hoverable,
- toggleKeepInfoVisible,
- keepInfoVisible,
- displayOpenScans,
- showLightBox,
- ]
+
+ )}
+
+ {item.contents && item.contents.data.length > 0 && (
+
+
{langui.contents}
+ {displayOpenScans && (
+
+
+
+ )}
+
+ {filterHasAttributes(item.contents.data, ["attributes"] as const).map(
+ (rangedContent) => (
+ ({
+ pre_title: translation.pre_title,
+ title: translation.title,
+ subtitle: translation.subtitle,
+ language: translation.language?.data?.attributes?.code,
+ })),
+ categories: filterHasAttributes(
+ rangedContent.attributes.content.data.attributes.categories?.data,
+ ["attributes"]
+ ).map((category) => category.attributes.short),
+ type:
+ rangedContent.attributes.content.data.attributes.type?.data
+ ?.attributes?.titles?.[0]?.title ??
+ prettySlug(
+ rangedContent.attributes.content.data.attributes.type?.data
+ ?.attributes?.slug
+ ),
+ slug: rangedContent.attributes.content.data.attributes.slug,
+ }
+ : undefined
+ }
+ rangeStart={
+ rangedContent.attributes.range[0]?.__typename === "ComponentRangePageRange"
+ ? `${rangedContent.attributes.range[0].starting_page}`
+ : ""
+ }
+ slug={rangedContent.attributes.slug}
+ parentSlug={item.slug}
+ key={rangedContent.id}
+ hasScanSet={
+ isDefined(rangedContent.attributes.scan_set) &&
+ rangedContent.attributes.scan_set.length > 0
+ }
+ condensed={!isContentPanelAtLeast3xl}
+ />
+ )
+ )}
+
+
+ )}
+
+
);
return
;
diff --git a/src/pages/library/[slug]/reader.tsx b/src/pages/library/[slug]/reader.tsx
index f5c76aa..68f99a7 100644
--- a/src/pages/library/[slug]/reader.tsx
+++ b/src/pages/library/[slug]/reader.tsx
@@ -1,5 +1,5 @@
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
-import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
+import { Fragment, useCallback, useEffect, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import Slider from "rc-slider";
import { useRouter } from "next/router";
@@ -44,6 +44,7 @@ import { useFullscreen } from "hooks/useFullscreen";
import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms";
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
+import { useIsWebkit } from "hooks/useIsWebkit";
const CUSTOM_DARK_DROPSHADOW = `
drop-shadow(0 0 0.5em rgb(var(--theme-color-shade) / 30%))
@@ -112,19 +113,14 @@ const LibrarySlug = ({
is1ColumnLayout ? "single" : "double"
);
const router = useRouter();
+ const isWebkit = useIsWebkit();
const { isFullscreen, toggleFullscreen, requestFullscreen } = useFullscreen(Ids.ContentPanel);
- const effectiveDisplayMode = useMemo(
- () =>
- currentPageIndex === 0 || currentPageIndex === pages.length - 1 ? "single" : displayMode,
- [currentPageIndex, displayMode, pages.length]
- );
+ const effectiveDisplayMode =
+ currentPageIndex === 0 || currentPageIndex === pages.length - 1 ? "single" : displayMode;
- const ajustedSidepagesTotalWidth = useMemo(
- () => pages.length * SIDEPAGES_PAGE_WIDTH * (120 / pageWidth),
- [pageWidth, pages.length]
- );
+ const ajustedSidepagesTotalWidth = pages.length * SIDEPAGES_PAGE_WIDTH * (120 / pageWidth);
const changeCurrentPageIndex = useCallback(
(callbackFn: (current: number) => number) => {
@@ -185,61 +181,39 @@ const LibrarySlug = ({
handlePageNavigation,
]);
- const firstPage = useMemo(
- () =>
- pages[
- effectiveDisplayMode === "double" && currentPageIndex % 2 === 0
- ? currentPageIndex - 1
- : currentPageIndex
- ],
- [currentPageIndex, effectiveDisplayMode, pages]
- );
- const secondPage = useMemo(
- () =>
- pages[
- effectiveDisplayMode === "double" && currentPageIndex % 2 === 0
- ? currentPageIndex
- : currentPageIndex + 1
- ],
- [currentPageIndex, effectiveDisplayMode, pages]
- );
+ const firstPage =
+ pages[
+ effectiveDisplayMode === "double" && currentPageIndex % 2 === 0
+ ? currentPageIndex - 1
+ : currentPageIndex
+ ];
- const leftSidePagesCount = useMemo(
- () =>
- pageOrder === PageOrder.LeftToRight ? currentPageIndex : pages.length - 1 - currentPageIndex,
- [currentPageIndex, pageOrder, pages.length]
- );
+ const secondPage =
+ pages[
+ effectiveDisplayMode === "double" && currentPageIndex % 2 === 0
+ ? currentPageIndex
+ : currentPageIndex + 1
+ ];
- const rightSidePagesCount = useMemo(
- () =>
- pageOrder === PageOrder.LeftToRight ? pages.length - 1 - currentPageIndex : currentPageIndex,
- [currentPageIndex, pageOrder, pages.length]
- );
+ const leftSidePagesCount =
+ pageOrder === PageOrder.LeftToRight ? currentPageIndex : pages.length - 1 - currentPageIndex;
- const leftSidePagesWidth = useMemo(
- () =>
- `${
- pageOrder === PageOrder.LeftToRight
- ? (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
- : ajustedSidepagesTotalWidth -
- (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
- }vmin`,
- [ajustedSidepagesTotalWidth, currentPageIndex, pageOrder, pages.length]
- );
+ const rightSidePagesCount =
+ pageOrder === PageOrder.LeftToRight ? pages.length - 1 - currentPageIndex : currentPageIndex;
- const rightSidePagesWidth = useMemo(
- () =>
- `${
- pageOrder === PageOrder.LeftToRight
- ? ajustedSidepagesTotalWidth -
- (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
- : (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
- }vmin`,
- [ajustedSidepagesTotalWidth, currentPageIndex, pageOrder, pages.length]
- );
+ const leftSidePagesWidth = `${
+ pageOrder === PageOrder.LeftToRight
+ ? (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
+ : ajustedSidepagesTotalWidth - (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
+ }vmin`;
- const leftSideClipPath = useMemo(
- () => `polygon(
+ const rightSidePagesWidth = `${
+ pageOrder === PageOrder.LeftToRight
+ ? ajustedSidepagesTotalWidth - (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
+ : (currentPageIndex / pages.length) * ajustedSidepagesTotalWidth
+ }vmin`;
+
+ const leftSideClipPath = `polygon(
${
isSidePagesEnabled
? `
@@ -265,12 +239,9 @@ const LibrarySlug = ({
: "101% 0%, 101% 100%,"
}
70% 100%
- )`,
- [filterSettings.bookFold, isSidePagesEnabled, leftSidePagesWidth]
- );
+ )`;
- const rightSideClipPath = useMemo(
- () => `polygon(
+ const rightSideClipPath = `polygon(
${
isSidePagesEnabled
? `calc(100% - ${rightSidePagesWidth}) 0%,
@@ -295,48 +266,237 @@ const LibrarySlug = ({
: "-1% 100%, -1% 0%,"
}
30% 0%
- )`,
- [filterSettings.bookFold, isSidePagesEnabled, rightSidePagesWidth]
- );
+ )`;
- const pageHeight = useMemo(
- () => `calc(100vh - ${is1ColumnLayout ? 5 : 4}rem - 3rem)`,
- [is1ColumnLayout]
- );
+ const pageHeight = `calc(100vh - ${is1ColumnLayout ? 5 : 4}rem - 3rem)`;
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+ {!isWebkit && (
-
+ )}
+
-
-
{langui.night_reader}:
+
+
{langui.night_reader}:
+
{
+ let value = 0;
+ if (Array.isArray(event)) {
+ value = event[0];
+ } else {
+ value = event;
+ }
+ setTeint(value / 10);
+ }}
+ />
+
+
+
+
{langui.reading_layout}:
+
changeDisplayMode("single"),
+ },
+ {
+ icon: Icon.AutoStories,
+ tooltip: langui.double_page_view,
+ active: displayMode === "double",
+ onClick: () => changeDisplayMode("double"),
+ },
+ ]}
+ />
+
+
+
+
{langui.quality}:
+
setPageQuality(ImageQuality.Medium),
+ },
+ {
+ text: "HD",
+ active: pageQuality === ImageQuality.Large,
+ onClick: () => setPageQuality(ImageQuality.Large),
+ },
+ ]}
+ />
+
+
+
{
+ resetReaderSettings();
+ setDisplayMode(is1ColumnLayout ? "single" : "double");
+ sendAnalytics("Reader", "Reset all options");
+ }}
+ />
+
+ );
+
+ const contentPanel = (
+
+
+
setCurrentZoom(zoom.state.scale)}
+ panning={{ disabled: currentZoom <= 1, velocityDisabled: false }}
+ doubleClick={{ disabled: true, mode: "reset" }}
+ zoomAnimation={{ size: 0.1 }}
+ velocityAnimation={{ animationTime: 0, equalToMove: true }}>
+
+ {effectiveDisplayMode === "single" ? (
+
+
+
+
currentZoom <= 1 && handlePageNavigation("left")}
+ />
+
currentZoom <= 1 && handlePageNavigation("right")}
+ />
+
+ ) : (
+ <>
+
currentZoom <= 1 && handlePageNavigation("left")}
+ style={{
+ clipPath: leftSideClipPath,
+ }}>
+ {isSidePagesEnabled && (
+
+ )}
+
+
+
+
+
currentZoom <= 1 && handlePageNavigation("right")}
+ style={{
+ clipPath: rightSideClipPath,
+ }}>
+
+ {isSidePagesEnabled && (
+
+ )}
+
+
+
+ >
+ )}
+
+
+
+
+
+
+ {currentPageIndex - 1} / {pages.length - 2}
+
{
let value = 0;
if (Array.isArray(event)) {
@@ -344,317 +504,60 @@ const LibrarySlug = ({
} else {
value = event;
}
- setTeint(value / 10);
+ changeCurrentPageIndex(() => value);
}}
/>
-
-
-
-
{langui.reading_layout}:
-
changeDisplayMode("single"),
- },
- {
- icon: Icon.AutoStories,
- tooltip: langui.double_page_view,
- active: displayMode === "double",
- onClick: () => changeDisplayMode("double"),
- },
- ]}
- />
-
-
-
-
{langui.quality}:
-
setPageQuality(ImageQuality.Medium),
- },
- {
- text: "HD",
- active: pageQuality === ImageQuality.Large,
- onClick: () => setPageQuality(ImageQuality.Large),
- },
- ]}
- />
-
-
-
{
- resetReaderSettings();
- setDisplayMode(is1ColumnLayout ? "single" : "double");
- sendAnalytics("Reader", "Reset all options");
- }}
- />
-
- ),
- [
- langui.item,
- langui.paper_texture,
- langui.book_fold,
- langui.lighting,
- langui.side_pages,
- langui.shadow,
- langui.night_reader,
- langui.reading_layout,
- langui.single_page_view,
- langui.double_page_view,
- langui.quality,
- langui.reset_all_options,
- itemSlug,
- filterSettings.paperTexture,
- filterSettings.bookFold,
- filterSettings.lighting,
- filterSettings.dropShadow,
- filterSettings.teint,
- togglePaperTexture,
- toggleBookFold,
- toggleLighting,
- isSidePagesEnabled,
- toggleIsSidePagesEnabled,
- toggleDropShadow,
- displayMode,
- pageQuality,
- setTeint,
- changeDisplayMode,
- setPageQuality,
- resetReaderSettings,
- is1ColumnLayout,
- ]
- );
-
- const contentPanel = useMemo(
- () => (
-
-
-
setCurrentZoom(zoom.state.scale)}
- panning={{ disabled: currentZoom <= 1, velocityDisabled: false }}
- doubleClick={{ disabled: true, mode: "reset" }}
- zoomAnimation={{ size: 0.1 }}
- velocityAnimation={{ animationTime: 0, equalToMove: true }}>
-
- {effectiveDisplayMode === "single" ? (
-
-
-
-
currentZoom <= 1 && handlePageNavigation("left")}
- />
-
currentZoom <= 1 && handlePageNavigation("right")}
- />
-
- ) : (
- <>
-
currentZoom <= 1 && handlePageNavigation("left")}
- style={{
- clipPath: leftSideClipPath,
- }}>
- {isSidePagesEnabled && (
-
- )}
-
-
-
-
-
currentZoom <= 1 && handlePageNavigation("right")}
- style={{
- clipPath: rightSideClipPath,
- }}>
-
- {isSidePagesEnabled && (
-
- )}
-
-
-
- >
- )}
-
-
-
-
-
-
- {currentPageIndex - 1} / {pages.length - 2}
-
-
{
- let value = 0;
- if (Array.isArray(event)) {
- value = event[0];
- } else {
- value = event;
- }
- changeCurrentPageIndex(() => value);
- }}
+
+
setIsGalleryMode((current) => !current)}
+ size="small"
+ />
+
-
- setIsGalleryMode((current) => !current)}
- size="small"
- />
-
-
-
-
- {item.contents?.data.map((content) => (
-
- {content.attributes?.scan_set?.[0] && (
- {
- const range = content.attributes?.range[0];
- let newPageIndex = index + 1;
- if (range?.__typename === "ComponentRangePageRange") {
- newPageIndex += range.starting_page;
- }
- changeCurrentPageIndex(() => newPageIndex);
- setIsGalleryMode(false);
- }}
- id={content.attributes.slug}
- translations={filterHasAttributes(
- content.attributes.content?.data?.attributes?.translations,
- ["language.data.attributes"] as const
- ).map((translation) => ({
- language: translation.language.data.attributes.code,
- title: prettyInlineTitle(
- translation.pre_title,
- translation.title,
- translation.subtitle
- ),
- }))}
- fallback={{
- title: prettySlug(content.attributes.slug, item.slug),
- }}
- content={content.attributes.content}
- />
- )}
-
- ))}
-
- ),
- [
- is1ColumnLayout,
- currentZoom,
- filterSettings,
- isDarkMode,
- pageHeight,
- effectiveDisplayMode,
- firstPage,
- pageQuality,
- bookType,
- leftSideClipPath,
- isSidePagesEnabled,
- leftSidePagesWidth,
- leftSidePagesCount,
- pageOrder,
- secondPage,
- rightSideClipPath,
- rightSidePagesWidth,
- rightSidePagesCount,
- isGalleryMode,
- currentPageIndex,
- pages.length,
- isFullscreen,
- toggleFullscreen,
- item.contents?.data,
- item.slug,
- handlePageNavigation,
- changeCurrentPageIndex,
- ]
+
+ {item.contents?.data.map((content) => (
+
+ {content.attributes?.scan_set?.[0] && (
+ {
+ const range = content.attributes?.range[0];
+ let newPageIndex = index + 1;
+ if (range?.__typename === "ComponentRangePageRange") {
+ newPageIndex += range.starting_page;
+ }
+ changeCurrentPageIndex(() => newPageIndex);
+ setIsGalleryMode(false);
+ }}
+ id={content.attributes.slug}
+ translations={filterHasAttributes(
+ content.attributes.content?.data?.attributes?.translations,
+ ["language.data.attributes"] as const
+ ).map((translation) => ({
+ language: translation.language.data.attributes.code,
+ title: prettyInlineTitle(
+ translation.pre_title,
+ translation.title,
+ translation.subtitle
+ ),
+ }))}
+ fallback={{
+ title: prettySlug(content.attributes.slug, item.slug),
+ }}
+ content={content.attributes.content}
+ />
+ )}
+
+ ))}
+
+
+
);
return (
@@ -798,9 +701,9 @@ interface PageFiltersProps {
const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
const isDarkMode = useAtomGetter(atoms.settings.darkMode);
- const commonCss = useMemo(
- () => cJoin("absolute inset-0", cIf(page === "right", "[background-position-x:-100%]")),
- [page]
+ const commonCss = cJoin(
+ "absolute inset-0",
+ cIf(page === "right", "[background-position-x:-100%]")
);
return (
@@ -929,10 +832,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
}, []),
});
- const pages = useMemo(
- () => filterHasAttributes(selectedScan?.pages?.data, ["attributes"]),
- [selectedScan]
- );
+ const pages = filterHasAttributes(selectedScan?.pages?.data, ["attributes"]);
return (
<>
diff --git a/src/pages/library/index.tsx b/src/pages/library/index.tsx
index 6476846..45b8a0d 100644
--- a/src/pages/library/index.tsx
+++ b/src/pages/library/index.tsx
@@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
-import { useState, useMemo, useCallback } from "react";
+import { useState, useCallback } from "react";
import { useBoolean } from "usehooks-ts";
import naturalCompare from "string-natural-compare";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
@@ -222,255 +222,222 @@ const Library = ({ items, ...otherProps }: Props): JSX.Element => {
[groupingMethod, langui]
);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
- {
- setSearchName(name);
- if (isDefinedAndNotEmpty(name)) {
- sendAnalytics("Library", "Change search term");
- } else {
- sendAnalytics("Library", "Clear search term");
- }
- }}
- />
+ {
+ setSearchName(name);
+ if (isDefinedAndNotEmpty(name)) {
+ sendAnalytics("Library", "Change search term");
+ } else {
+ sendAnalytics("Library", "Clear search term");
+ }
+ }}
+ />
-
- {
- setGroupingMethod(value);
- sendAnalytics(
- "Library",
- `Change grouping method (${["none", "category", "type", "year"][value + 1]})`
- );
- }}
- allowEmpty
- />
-
-
-
- {
- setSortingMethod(value);
- sendAnalytics(
- "Library",
- `Change sorting method (${["name", "price", "release date"][value]})`
- );
- }}
- />
-
-
-
- {
- toggleShowSubitems();
- sendAnalytics("Library", `${showSubitems ? "Hide" : "Show"} subitems`);
- }}
- />
-
-
-
- {
- toggleShowPrimaryItems();
- sendAnalytics("Library", `${showPrimaryItems ? "Hide" : "Show"} primary items`);
- }}
- />
-
-
-
- {
- toggleShowSecondaryItems();
- sendAnalytics("Library", `${showSecondaryItems ? "Hide" : "Show"} secondary items`);
- }}
- />
-
-
- {hoverable && (
-
- {
- toggleKeepInfoVisible();
- sendAnalytics("Library", `Always ${keepInfoVisible ? "hide" : "show"} info`);
- }}
- />
-
- )}
-
- {
- setFilterUserStatus(LibraryItemUserStatus.Want);
- sendAnalytics("Library", "Set filter status (I want)");
- },
- active: filterUserStatus === LibraryItemUserStatus.Want,
- },
- {
- tooltip: langui.only_display_items_i_have,
- icon: Icon.BackHand,
- onClick: () => {
- setFilterUserStatus(LibraryItemUserStatus.Have);
- sendAnalytics("Library", "Set filter status (I have)");
- },
- active: filterUserStatus === LibraryItemUserStatus.Have,
- },
- {
- tooltip: langui.only_display_unmarked_items,
- icon: Icon.RadioButtonUnchecked,
- onClick: () => {
- setFilterUserStatus(LibraryItemUserStatus.None);
- sendAnalytics("Library", "Set filter status (unmarked)");
- },
- active: filterUserStatus === LibraryItemUserStatus.None,
- },
- {
- tooltip: langui.only_display_unmarked_items,
- text: langui.all,
- onClick: () => {
- setFilterUserStatus(undefined);
- sendAnalytics("Library", "Set filter status (all)");
- },
- active: isUndefined(filterUserStatus),
- },
+
+ {
+ setGroupingMethod(value);
+ sendAnalytics(
+ "Library",
+ `Change grouping method (${["none", "category", "type", "year"][value + 1]})`
+ );
+ }}
+ allowEmpty
/>
+
- {
- setSearchName(DEFAULT_FILTERS_STATE.searchName);
- setShowSubitems(DEFAULT_FILTERS_STATE.showSubitems);
- setShowPrimaryItems(DEFAULT_FILTERS_STATE.showPrimaryItems);
- setShowSecondaryItems(DEFAULT_FILTERS_STATE.showSecondaryItems);
- setSortingMethod(DEFAULT_FILTERS_STATE.sortingMethod);
- setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
- setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
- setFilterUserStatus(DEFAULT_FILTERS_STATE.filterUserStatus);
- sendAnalytics("Library", "Reset all filters");
+
+ {
+ setSortingMethod(value);
+ sendAnalytics(
+ "Library",
+ `Change sorting method (${["name", "price", "release date"][value]})`
+ );
}}
/>
-
- ),
- [
- filterUserStatus,
- groupingMethod,
- hoverable,
- keepInfoVisible,
- langui,
- searchName,
- setKeepInfoVisible,
- setShowPrimaryItems,
- setShowSecondaryItems,
- setShowSubitems,
- showPrimaryItems,
- showSecondaryItems,
- showSubitems,
- sortingMethod,
- toggleKeepInfoVisible,
- toggleShowPrimaryItems,
- toggleShowSecondaryItems,
- toggleShowSubitems,
- ]
+
+
+
+ {
+ toggleShowSubitems();
+ sendAnalytics("Library", `${showSubitems ? "Hide" : "Show"} subitems`);
+ }}
+ />
+
+
+
+ {
+ toggleShowPrimaryItems();
+ sendAnalytics("Library", `${showPrimaryItems ? "Hide" : "Show"} primary items`);
+ }}
+ />
+
+
+
+ {
+ toggleShowSecondaryItems();
+ sendAnalytics("Library", `${showSecondaryItems ? "Hide" : "Show"} secondary items`);
+ }}
+ />
+
+
+ {hoverable && (
+
+ {
+ toggleKeepInfoVisible();
+ sendAnalytics("Library", `Always ${keepInfoVisible ? "hide" : "show"} info`);
+ }}
+ />
+
+ )}
+
+ {
+ setFilterUserStatus(LibraryItemUserStatus.Want);
+ sendAnalytics("Library", "Set filter status (I want)");
+ },
+ active: filterUserStatus === LibraryItemUserStatus.Want,
+ },
+ {
+ tooltip: langui.only_display_items_i_have,
+ icon: Icon.BackHand,
+ onClick: () => {
+ setFilterUserStatus(LibraryItemUserStatus.Have);
+ sendAnalytics("Library", "Set filter status (I have)");
+ },
+ active: filterUserStatus === LibraryItemUserStatus.Have,
+ },
+ {
+ tooltip: langui.only_display_unmarked_items,
+ icon: Icon.RadioButtonUnchecked,
+ onClick: () => {
+ setFilterUserStatus(LibraryItemUserStatus.None);
+ sendAnalytics("Library", "Set filter status (unmarked)");
+ },
+ active: filterUserStatus === LibraryItemUserStatus.None,
+ },
+ {
+ tooltip: langui.only_display_unmarked_items,
+ text: langui.all,
+ onClick: () => {
+ setFilterUserStatus(undefined);
+ sendAnalytics("Library", "Set filter status (all)");
+ },
+ active: isUndefined(filterUserStatus),
+ },
+ ]}
+ />
+
+ {
+ setSearchName(DEFAULT_FILTERS_STATE.searchName);
+ setShowSubitems(DEFAULT_FILTERS_STATE.showSubitems);
+ setShowPrimaryItems(DEFAULT_FILTERS_STATE.showPrimaryItems);
+ setShowSecondaryItems(DEFAULT_FILTERS_STATE.showSecondaryItems);
+ setSortingMethod(DEFAULT_FILTERS_STATE.sortingMethod);
+ setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
+ setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
+ setFilterUserStatus(DEFAULT_FILTERS_STATE.filterUserStatus);
+ sendAnalytics("Library", "Reset all filters");
+ }}
+ />
+
);
- const contentPanel = useMemo(
- () => (
-
- item.id}
- renderItem={({ item }) => (
- 0 &&
- item.attributes.metadata[0]
- ? [prettyItemSubType(item.attributes.metadata[0])]
- : []
- }
- bottomChips={item.attributes.categories?.data.map(
- (category) => category.attributes?.short ?? ""
- )}
- metadata={{
- releaseDate: item.attributes.release_date,
- price: item.attributes.price,
- position: "Bottom",
- }}
- infoAppend={
- !isUntangibleGroupItem(item.attributes.metadata?.[0]) && (
-
- )
- }
- />
- )}
- className={cJoin(
- "grid-cols-2 items-end",
- cIf(isContentPanelAtLeast4xl, "grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]")
- )}
- searchingTerm={searchName}
- sortingFunction={sortingFunction}
- groupingFunction={groupingFunction}
- searchingBy={(item) =>
- prettyInlineTitle("", item.attributes.title, item.attributes.subtitle)
- }
- filteringFunction={filteringFunction}
- paginationItemPerPage={25}
- />
-
- ),
- [
- filteringFunction,
- groupingFunction,
- isContentPanelAtLeast4xl,
- items,
- keepInfoVisible,
- searchName,
- sortingFunction,
- ]
+ const contentPanel = (
+
+ item.id}
+ renderItem={({ item }) => (
+ 0 &&
+ item.attributes.metadata[0]
+ ? [prettyItemSubType(item.attributes.metadata[0])]
+ : []
+ }
+ bottomChips={item.attributes.categories?.data.map(
+ (category) => category.attributes?.short ?? ""
+ )}
+ metadata={{
+ releaseDate: item.attributes.release_date,
+ price: item.attributes.price,
+ position: "Bottom",
+ }}
+ infoAppend={
+ !isUntangibleGroupItem(item.attributes.metadata?.[0]) && (
+
+ )
+ }
+ />
+ )}
+ className={cJoin(
+ "grid-cols-2 items-end",
+ cIf(isContentPanelAtLeast4xl, "grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]")
+ )}
+ searchingTerm={searchName}
+ sortingFunction={sortingFunction}
+ groupingFunction={groupingFunction}
+ searchingBy={(item) =>
+ prettyInlineTitle("", item.attributes.title, item.attributes.subtitle)
+ }
+ filteringFunction={filteringFunction}
+ paginationItemPerPage={25}
+ />
+
);
return (
diff --git a/src/pages/news/index.tsx b/src/pages/news/index.tsx
index 9eb8523..344df0f 100644
--- a/src/pages/news/index.tsx
+++ b/src/pages/news/index.tsx
@@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
-import { useMemo, useState } from "react";
+import { useState } from "react";
import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { Switch } from "components/Inputs/Switch";
@@ -58,101 +58,95 @@ const News = ({ posts, ...otherProps }: Props): JSX.Element => {
} = useBoolean(DEFAULT_FILTERS_STATE.keepInfoVisible);
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
- {
- setSearchName(name);
- if (isDefinedAndNotEmpty(name)) {
- sendAnalytics("News", "Change search term");
- } else {
- sendAnalytics("News", "Clear search term");
- }
- }}
- />
+ {
+ setSearchName(name);
+ if (isDefinedAndNotEmpty(name)) {
+ sendAnalytics("News", "Change search term");
+ } else {
+ sendAnalytics("News", "Clear search term");
+ }
+ }}
+ />
- {hoverable && (
-
- {
- toggleKeepInfoVisible();
- sendAnalytics("News", `Always ${keepInfoVisible ? "hide" : "show"} info`);
- }}
- />
-
- )}
+ {hoverable && (
+
+ {
+ toggleKeepInfoVisible();
+ sendAnalytics("News", `Always ${keepInfoVisible ? "hide" : "show"} info`);
+ }}
+ />
+
+ )}
- {
- setSearchName(DEFAULT_FILTERS_STATE.searchName);
- setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
- sendAnalytics("News", "Reset all filters");
- }}
- />
-
- ),
- [hoverable, keepInfoVisible, langui, searchName, setKeepInfoVisible, toggleKeepInfoVisible]
+ {
+ setSearchName(DEFAULT_FILTERS_STATE.searchName);
+ setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
+ sendAnalytics("News", "Reset all filters");
+ }}
+ />
+
);
- const contentPanel = useMemo(
- () => (
-
- post.id}
- renderItem={({ item: post }) => (
- ({
- language: translation.language.data.attributes.code,
- title: translation.title,
- description: translation.excerpt,
- }))}
- fallback={{ title: prettySlug(post.attributes.slug) }}
- thumbnail={post.attributes.thumbnail?.data?.attributes}
- thumbnailAspectRatio="3/2"
- thumbnailForceAspectRatio
- bottomChips={post.attributes.categories?.data.map(
- (category) => category.attributes?.short ?? ""
- )}
- keepInfoVisible={keepInfoVisible}
- metadata={{
- releaseDate: post.attributes.date,
- releaseDateFormat: "long",
- position: "Top",
- }}
- />
- )}
- className={cIf(
- isContentPanelAtLeast4xl,
- "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
- "grid-cols-2 gap-x-4 gap-y-6"
- )}
- searchingTerm={searchName}
- searchingBy={(post) =>
- `${prettySlug(post.attributes.slug)} ${post.attributes.translations
- ?.map((translation) => translation?.title)
- .join(" ")}`
- }
- paginationItemPerPage={25}
- />
-
- ),
- [keepInfoVisible, posts, searchName, isContentPanelAtLeast4xl]
+ const contentPanel = (
+
+ post.id}
+ renderItem={({ item: post }) => (
+ ({
+ language: translation.language.data.attributes.code,
+ title: translation.title,
+ description: translation.excerpt,
+ }))}
+ fallback={{ title: prettySlug(post.attributes.slug) }}
+ thumbnail={post.attributes.thumbnail?.data?.attributes}
+ thumbnailAspectRatio="3/2"
+ thumbnailForceAspectRatio
+ bottomChips={post.attributes.categories?.data.map(
+ (category) => category.attributes?.short ?? ""
+ )}
+ keepInfoVisible={keepInfoVisible}
+ metadata={{
+ releaseDate: post.attributes.date,
+ releaseDateFormat: "long",
+ position: "Top",
+ }}
+ />
+ )}
+ className={cIf(
+ isContentPanelAtLeast4xl,
+ "grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] gap-x-6 gap-y-8",
+ "grid-cols-2 gap-x-4 gap-y-6"
+ )}
+ searchingTerm={searchName}
+ searchingBy={(post) =>
+ `${prettySlug(post.attributes.slug)} ${post.attributes.translations
+ ?.map((translation) => translation?.title)
+ .join(" ")}`
+ }
+ paginationItemPerPage={25}
+ />
+
);
if (isTerminalMode) {
diff --git a/src/pages/wiki/[slug]/index.tsx b/src/pages/wiki/[slug]/index.tsx
index 3781a76..3c7508e 100644
--- a/src/pages/wiki/[slug]/index.tsx
+++ b/src/pages/wiki/[slug]/index.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useMemo } from "react";
+import { useCallback } from "react";
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
import { useRouter } from "next/router";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
@@ -49,146 +49,126 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
});
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
- const subPanel = useMemo(
- () => (
-
-
-
- ),
- [langui]
+ const subPanel = (
+
+
+
);
- const contentPanel = useMemo(
- () => (
-
-
+ const contentPanel = (
+
+
-
-
{selectedTranslation?.title}
- {selectedTranslation?.aliases && selectedTranslation.aliases.length > 0 && (
-
- {`(${selectedTranslation.aliases.map((alias) => alias?.alias).join("・")})`}
-
- )}
-
-
-
- {selectedTranslation && (
- <>
-
-
-
- {page.thumbnail?.data?.attributes && (
-
{
- if (page.thumbnail?.data?.attributes) {
- showLightBox([page.thumbnail.data.attributes]);
- }
- }}
- />
- )}
-
- {page.categories?.data && page.categories.data.length > 0 && (
- <>
-
{langui.categories}
-
-
- {filterHasAttributes(page.categories.data, ["attributes"] as const).map(
- (category) => (
-
- )
- )}
-
- >
- )}
-
- {page.tags?.data && page.tags.data.length > 0 && (
- <>
-
{langui.tags}
-
- {filterHasAttributes(page.tags.data, ["attributes"] as const).map((tag) => (
-
- ))}
-
- >
- )}
-
-
-
- {isDefinedAndNotEmpty(selectedTranslation.summary) && (
-
-
{langui.summary}
-
{selectedTranslation.summary}
-
- )}
-
- {filterHasAttributes(page.definitions, ["translations"] as const).map(
- (definition, index) => (
-
- ({
- language: translation?.language?.data?.attributes?.code,
- definition: translation?.definition,
- status: translation?.status,
- }))}
- index={index + 1}
- categories={filterHasAttributes(definition.categories?.data, [
- "attributes",
- ] as const).map((category) => category.attributes.short)}
- />
-
- )
- )}
-
- >
+
+
{selectedTranslation?.title}
+ {selectedTranslation?.aliases && selectedTranslation.aliases.length > 0 && (
+
+ {`(${selectedTranslation.aliases.map((alias) => alias?.alias).join("・")})`}
+
)}
-
- ),
- [
- LanguageSwitcher,
- is3ColumnsLayout,
- languageSwitcherProps,
- langui.categories,
- langui.summary,
- langui.tags,
- langui.wiki,
- page.categories?.data,
- page.definitions,
- page.tags?.data,
- page.thumbnail?.data?.attributes,
- selectedTranslation,
- showLightBox,
- ]
+
+
+
+ {selectedTranslation && (
+ <>
+
+
+
+ {page.thumbnail?.data?.attributes && (
+
{
+ if (page.thumbnail?.data?.attributes) {
+ showLightBox([page.thumbnail.data.attributes]);
+ }
+ }}
+ />
+ )}
+
+ {page.categories?.data && page.categories.data.length > 0 && (
+ <>
+
{langui.categories}
+
+
+ {filterHasAttributes(page.categories.data, ["attributes"] as const).map(
+ (category) => (
+
+ )
+ )}
+
+ >
+ )}
+
+ {page.tags?.data && page.tags.data.length > 0 && (
+ <>
+
{langui.tags}
+
+ {filterHasAttributes(page.tags.data, ["attributes"] as const).map((tag) => (
+
+ ))}
+
+ >
+ )}
+
+
+
+ {isDefinedAndNotEmpty(selectedTranslation.summary) && (
+
+
{langui.summary}
+
{selectedTranslation.summary}
+
+ )}
+
+ {filterHasAttributes(page.definitions, ["translations"] as const).map(
+ (definition, index) => (
+
+ ({
+ language: translation?.language?.data?.attributes?.code,
+ definition: translation?.definition,
+ status: translation?.status,
+ }))}
+ index={index + 1}
+ categories={filterHasAttributes(definition.categories?.data, [
+ "attributes",
+ ] as const).map((category) => category.attributes.short)}
+ />
+
+ )
+ )}
+
+ >
+ )}
+
);
if (isTerminalMode) {
diff --git a/src/pages/wiki/chronology.tsx b/src/pages/wiki/chronology.tsx
index d164917..95d2f8e 100644
--- a/src/pages/wiki/chronology.tsx
+++ b/src/pages/wiki/chronology.tsx
@@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
-import { Fragment, useCallback, useMemo } from "react";
+import { Fragment, useCallback } from "react";
import { useRouter } from "next/router";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { InsetBox } from "components/Containers/InsetBox";
@@ -46,80 +46,70 @@ interface Props extends AppLayoutRequired {
const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): JSX.Element => {
const langui = useAtomGetter(atoms.localData.langui);
- const ids = useMemo(
- () =>
- filterHasAttributes(chronologyEras, ["attributes"] as const).map(
- (era) => era.attributes.slug
- ),
- [chronologyEras]
+ const ids = filterHasAttributes(chronologyEras, ["attributes"] as const).map(
+ (era) => era.attributes.slug
);
const currentIntersection = useIntersectionList(ids);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
- {filterHasAttributes(chronologyEras, ["attributes", "id"] as const).map((era, index) => (
-
- ({
- language: translation.language.data.attributes.code,
- title: translation.title,
- subtitle: `${era.attributes.starting_year} → ${era.attributes.ending_year}`,
- }))}
- fallback={{
- title: prettySlug(era.attributes.slug),
- subtitle: `${era.attributes.starting_year} → ${era.attributes.ending_year}`,
- }}
- url={`#${era.attributes.slug}`}
- border
- active={currentIntersection === index}
- />
-
- ))}
-
- ),
- [chronologyEras, currentIntersection, langui]
- );
-
- const contentPanel = useMemo(
- () => (
-
-
-
- {filterHasAttributes(chronologyEras, ["attributes"] as const).map((era) => (
- (
+
+ ({
language: translation.language.data.attributes.code,
title: translation.title,
- description: translation.description,
+ subtitle: `${era.attributes.starting_year} → ${era.attributes.ending_year}`,
}))}
- fallback={{ title: prettySlug(era.attributes.slug) }}
- chronologyItems={filterHasAttributes(chronologyItems, ["attributes"] as const).filter(
- (item) =>
- item.attributes.year >= era.attributes.starting_year &&
- item.attributes.year < era.attributes.ending_year
- )}
+ fallback={{
+ title: prettySlug(era.attributes.slug),
+ subtitle: `${era.attributes.starting_year} → ${era.attributes.ending_year}`,
+ }}
+ url={`#${era.attributes.slug}`}
+ border
+ active={currentIntersection === index}
/>
- ))}
-
- ),
- [chronologyEras, chronologyItems, langui]
+
+ ))}
+
+ );
+
+ const contentPanel = (
+
+
+
+ {filterHasAttributes(chronologyEras, ["attributes"] as const).map((era) => (
+ ({
+ language: translation.language.data.attributes.code,
+ title: translation.title,
+ description: translation.description,
+ }))}
+ fallback={{ title: prettySlug(era.attributes.slug) }}
+ chronologyItems={filterHasAttributes(chronologyItems, ["attributes"] as const).filter(
+ (item) =>
+ item.attributes.year >= era.attributes.starting_year &&
+ item.attributes.year < era.attributes.ending_year
+ )}
+ />
+ ))}
+
);
return ;
@@ -161,7 +151,7 @@ interface ChronologyEraProps {
}
const ChronologyEra = ({ id, title, description, chronologyItems }: ChronologyEraProps) => {
- const yearGroups = useMemo(() => {
+ const yearGroups = (() => {
const memo: Props["chronologyItems"][] = [];
let currentYear = -Infinity;
filterHasAttributes(chronologyItems, ["attributes"] as const).forEach((item) => {
@@ -173,7 +163,7 @@ const ChronologyEra = ({ id, title, description, chronologyItems }: ChronologyEr
}
});
return memo;
- }, [chronologyItems]);
+ })();
return (
diff --git a/src/pages/wiki/index.tsx b/src/pages/wiki/index.tsx
index 86f4b09..33f8163 100644
--- a/src/pages/wiki/index.tsx
+++ b/src/pages/wiki/index.tsx
@@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
-import { useCallback, useMemo, useState } from "react";
+import { useCallback, useState } from "react";
import { useBoolean } from "usehooks-ts";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import { NavOption } from "components/PanelComponents/NavOption";
@@ -67,84 +67,73 @@ const Wiki = ({ pages, ...otherProps }: Props): JSX.Element => {
setValue: setKeepInfoVisible,
} = useBoolean(DEFAULT_FILTERS_STATE.keepInfoVisible);
- const subPanel = useMemo(
- () => (
-
-
+ const subPanel = (
+
+
-
+
- {
- setSearchName(name);
- if (isDefinedAndNotEmpty(name)) {
- sendAnalytics("Wiki", "Change search term");
- } else {
- sendAnalytics("Wiki", "Clear search term");
- }
+ {
+ setSearchName(name);
+ if (isDefinedAndNotEmpty(name)) {
+ sendAnalytics("Wiki", "Change search term");
+ } else {
+ sendAnalytics("Wiki", "Clear search term");
+ }
+ }}
+ />
+
+
+ {
+ setGroupingMethod(value);
+ sendAnalytics("Wiki", `Change grouping method (${["none", "category"][value + 1]})`);
}}
+ allowEmpty
/>
+
-
- {
- setGroupingMethod(value);
- sendAnalytics("Wiki", `Change grouping method (${["none", "category"][value + 1]})`);
+ {hoverable && (
+
+ {
+ toggleKeepInfoVisible();
+ sendAnalytics("Wiki", `Always ${keepInfoVisible ? "hide" : "show"} info`);
}}
- allowEmpty
/>
+ )}
- {hoverable && (
-
- {
- toggleKeepInfoVisible();
- sendAnalytics("Wiki", `Always ${keepInfoVisible ? "hide" : "show"} info`);
- }}
- />
-
- )}
+ {
+ setSearchName(DEFAULT_FILTERS_STATE.searchName);
+ setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
+ setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
+ sendAnalytics("Wiki", "Reset all filters");
+ }}
+ />
- {
- setSearchName(DEFAULT_FILTERS_STATE.searchName);
- setGroupingMethod(DEFAULT_FILTERS_STATE.groupingMethod);
- setKeepInfoVisible(DEFAULT_FILTERS_STATE.keepInfoVisible);
- sendAnalytics("Wiki", "Reset all filters");
- }}
- />
+
-
+ {langui.special_pages}
- {langui.special_pages}
-
-
-
- ),
- [
- groupingMethod,
- hoverable,
- keepInfoVisible,
- langui,
- searchName,
- setKeepInfoVisible,
- toggleKeepInfoVisible,
- ]
+
+
);
const groupingFunction = useCallback(
@@ -172,64 +161,59 @@ const Wiki = ({ pages, ...otherProps }: Props): JSX.Element => {
[groupingMethod, langui]
);
- const contentPanel = useMemo(
- () => (
-
- item.id}
- renderItem={({ item }) => (
- ({
- title: translation.title,
- subtitle:
- translation.aliases && translation.aliases.length > 0
- ? translation.aliases.map((alias) => alias?.alias).join("・")
- : undefined,
- description: translation.summary,
- language: translation.language.data.attributes.code,
- }))}
- fallback={{ title: prettySlug(item.attributes.slug) }}
- thumbnail={item.attributes.thumbnail?.data?.attributes}
- thumbnailAspectRatio={"4/3"}
- thumbnailRounded
- thumbnailForceAspectRatio
- keepInfoVisible={keepInfoVisible}
- topChips={filterHasAttributes(item.attributes.tags?.data, [
- "attributes",
- ] as const).map(
- (tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug)
- )}
- bottomChips={filterHasAttributes(item.attributes.categories?.data, [
- "attributes",
- ] as const).map((category) => category.attributes.short)}
- />
- )}
- className={cIf(
- isContentPanelAtLeast4xl,
- "grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] gap-x-6 gap-y-8",
- "grid-cols-2 gap-x-3 gap-y-5"
- )}
- searchingTerm={searchName}
- searchingBy={(item) =>
- filterDefined(item.attributes.translations)
- .map(
- (translation) =>
- `${translation.title} ${filterDefined(translation.aliases)
- .map((alias) => alias.alias)
- .join(" ")}`
- )
- .join(" ")
- }
- groupingFunction={groupingFunction}
- paginationItemPerPage={25}
- />
-
- ),
- [groupingFunction, keepInfoVisible, pages, searchName, isContentPanelAtLeast4xl]
+ const contentPanel = (
+
+ item.id}
+ renderItem={({ item }) => (
+ ({
+ title: translation.title,
+ subtitle:
+ translation.aliases && translation.aliases.length > 0
+ ? translation.aliases.map((alias) => alias?.alias).join("・")
+ : undefined,
+ description: translation.summary,
+ language: translation.language.data.attributes.code,
+ }))}
+ fallback={{ title: prettySlug(item.attributes.slug) }}
+ thumbnail={item.attributes.thumbnail?.data?.attributes}
+ thumbnailAspectRatio={"4/3"}
+ thumbnailRounded
+ thumbnailForceAspectRatio
+ keepInfoVisible={keepInfoVisible}
+ topChips={filterHasAttributes(item.attributes.tags?.data, ["attributes"] as const).map(
+ (tag) => tag.attributes.titles?.[0]?.title ?? prettySlug(tag.attributes.slug)
+ )}
+ bottomChips={filterHasAttributes(item.attributes.categories?.data, [
+ "attributes",
+ ] as const).map((category) => category.attributes.short)}
+ />
+ )}
+ className={cIf(
+ isContentPanelAtLeast4xl,
+ "grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] gap-x-6 gap-y-8",
+ "grid-cols-2 gap-x-3 gap-y-5"
+ )}
+ searchingTerm={searchName}
+ searchingBy={(item) =>
+ filterDefined(item.attributes.translations)
+ .map(
+ (translation) =>
+ `${translation.title} ${filterDefined(translation.aliases)
+ .map((alias) => alias.alias)
+ .join(" ")}`
+ )
+ .join(" ")
+ }
+ groupingFunction={groupingFunction}
+ paginationItemPerPage={25}
+ />
+
);
if (isTerminalMode) {
diff --git a/tailwind.config.js b/tailwind.config.js
index 9739812..e1ef23d 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -249,6 +249,21 @@ module.exports = {
});
}),
+ /* Webkit fixes */
+ plugin(({ addUtilities }) => {
+ addUtilities({
+ ".webkit-fixes": {
+ "*": {
+ "--tw-drop-shadow": "unset !important",
+ "--tw-shadow": "unset !important",
+ },
+ ".texture-paper-dots": {
+ backgroundImage: "unset !important",
+ },
+ },
+ });
+ }),
+
/* Add support for break-wrods CSS attribute */
plugin(({ addUtilities }) => {
addUtilities({