There's a lot to unpack here
This commit is contained in:
parent
ba13c736b0
commit
ae25df8d72
|
@ -1,2 +1,9 @@
|
||||||
*.js
|
src/graphql/generated.ts
|
||||||
*.ts
|
.eslintrc.js
|
||||||
|
graphql-codegen.config.js
|
||||||
|
next-env.d.ts
|
||||||
|
next-sitemap.config.js
|
||||||
|
next.config.js
|
||||||
|
postcss.config.js
|
||||||
|
tailwind.config.js
|
||||||
|
design.config.js
|
51
.eslintrc.js
51
.eslintrc.js
|
@ -7,6 +7,8 @@ module.exports = {
|
||||||
extends: [
|
extends: [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:import/recommended",
|
||||||
|
"plugin:import/typescript",
|
||||||
"next/core-web-vitals",
|
"next/core-web-vitals",
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
|
@ -122,7 +124,7 @@ module.exports = {
|
||||||
"prefer-exponentiation-operator": "warn",
|
"prefer-exponentiation-operator": "warn",
|
||||||
"prefer-named-capture-group": "warn",
|
"prefer-named-capture-group": "warn",
|
||||||
"prefer-numeric-literals": "warn",
|
"prefer-numeric-literals": "warn",
|
||||||
// "prefer-object-has-own": "warn",
|
"prefer-object-has-own": "warn",
|
||||||
"prefer-object-spread": "warn",
|
"prefer-object-spread": "warn",
|
||||||
"prefer-promise-reject-errors": "warn",
|
"prefer-promise-reject-errors": "warn",
|
||||||
"prefer-regex-literals": "warn",
|
"prefer-regex-literals": "warn",
|
||||||
|
@ -214,5 +216,52 @@ module.exports = {
|
||||||
|
|
||||||
/* NEXTJS */
|
/* NEXTJS */
|
||||||
"@next/next/no-img-element": "off",
|
"@next/next/no-img-element": "off",
|
||||||
|
|
||||||
|
/* IMPORTS */
|
||||||
|
"import/no-unresolved": "error",
|
||||||
|
"import/named": "error",
|
||||||
|
"import/default": "error",
|
||||||
|
"import/namespace": "error",
|
||||||
|
"import/no-restricted-paths": "error",
|
||||||
|
"import/no-absolute-path": "error",
|
||||||
|
"import/no-dynamic-require": "error",
|
||||||
|
// "import/no-internal-modules": "error",
|
||||||
|
"import/no-webpack-loader-syntax": "error",
|
||||||
|
"import/no-self-import": "error",
|
||||||
|
"import/no-cycle": "error",
|
||||||
|
"import/no-useless-path-segments": "error",
|
||||||
|
// "import/no-relative-parent-imports": "error",
|
||||||
|
"import/no-relative-packages": "error",
|
||||||
|
|
||||||
|
"import/export": "error",
|
||||||
|
"import/no-named-as-default": "error",
|
||||||
|
"import/no-named-as-default-member": "error",
|
||||||
|
"import/no-deprecated": "error",
|
||||||
|
"import/no-extraneous-dependencies": "error",
|
||||||
|
"import/no-mutable-exports": "error",
|
||||||
|
"import/no-unused-modules": "error",
|
||||||
|
|
||||||
|
"import/unambiguous": "error",
|
||||||
|
"import/no-commonjs": "error",
|
||||||
|
"import/no-amd": "error",
|
||||||
|
"import/no-nodejs-modules": "error",
|
||||||
|
"import/no-import-module-exports": "error",
|
||||||
|
|
||||||
|
"import/first": "error",
|
||||||
|
// "import/exports-last": "error",
|
||||||
|
"import/no-duplicates": "error",
|
||||||
|
"import/no-namespace": "error",
|
||||||
|
"import/extensions": "error",
|
||||||
|
"import/order": "warn",
|
||||||
|
"import/newline-after-import": "error",
|
||||||
|
// "import/prefer-default-export": "error",
|
||||||
|
// "import/max-dependencies": "error",
|
||||||
|
// "import/no-unassigned-import": "error",
|
||||||
|
"import/no-named-default": "error",
|
||||||
|
// "import/no-default-export": "error",
|
||||||
|
// "import/no-named-export": "error",
|
||||||
|
"import/no-anonymous-default-export": "error",
|
||||||
|
// "import/group-exports": "error",
|
||||||
|
// "import/dynamic-import-chunkname)": "error",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hot-keys": "^2.7.2",
|
"react-hot-keys": "^2.7.2",
|
||||||
"react-swipeable": "^7.0.0",
|
"react-swipeable": "^7.0.0",
|
||||||
|
"tippy.js": "^6.3.7",
|
||||||
"turndown": "^7.1.1"
|
"turndown": "^7.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -38,10 +39,10 @@
|
||||||
"@typescript-eslint/parser": "^5.30.3",
|
"@typescript-eslint/parser": "^5.30.3",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"eslint-config-next": "12.2.0",
|
"eslint-config-next": "12.2.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"graphql": "^16.5.0",
|
"graphql": "^16.5.0",
|
||||||
"next-sitemap": "^3.1.7",
|
"next-sitemap": "^3.1.7",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier-plugin-organize-imports": "^3.0.0",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.1.11",
|
"prettier-plugin-tailwindcss": "^0.1.11",
|
||||||
"tailwindcss": "^3.1.4",
|
"tailwindcss": "^3.1.4",
|
||||||
"ts-unused-exports": "^8.0.0",
|
"ts-unused-exports": "^8.0.0",
|
||||||
|
@ -1336,18 +1337,6 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@graphql-codegen/cli/node_modules/mkdirp": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"mkdirp": "bin/cmd.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@graphql-codegen/cli/node_modules/mute-stream": {
|
"node_modules/@graphql-codegen/cli/node_modules/mute-stream": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||||
|
@ -4414,6 +4403,29 @@
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/encoding": {
|
||||||
|
"version": "0.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||||
|
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": "^0.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/encoding/node_modules/iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
@ -5589,9 +5601,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/https-proxy-agent": {
|
"node_modules/https-proxy-agent": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
|
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agent-base": "6",
|
"agent-base": "6",
|
||||||
|
@ -6780,6 +6792,18 @@
|
||||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
@ -7611,16 +7635,6 @@
|
||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier-plugin-organize-imports": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-juSCJs5TMOqGGPaN/A/1xzWFzRPH2LG1LPLCr64dzKaVnPafJdtgDNmDVlU+8A4LbQzVJg0DTvgA8swBuIUhlg==",
|
|
||||||
"dev": true,
|
|
||||||
"peerDependencies": {
|
|
||||||
"prettier": ">=2.0",
|
|
||||||
"typescript": ">=2.9"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/prettier-plugin-tailwindcss": {
|
"node_modules/prettier-plugin-tailwindcss": {
|
||||||
"version": "0.1.11",
|
"version": "0.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.11.tgz",
|
||||||
|
@ -8067,7 +8081,7 @@
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/scheduler": {
|
"node_modules/scheduler": {
|
||||||
"version": "0.23.0",
|
"version": "0.23.0",
|
||||||
|
@ -8175,6 +8189,15 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
|
@ -8193,15 +8216,6 @@
|
||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/source-map-support/node_modules/source-map": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sponge-case": {
|
"node_modules/sponge-case": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz",
|
||||||
|
@ -10052,12 +10066,6 @@
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mkdirp": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"mute-stream": {
|
"mute-stream": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||||
|
@ -12457,6 +12465,28 @@
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"encoding": {
|
||||||
|
"version": "0.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
||||||
|
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"requires": {
|
||||||
|
"iconv-lite": "^0.6.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"end-of-stream": {
|
"end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
@ -13362,9 +13392,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"https-proxy-agent": {
|
"https-proxy-agent": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
|
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"agent-base": "6",
|
"agent-base": "6",
|
||||||
|
@ -14271,6 +14301,12 @@
|
||||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
@ -14806,13 +14842,6 @@
|
||||||
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prettier-plugin-organize-imports": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-juSCJs5TMOqGGPaN/A/1xzWFzRPH2LG1LPLCr64dzKaVnPafJdtgDNmDVlU+8A4LbQzVJg0DTvgA8swBuIUhlg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"prettier-plugin-tailwindcss": {
|
"prettier-plugin-tailwindcss": {
|
||||||
"version": "0.1.11",
|
"version": "0.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.1.11.tgz",
|
||||||
|
@ -15125,7 +15154,7 @@
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"scheduler": {
|
"scheduler": {
|
||||||
"version": "0.23.0",
|
"version": "0.23.0",
|
||||||
|
@ -15212,6 +15241,12 @@
|
||||||
"integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
|
"integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"source-map-js": {
|
"source-map-js": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
|
@ -15225,14 +15260,6 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"buffer-from": "^1.0.0",
|
"buffer-from": "^1.0.0",
|
||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
||||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sponge-case": {
|
"sponge-case": {
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 12499",
|
"dev": "next dev -p 12499",
|
||||||
"precommit": "npm run prettier && npm run unused-exports && npm run lint && npm run tsc && npm run generate && echo ALL PRECOMMIT CHECKS PASSED SUCCESSFULLY, LET\\'S FUCKING GO!",
|
"precommit": "npm run prettier && npm run unused-exports && npm run eslint && npm run tsc && npm run generate && echo ALL PRECOMMIT CHECKS PASSED SUCCESSFULLY, LET\\'S FUCKING GO!",
|
||||||
"unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport=src/pages --ignoreFiles=generated",
|
"unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport=src/pages --ignoreFiles=generated",
|
||||||
"prebuild": "npm run generate",
|
"prebuild": "npm run generate",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"postbuild": "next-sitemap --config next-sitemap.config.js",
|
"postbuild": "next-sitemap --config next-sitemap.config.js",
|
||||||
"start": "next start -p 12500",
|
"start": "next start -p 12500",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
"eslint": "npx eslint .",
|
||||||
"generate": "graphql-codegen --config graphql-codegen.config.js",
|
"generate": "graphql-codegen --config graphql-codegen.config.js",
|
||||||
"tsc": "tsc",
|
"tsc": "tsc",
|
||||||
"prettier": "prettier --end-of-line auto --write ."
|
"prettier": "prettier --end-of-line auto --write ."
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hot-keys": "^2.7.2",
|
"react-hot-keys": "^2.7.2",
|
||||||
"react-swipeable": "^7.0.0",
|
"react-swipeable": "^7.0.0",
|
||||||
|
"tippy.js": "^6.3.7",
|
||||||
"turndown": "^7.1.1"
|
"turndown": "^7.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -47,10 +49,10 @@
|
||||||
"@typescript-eslint/parser": "^5.30.3",
|
"@typescript-eslint/parser": "^5.30.3",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"eslint-config-next": "12.2.0",
|
"eslint-config-next": "12.2.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"graphql": "^16.5.0",
|
"graphql": "^16.5.0",
|
||||||
"next-sitemap": "^3.1.7",
|
"next-sitemap": "^3.1.7",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier-plugin-organize-imports": "^3.0.0",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.1.11",
|
"prettier-plugin-tailwindcss": "^0.1.11",
|
||||||
"tailwindcss": "^3.1.4",
|
"tailwindcss": "^3.1.4",
|
||||||
"ts-unused-exports": "^8.0.0",
|
"ts-unused-exports": "^8.0.0",
|
||||||
|
|
|
@ -1,19 +1,3 @@
|
||||||
import { Button } from "components/Inputs/Button";
|
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { UploadImageFragment } from "graphql/generated";
|
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
|
||||||
import { cIf, cJoin } from "helpers/className";
|
|
||||||
import { prettyLanguage, prettySlug } from "helpers/formatters";
|
|
||||||
import { getOgImage, ImageQuality } from "helpers/img";
|
|
||||||
import {
|
|
||||||
filterHasAttributes,
|
|
||||||
isDefined,
|
|
||||||
isDefinedAndNotEmpty,
|
|
||||||
isUndefined,
|
|
||||||
iterateMap,
|
|
||||||
} from "helpers/others";
|
|
||||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
|
||||||
import { AnchorIds } from "hooks/useScrollTopOnChange";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useLayoutEffect, useMemo, useState } from "react";
|
import { useEffect, useLayoutEffect, useMemo, useState } from "react";
|
||||||
|
@ -26,6 +10,22 @@ import { TextInput } from "./Inputs/TextInput";
|
||||||
import { ContentPlaceholder } from "./PanelComponents/ContentPlaceholder";
|
import { ContentPlaceholder } from "./PanelComponents/ContentPlaceholder";
|
||||||
import { MainPanel } from "./Panels/MainPanel";
|
import { MainPanel } from "./Panels/MainPanel";
|
||||||
import { Popup } from "./Popup";
|
import { Popup } from "./Popup";
|
||||||
|
import { AnchorIds } from "hooks/useScrollTopOnChange";
|
||||||
|
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||||
|
import {
|
||||||
|
filterHasAttributes,
|
||||||
|
isDefined,
|
||||||
|
isDefinedAndNotEmpty,
|
||||||
|
isUndefined,
|
||||||
|
iterateMap,
|
||||||
|
} from "helpers/others";
|
||||||
|
import { getOgImage, ImageQuality } from "helpers/img";
|
||||||
|
import { prettyLanguage, prettySlug } from "helpers/formatters";
|
||||||
|
import { cIf, cJoin } from "helpers/className";
|
||||||
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
import { UploadImageFragment } from "graphql/generated";
|
||||||
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
|
import { Button } from "components/Inputs/Button";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -406,7 +406,7 @@ export const AppLayout = ({
|
||||||
insertLabels={
|
insertLabels={
|
||||||
new Map([
|
new Map([
|
||||||
[0, langui.primary_language],
|
[0, langui.primary_language],
|
||||||
[1, langui.secondary_language],
|
[1, langui.secondary_language],
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
onChange={(items) => {
|
onChange={(items) => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { cJoin } from "helpers/className";
|
|
||||||
import { MouseEventHandler } from "react";
|
import { MouseEventHandler } from "react";
|
||||||
|
import { cJoin } from "helpers/className";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -31,6 +31,7 @@ export const Ico = ({ onClick, icon, className }: Props): JSX.Element => (
|
||||||
* ─────────────────────────────────────────╯ OTHER ╰─────────────────────────────────────────────
|
* ─────────────────────────────────────────╯ OTHER ╰─────────────────────────────────────────────
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable max-len */
|
||||||
export enum Icon {
|
export enum Icon {
|
||||||
Onek = "1k",
|
Onek = "1k",
|
||||||
OnekPlus = "1k_plus",
|
OnekPlus = "1k_plus",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { UploadImageFragment } from "graphql/generated";
|
|
||||||
import { getAssetURL, getImgSizesByQuality, ImageQuality } from "helpers/img";
|
|
||||||
import { ImageProps } from "next/image";
|
import { ImageProps } from "next/image";
|
||||||
import { MouseEventHandler } from "react";
|
import { MouseEventHandler } from "react";
|
||||||
|
import { UploadImageFragment } from "graphql/generated";
|
||||||
|
import { getAssetURL, getImgSizesByQuality, ImageQuality } from "helpers/img";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import React, { MouseEventHandler } from "react";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
||||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import React, { MouseEventHandler } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -22,6 +22,7 @@ interface Props {
|
||||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
draggable?: boolean;
|
draggable?: boolean;
|
||||||
badgeNumber?: number;
|
badgeNumber?: number;
|
||||||
|
size?: "normal" | "small";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
@ -38,6 +39,7 @@ export const Button = ({
|
||||||
href,
|
href,
|
||||||
locale,
|
locale,
|
||||||
badgeNumber,
|
badgeNumber,
|
||||||
|
size = "normal",
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
@ -70,13 +72,17 @@ export const Button = ({
|
||||||
"!border-black bg-black !text-light drop-shadow-black-lg",
|
"!border-black bg-black !text-light drop-shadow-black-lg",
|
||||||
"cursor-pointer hover:bg-dark hover:text-light hover:drop-shadow-shade-lg"
|
"cursor-pointer hover:bg-dark hover:text-light hover:drop-shadow-shade-lg"
|
||||||
),
|
),
|
||||||
|
cIf(size === "small", "px-3 py-1 text-xs"),
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isDefined(badgeNumber) && (
|
{isDefined(badgeNumber) && (
|
||||||
<div
|
<div
|
||||||
className="absolute -top-3 -right-2 grid h-8 w-8 place-items-center rounded-full
|
className={cJoin(
|
||||||
bg-dark font-bold text-light transition-opacity group-hover:opacity-0"
|
`absolute -top-3 -right-2 grid h-8 w-8 place-items-center
|
||||||
|
rounded-full bg-dark font-bold text-light transition-opacity group-hover:opacity-0`,
|
||||||
|
cIf(size === "small", "-top-2 -right-2 h-5 w-5")
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<p className="-translate-y-[0.05em]">{badgeNumber}</p>
|
<p className="-translate-y-[0.05em]">{badgeNumber}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { Button } from "./Button";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { cJoin } from "helpers/className";
|
import { cJoin } from "helpers/className";
|
||||||
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { Button } from "./Button";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { Fragment } from "react";
|
||||||
|
import { ToolTip } from "../ToolTip";
|
||||||
|
import { Button } from "./Button";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { cJoin } from "helpers/className";
|
import { cJoin } from "helpers/className";
|
||||||
import { prettyLanguage } from "helpers/formatters";
|
import { prettyLanguage } from "helpers/formatters";
|
||||||
import { iterateMap } from "helpers/others";
|
import { iterateMap } from "helpers/others";
|
||||||
import { Fragment } from "react";
|
|
||||||
import { ToolTip } from "../ToolTip";
|
|
||||||
import { Button } from "./Button";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -18,6 +18,7 @@ interface Props {
|
||||||
locales: Map<string, number>;
|
locales: Map<string, number>;
|
||||||
localesIndex: number | undefined;
|
localesIndex: number | undefined;
|
||||||
onLanguageChanged: (index: number) => void;
|
onLanguageChanged: (index: number) => void;
|
||||||
|
size?: Parameters<typeof Button>[0]["size"];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
@ -27,6 +28,7 @@ export const LanguageSwitcher = ({
|
||||||
locales,
|
locales,
|
||||||
localesIndex,
|
localesIndex,
|
||||||
languages,
|
languages,
|
||||||
|
size,
|
||||||
onLanguageChanged,
|
onLanguageChanged,
|
||||||
}: Props): JSX.Element => (
|
}: Props): JSX.Element => (
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
@ -47,6 +49,7 @@ export const LanguageSwitcher = ({
|
||||||
<Button
|
<Button
|
||||||
badgeNumber={locales.size > 1 ? locales.size : undefined}
|
badgeNumber={locales.size > 1 ? locales.size : undefined}
|
||||||
icon={Icon.Translate}
|
icon={Icon.Translate}
|
||||||
|
size={size}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { Fragment, useCallback, useState } from "react";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { isDefinedAndNotEmpty, iterateMap, mapMoveEntry } from "helpers/others";
|
import { isDefinedAndNotEmpty, iterateMap, mapMoveEntry } from "helpers/others";
|
||||||
import { Fragment, useCallback, useState } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
import { ButtonGroup } from "./ButtonGroup";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { cJoin } from "helpers/className";
|
import { cJoin } from "helpers/className";
|
||||||
import { Dispatch, SetStateAction } from "react";
|
|
||||||
import { Button } from "./Button";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -23,19 +23,21 @@ export const PageSelector = ({
|
||||||
maxPage,
|
maxPage,
|
||||||
className,
|
className,
|
||||||
}: Props): JSX.Element => (
|
}: Props): JSX.Element => (
|
||||||
<div className={cJoin("flex flex-row place-content-center", className)}>
|
<ButtonGroup
|
||||||
<Button
|
className={cJoin("flex flex-row place-content-center", className)}
|
||||||
onClick={() => setPage((current) => (page > 0 ? current - 1 : current))}
|
buttonsProps={[
|
||||||
className="rounded-r-none"
|
{
|
||||||
icon={Icon.NavigateBefore}
|
onClick: () => setPage((current) => (page > 0 ? current - 1 : current)),
|
||||||
/>
|
icon: Icon.NavigateBefore,
|
||||||
<Button className="rounded-none border-x-0" text={(page + 1).toString()} />
|
},
|
||||||
<Button
|
{
|
||||||
onClick={() =>
|
text: (page + 1).toString(),
|
||||||
setPage((current) => (page < maxPage ? page + 1 : current))
|
},
|
||||||
}
|
{
|
||||||
className="rounded-l-none"
|
onClick: () =>
|
||||||
icon={Icon.NavigateNext}
|
setPage((current) => (page < maxPage ? page + 1 : current)),
|
||||||
/>
|
icon: Icon.NavigateNext,
|
||||||
</div>
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { Dispatch, Fragment, SetStateAction, useState } from "react";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { useToggle } from "hooks/useToggle";
|
import { useToggle } from "hooks/useToggle";
|
||||||
import { Dispatch, Fragment, SetStateAction, useState } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { useToggle } from "hooks/useToggle";
|
import { useToggle } from "hooks/useToggle";
|
||||||
import { Dispatch, SetStateAction } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { cJoin } from "helpers/className";
|
import { cJoin } from "helpers/className";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { Dispatch, SetStateAction } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Fragment, useCallback, useMemo } from "react";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { Img } from "components/Img";
|
import { Img } from "components/Img";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
|
@ -14,7 +15,6 @@ import {
|
||||||
isDefinedAndNotEmpty,
|
isDefinedAndNotEmpty,
|
||||||
} from "helpers/others";
|
} from "helpers/others";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { Fragment, useCallback, useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -139,7 +139,7 @@ export const ScanSet = ({
|
||||||
<LanguageSwitcher {...languageSwitcherProps} />
|
<LanguageSwitcher {...languageSwitcherProps} />
|
||||||
|
|
||||||
<div className="grid place-content-center place-items-center">
|
<div className="grid place-content-center place-items-center">
|
||||||
<p className="font-headers">{langui.status}:</p>
|
<p className="font-headers font-bold">{langui.status}:</p>
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(selectedScan.status, langui)}
|
content={getStatusDescription(selectedScan.status, langui)}
|
||||||
maxWidth={"20rem"}
|
maxWidth={"20rem"}
|
||||||
|
@ -150,7 +150,8 @@ export const ScanSet = ({
|
||||||
|
|
||||||
{selectedScan.scanners && selectedScan.scanners.data.length > 0 && (
|
{selectedScan.scanners && selectedScan.scanners.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Scanners"}:</p>
|
{/* TODO: Add Scanner to langui */}
|
||||||
|
<p className="font-headers font-bold">{"Scanners"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.scanners.data).map(
|
{filterHasAttributes(selectedScan.scanners.data).map(
|
||||||
(scanner) => (
|
(scanner) => (
|
||||||
|
@ -168,7 +169,8 @@ export const ScanSet = ({
|
||||||
|
|
||||||
{selectedScan.cleaners && selectedScan.cleaners.data.length > 0 && (
|
{selectedScan.cleaners && selectedScan.cleaners.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Cleaners"}:</p>
|
{/* TODO: Add Cleaners to langui */}
|
||||||
|
<p className="font-headers font-bold">{"Cleaners"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.cleaners.data).map(
|
{filterHasAttributes(selectedScan.cleaners.data).map(
|
||||||
(cleaner) => (
|
(cleaner) => (
|
||||||
|
@ -187,7 +189,8 @@ export const ScanSet = ({
|
||||||
{selectedScan.typesetters &&
|
{selectedScan.typesetters &&
|
||||||
selectedScan.typesetters.data.length > 0 && (
|
selectedScan.typesetters.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Typesetters"}:</p>
|
{/* TODO: Add Cleaners to Typesetters */}
|
||||||
|
<p className="font-headers font-bold">{"Typesetters"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.typesetters.data).map(
|
{filterHasAttributes(selectedScan.typesetters.data).map(
|
||||||
(typesetter) => (
|
(typesetter) => (
|
||||||
|
@ -205,6 +208,7 @@ export const ScanSet = ({
|
||||||
|
|
||||||
{isDefinedAndNotEmpty(selectedScan.notes) && (
|
{isDefinedAndNotEmpty(selectedScan.notes) && (
|
||||||
<ToolTip content={selectedScan.notes}>
|
<ToolTip content={selectedScan.notes}>
|
||||||
|
{/* TODO: Add Notes to Typesetters */}
|
||||||
<Chip>{"Notes"}</Chip>
|
<Chip>{"Notes"}</Chip>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Fragment, useCallback, useMemo } from "react";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { Img } from "components/Img";
|
import { Img } from "components/Img";
|
||||||
import { RecorderChip } from "components/RecorderChip";
|
import { RecorderChip } from "components/RecorderChip";
|
||||||
|
@ -10,7 +11,6 @@ import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { Fragment, useCallback, useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -73,9 +73,10 @@ export const ScanSetCover = ({
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className="flex flex-row flex-wrap place-items-center
|
className="flex flex-row flex-wrap place-items-center
|
||||||
gap-6 pt-10 text-base first-of-type:pt-0"
|
gap-6 pt-10 text-base first-of-type:pt-0"
|
||||||
>
|
>
|
||||||
<h2 id="cover" className="text-2xl">
|
<h2 id={"cover"} className="text-2xl">
|
||||||
|
{/* TODO: Add Cover to langui */}
|
||||||
{"Cover"}
|
{"Cover"}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ export const ScanSetCover = ({
|
||||||
<LanguageSwitcher {...languageSwitcherProps} />
|
<LanguageSwitcher {...languageSwitcherProps} />
|
||||||
|
|
||||||
<div className="grid place-content-center place-items-center">
|
<div className="grid place-content-center place-items-center">
|
||||||
<p className="font-headers">{langui.status}:</p>
|
<p className="font-headers font-bold">{langui.status}:</p>
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(selectedScan.status, langui)}
|
content={getStatusDescription(selectedScan.status, langui)}
|
||||||
maxWidth={"20rem"}
|
maxWidth={"20rem"}
|
||||||
|
@ -102,7 +103,8 @@ export const ScanSetCover = ({
|
||||||
|
|
||||||
{selectedScan.scanners && selectedScan.scanners.data.length > 0 && (
|
{selectedScan.scanners && selectedScan.scanners.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Scanners"}:</p>
|
{/* TODO: Add Scanner to langui */}
|
||||||
|
<p className="font-headers font-bold">{"Scanners"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.scanners.data).map(
|
{filterHasAttributes(selectedScan.scanners.data).map(
|
||||||
(scanner) => (
|
(scanner) => (
|
||||||
|
@ -120,7 +122,8 @@ export const ScanSetCover = ({
|
||||||
|
|
||||||
{selectedScan.cleaners && selectedScan.cleaners.data.length > 0 && (
|
{selectedScan.cleaners && selectedScan.cleaners.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Cleaners"}:</p>
|
{/* TODO: Add Cleaners to langui */}
|
||||||
|
<p className="font-headers font-bold">{"Cleaners"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.cleaners.data).map(
|
{filterHasAttributes(selectedScan.cleaners.data).map(
|
||||||
(cleaner) => (
|
(cleaner) => (
|
||||||
|
@ -139,7 +142,8 @@ export const ScanSetCover = ({
|
||||||
{selectedScan.typesetters &&
|
{selectedScan.typesetters &&
|
||||||
selectedScan.typesetters.data.length > 0 && (
|
selectedScan.typesetters.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Typesetters"}:</p>
|
{/* TODO: Add Cleaners to Typesetters */}
|
||||||
|
<p className="font-headers font-bold">{"Typesetters"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(selectedScan.typesetters.data).map(
|
{filterHasAttributes(selectedScan.typesetters.data).map(
|
||||||
(typesetter) => (
|
(typesetter) => (
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import Markdown from "markdown-to-jsx";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import React, { Fragment, useMemo } from "react";
|
||||||
|
import ReactDOMServer from "react-dom/server";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { Img } from "components/Img";
|
import { Img } from "components/Img";
|
||||||
|
@ -10,10 +14,6 @@ import { slugify } from "helpers/formatters";
|
||||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { useLightBox } from "hooks/useLightBox";
|
import { useLightBox } from "hooks/useLightBox";
|
||||||
import Markdown from "markdown-to-jsx";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import React, { Fragment, useMemo } from "react";
|
|
||||||
import ReactDOMServer from "react-dom/server";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -458,7 +458,7 @@ const preprocessMarkDawn = (text: string): string => {
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map((line) => {
|
.map((line) => {
|
||||||
if (line === "* * *" || line === "---") {
|
if (line === "* * *" || line === "---") {
|
||||||
scenebreakIndex += 1;
|
scenebreakIndex++;
|
||||||
return `<SceneBreak id="scene-break-${scenebreakIndex}">`;
|
return `<SceneBreak id="scene-break-${scenebreakIndex}">`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,7 +506,7 @@ const markdawnHeadersParser = (
|
||||||
let index = 2;
|
let index = 2;
|
||||||
while (visitedSlugs.includes(newSlug)) {
|
while (visitedSlugs.includes(newSlug)) {
|
||||||
newSlug = `${slug}-${index}`;
|
newSlug = `${slug}-${index}`;
|
||||||
index += 1;
|
index++;
|
||||||
}
|
}
|
||||||
visitedSlugs.push(newSlug);
|
visitedSlugs.push(newSlug);
|
||||||
return `<h${headerLevel} id="${newSlug}">${lineText}</h${headerLevel}>`;
|
return `<h${headerLevel} id="${newSlug}">${lineText}</h${headerLevel}>`;
|
||||||
|
@ -543,7 +543,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
||||||
slug: getSlug(line),
|
slug: getSlug(line),
|
||||||
children: [],
|
children: [],
|
||||||
});
|
});
|
||||||
h2 += 1;
|
h2++;
|
||||||
h3 = -1;
|
h3 = -1;
|
||||||
h4 = -1;
|
h4 = -1;
|
||||||
h5 = -1;
|
h5 = -1;
|
||||||
|
@ -554,7 +554,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
||||||
slug: getSlug(line),
|
slug: getSlug(line),
|
||||||
children: [],
|
children: [],
|
||||||
});
|
});
|
||||||
h3 += 1;
|
h3++;
|
||||||
h4 = -1;
|
h4 = -1;
|
||||||
h5 = -1;
|
h5 = -1;
|
||||||
scenebreak = 0;
|
scenebreak = 0;
|
||||||
|
@ -564,7 +564,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
||||||
slug: getSlug(line),
|
slug: getSlug(line),
|
||||||
children: [],
|
children: [],
|
||||||
});
|
});
|
||||||
h4 += 1;
|
h4++;
|
||||||
h5 = -1;
|
h5 = -1;
|
||||||
scenebreak = 0;
|
scenebreak = 0;
|
||||||
} else if (h4 >= 0 && line.startsWith("<h5 id=")) {
|
} else if (h4 >= 0 && line.startsWith("<h5 id=")) {
|
||||||
|
@ -573,7 +573,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
||||||
slug: getSlug(line),
|
slug: getSlug(line),
|
||||||
children: [],
|
children: [],
|
||||||
});
|
});
|
||||||
h5 += 1;
|
h5++;
|
||||||
scenebreak = 0;
|
scenebreak = 0;
|
||||||
} else if (h5 >= 0 && line.startsWith("<h6 id=")) {
|
} else if (h5 >= 0 && line.startsWith("<h6 id=")) {
|
||||||
toc.children[h2].children[h3].children[h4].children[h5].children.push({
|
toc.children[h2].children[h3].children[h4].children[h5].children.push({
|
||||||
|
@ -582,8 +582,8 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
||||||
children: [],
|
children: [],
|
||||||
});
|
});
|
||||||
} else if (line.startsWith(`<SceneBreak`)) {
|
} else if (line.startsWith(`<SceneBreak`)) {
|
||||||
scenebreak += 1;
|
scenebreak++;
|
||||||
scenebreakIndex += 1;
|
scenebreakIndex++;
|
||||||
|
|
||||||
const child = {
|
const child = {
|
||||||
title: `Scene break ${scenebreak}`,
|
title: `Scene break ${scenebreak}`,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { MouseEventHandler, useMemo } from "react";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { cJoin, cIf } from "helpers/className";
|
import { cJoin, cIf } from "helpers/className";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { MouseEventHandler, useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import Markdown from "markdown-to-jsx";
|
||||||
|
import Link from "next/link";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { NavOption } from "components/PanelComponents/NavOption";
|
import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
|
@ -6,8 +8,6 @@ import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
import { useMediaDesktop } from "hooks/useMediaQuery";
|
import { useMediaDesktop } from "hooks/useMediaQuery";
|
||||||
import Markdown from "markdown-to-jsx";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { cIf, cJoin } from "helpers/className";
|
|
||||||
import { Dispatch, SetStateAction, useEffect } from "react";
|
import { Dispatch, SetStateAction, useEffect } from "react";
|
||||||
import Hotkeys from "react-hot-keys";
|
import Hotkeys from "react-hot-keys";
|
||||||
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
|
import { cIf, cJoin } from "helpers/className";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
|
||||||
import { getDescription } from "helpers/description";
|
|
||||||
import { prettySlug } from "helpers/formatters";
|
|
||||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
|
||||||
import { PostWithTranslations } from "helpers/types";
|
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
|
||||||
import { Fragment, useCallback, useMemo } from "react";
|
import { Fragment, useCallback, useMemo } from "react";
|
||||||
import { AppLayout } from "./AppLayout";
|
import { AppLayout } from "./AppLayout";
|
||||||
import { Chip } from "./Chip";
|
import { Chip } from "./Chip";
|
||||||
|
@ -15,6 +9,12 @@ import { SubPanel } from "./Panels/SubPanel";
|
||||||
import { RecorderChip } from "./RecorderChip";
|
import { RecorderChip } from "./RecorderChip";
|
||||||
import { ThumbnailHeader } from "./ThumbnailHeader";
|
import { ThumbnailHeader } from "./ThumbnailHeader";
|
||||||
import { ToolTip } from "./ToolTip";
|
import { ToolTip } from "./ToolTip";
|
||||||
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
|
import { PostWithTranslations } from "helpers/types";
|
||||||
|
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||||
|
import { prettySlug } from "helpers/formatters";
|
||||||
|
import { getDescription } from "helpers/description";
|
||||||
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -95,7 +95,7 @@ export const PostPage = ({
|
||||||
<>
|
<>
|
||||||
{selectedTranslation && (
|
{selectedTranslation && (
|
||||||
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
||||||
<p className="font-headers">{langui.status}:</p>
|
<p className="font-headers font-bold">{langui.status}:</p>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(
|
content={getStatusDescription(
|
||||||
|
@ -111,7 +111,7 @@ export const PostPage = ({
|
||||||
|
|
||||||
{post.authors && post.authors.data.length > 0 && (
|
{post.authors && post.authors.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Authors"}:</p>
|
<p className="font-headers font-bold">{"Authors"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(post.authors.data).map((author) => (
|
{filterHasAttributes(post.authors.data).map((author) => (
|
||||||
<Fragment key={author.id}>
|
<Fragment key={author.id}>
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
import { Chip } from "./Chip";
|
||||||
|
import { Ico, Icon } from "./Ico";
|
||||||
|
import { Img } from "./Img";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
import {
|
import {
|
||||||
DatePickerFragment,
|
DatePickerFragment,
|
||||||
|
@ -15,11 +20,6 @@ import {
|
||||||
} from "helpers/formatters";
|
} from "helpers/formatters";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import Link from "next/link";
|
|
||||||
import { useCallback, useMemo } from "react";
|
|
||||||
import { Chip } from "./Chip";
|
|
||||||
import { Ico, Icon } from "./Ico";
|
|
||||||
import { Img } from "./Img";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -212,7 +212,7 @@ export const PreviewCard = ({
|
||||||
>
|
>
|
||||||
<Ico
|
<Ico
|
||||||
icon={Icon.PlayCircleOutline}
|
icon={Icon.PlayCircleOutline}
|
||||||
className="text-6xl opacity-0 transition-opacity group-hover:opacity-100"
|
className="!text-6xl opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -271,7 +271,7 @@ export const PreviewCard = ({
|
||||||
<p className="mb-1 break-words leading-none">{pre_title}</p>
|
<p className="mb-1 break-words leading-none">{pre_title}</p>
|
||||||
)}
|
)}
|
||||||
{title && (
|
{title && (
|
||||||
<p className="break-words font-headers font-bold text-lg leading-none">
|
<p className="break-words font-headers text-lg font-bold leading-none">
|
||||||
{title}
|
{title}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { Chip } from "./Chip";
|
||||||
|
import { Img } from "./Img";
|
||||||
import { UploadImageFragment } from "graphql/generated";
|
import { UploadImageFragment } from "graphql/generated";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { prettySlug } from "helpers/formatters";
|
import { prettySlug } from "helpers/formatters";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import Link from "next/link";
|
|
||||||
import { useCallback } from "react";
|
|
||||||
import { Chip } from "./Chip";
|
|
||||||
import { Img } from "./Img";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -59,7 +59,9 @@ const PreviewLine = ({
|
||||||
<div className="my-1 flex flex-col">
|
<div className="my-1 flex flex-col">
|
||||||
{pre_title && <p className="mb-1 leading-none">{pre_title}</p>}
|
{pre_title && <p className="mb-1 leading-none">{pre_title}</p>}
|
||||||
{title && (
|
{title && (
|
||||||
<p className="font-headers text-lg leading-none">{title}</p>
|
<p className="font-headers text-lg font-bold leading-none">
|
||||||
|
{title}
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
{subtitle && <p className="leading-none">{subtitle}</p>}
|
{subtitle && <p className="leading-none">{subtitle}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
import { Fragment } from "react";
|
||||||
|
import { Img } from "./Img";
|
||||||
|
import { Markdawn } from "./Markdown/Markdawn";
|
||||||
|
import { ToolTip } from "./ToolTip";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { RecorderChipFragment } from "graphql/generated";
|
import { RecorderChipFragment } from "graphql/generated";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
import { filterHasAttributes } from "helpers/others";
|
import { filterHasAttributes } from "helpers/others";
|
||||||
import { Fragment } from "react";
|
|
||||||
import { Img } from "./Img";
|
|
||||||
import { Markdawn } from "./Markdown/Markdawn";
|
|
||||||
import { ToolTip } from "./ToolTip";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { Chip } from "./Chip";
|
||||||
|
import { PageSelector } from "./Inputs/PageSelector";
|
||||||
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
import { cJoin } from "helpers/className";
|
||||||
|
import { isDefined, isDefinedAndNotEmpty, iterateMap } from "helpers/others";
|
||||||
|
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
|
|
||||||
|
interface Props<T> {
|
||||||
|
// Items
|
||||||
|
items: T[];
|
||||||
|
getItemId: (item: T) => string;
|
||||||
|
renderItem: (props: { item: T }) => JSX.Element;
|
||||||
|
renderWhenEmpty?: () => JSX.Element;
|
||||||
|
// Pagination
|
||||||
|
paginationItemPerPage?: number;
|
||||||
|
paginationSelectorTop?: boolean;
|
||||||
|
paginationSelectorBottom?: boolean;
|
||||||
|
paginationScroolTop?: boolean;
|
||||||
|
// Searching
|
||||||
|
searchingTerm?: string;
|
||||||
|
searchingBy?: (item: T) => string;
|
||||||
|
searchingCaseInsensitive?: boolean;
|
||||||
|
// Grouping
|
||||||
|
groupingFunction?: (item: T) => string[];
|
||||||
|
groupSortingFunction?: (a: string, b: string) => number;
|
||||||
|
// Filtering
|
||||||
|
filteringFunction?: (item: T) => boolean;
|
||||||
|
// Sorting
|
||||||
|
sortingFunction?: (a: T, b: T) => number;
|
||||||
|
// Other
|
||||||
|
className?: string;
|
||||||
|
langui: AppStaticProps["langui"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SmartList = <T,>({
|
||||||
|
items,
|
||||||
|
getItemId,
|
||||||
|
renderItem: RenderItem,
|
||||||
|
renderWhenEmpty: RenderWhenEmpty,
|
||||||
|
paginationItemPerPage = 0,
|
||||||
|
paginationSelectorTop = true,
|
||||||
|
paginationSelectorBottom = true,
|
||||||
|
paginationScroolTop = true,
|
||||||
|
searchingTerm,
|
||||||
|
searchingBy,
|
||||||
|
searchingCaseInsensitive = true,
|
||||||
|
groupingFunction = () => [""],
|
||||||
|
groupSortingFunction = (a, b) => a.localeCompare(b),
|
||||||
|
filteringFunction = () => true,
|
||||||
|
sortingFunction = () => 0,
|
||||||
|
className,
|
||||||
|
langui,
|
||||||
|
}: Props<T>): JSX.Element => {
|
||||||
|
const [page, setPage] = useState(0);
|
||||||
|
useScrollTopOnChange(AnchorIds.ContentPanel, [page], paginationScroolTop);
|
||||||
|
|
||||||
|
type Group = Map<string, T[]>;
|
||||||
|
|
||||||
|
useEffect(() => setPage(0), [searchingTerm]);
|
||||||
|
|
||||||
|
const searchFilter = useCallback(() => {
|
||||||
|
if (isDefinedAndNotEmpty(searchingTerm) && isDefined(searchingBy)) {
|
||||||
|
if (searchingCaseInsensitive) {
|
||||||
|
return items.filter((item) =>
|
||||||
|
searchingBy(item).toLowerCase().includes(searchingTerm.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return items.filter((item) => searchingBy(item).includes(searchingTerm));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}, [items, searchingBy, searchingCaseInsensitive, searchingTerm]);
|
||||||
|
|
||||||
|
const filteredItems = useMemo(() => {
|
||||||
|
const filteredBySearch = searchFilter();
|
||||||
|
return filteredBySearch.filter(filteringFunction);
|
||||||
|
}, [filteringFunction, searchFilter]);
|
||||||
|
|
||||||
|
const sortedItem = useMemo(
|
||||||
|
() => filteredItems.sort(sortingFunction),
|
||||||
|
[filteredItems, sortingFunction]
|
||||||
|
);
|
||||||
|
|
||||||
|
const paginatedItems = useMemo(() => {
|
||||||
|
if (paginationItemPerPage > 0) {
|
||||||
|
const memo = [];
|
||||||
|
for (
|
||||||
|
let index = 0;
|
||||||
|
paginationItemPerPage * index < sortedItem.length;
|
||||||
|
index++
|
||||||
|
) {
|
||||||
|
memo.push(
|
||||||
|
sortedItem.slice(
|
||||||
|
index * paginationItemPerPage,
|
||||||
|
(index + 1) * paginationItemPerPage
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return memo;
|
||||||
|
}
|
||||||
|
return [sortedItem];
|
||||||
|
}, [paginationItemPerPage, sortedItem]);
|
||||||
|
|
||||||
|
const groupedList = useMemo(() => {
|
||||||
|
const groups: Group = new Map();
|
||||||
|
paginatedItems[page]?.forEach((item) => {
|
||||||
|
groupingFunction(item).forEach((category) => {
|
||||||
|
if (groups.has(category)) {
|
||||||
|
groups.get(category)?.push(item);
|
||||||
|
} else {
|
||||||
|
groups.set(category, [item]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return groups;
|
||||||
|
}, [groupingFunction, page, paginatedItems]);
|
||||||
|
|
||||||
|
const pageCount = useMemo(
|
||||||
|
() =>
|
||||||
|
paginationItemPerPage > 0
|
||||||
|
? Math.floor(filteredItems.length / paginationItemPerPage)
|
||||||
|
: 1,
|
||||||
|
[paginationItemPerPage, filteredItems.length]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{pageCount > 1 && paginationSelectorTop && (
|
||||||
|
<PageSelector
|
||||||
|
maxPage={pageCount}
|
||||||
|
page={page}
|
||||||
|
setPage={setPage}
|
||||||
|
className="mb-12"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{groupedList.size > 0
|
||||||
|
? iterateMap(
|
||||||
|
groupedList,
|
||||||
|
(name, groupItems) =>
|
||||||
|
groupItems.length > 0 && (
|
||||||
|
<Fragment key={name}>
|
||||||
|
{name.length > 0 && (
|
||||||
|
<h2
|
||||||
|
className="flex flex-row place-items-center gap-2 pb-2 pt-10 text-2xl
|
||||||
|
first-of-type:pt-0"
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
<Chip>{`${groupItems.length} ${
|
||||||
|
groupItems.length <= 1
|
||||||
|
? langui.result?.toLowerCase() ?? ""
|
||||||
|
: langui.results?.toLowerCase() ?? ""
|
||||||
|
}`}</Chip>
|
||||||
|
</h2>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={cJoin(
|
||||||
|
`grid items-start gap-8 border-b-[3px] border-dotted pb-12
|
||||||
|
last-of-type:border-0 mobile:gap-4`,
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{groupItems.map((item) => (
|
||||||
|
<RenderItem item={item} key={getItemId(item)} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
),
|
||||||
|
([a], [b]) => groupSortingFunction(a, b)
|
||||||
|
)
|
||||||
|
: isDefined(RenderWhenEmpty) && <RenderWhenEmpty />}
|
||||||
|
|
||||||
|
{pageCount > 1 && paginationSelectorBottom && (
|
||||||
|
<PageSelector
|
||||||
|
maxPage={pageCount}
|
||||||
|
page={page}
|
||||||
|
setPage={setPage}
|
||||||
|
className="mt-12"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,3 +1,4 @@
|
||||||
|
// eslint-disable-next-line import/named
|
||||||
import Tippy, { TippyProps } from "@tippyjs/react";
|
import Tippy, { TippyProps } from "@tippyjs/react";
|
||||||
import { cJoin } from "helpers/className";
|
import { cJoin } from "helpers/className";
|
||||||
import "tippy.js/animations/scale-subtle.css";
|
import "tippy.js/animations/scale-subtle.css";
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Fragment } from "react";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
|
@ -12,7 +13,10 @@ import {
|
||||||
getStatusDescription,
|
getStatusDescription,
|
||||||
} from "helpers/others";
|
} from "helpers/others";
|
||||||
|
|
||||||
import { Fragment } from "react";
|
/*
|
||||||
|
* ╭─────────────╮
|
||||||
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||||
|
*/
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
item: NonNullable<GetChronologyItemsQuery["chronologyItems"]>["data"][number];
|
item: NonNullable<GetChronologyItemsQuery["chronologyItems"]>["data"][number];
|
||||||
|
@ -20,6 +24,8 @@ interface Props {
|
||||||
langui: AppStaticProps["langui"];
|
langui: AppStaticProps["langui"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const ChronologyItemComponent = ({
|
export const ChronologyItemComponent = ({
|
||||||
langui,
|
langui,
|
||||||
item,
|
item,
|
||||||
|
@ -124,6 +130,11 @@ export const ChronologyItemComponent = ({
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ╭───────────────────╮
|
||||||
|
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
||||||
|
*/
|
||||||
|
|
||||||
const generateAnchor = (
|
const generateAnchor = (
|
||||||
year: number | undefined,
|
year: number | undefined,
|
||||||
month: number | null | undefined,
|
month: number | null | undefined,
|
||||||
|
|
|
@ -2,6 +2,11 @@ import { ChronologyItemComponent } from "components/Wiki/Chronology/ChronologyIt
|
||||||
import { GetChronologyItemsQuery } from "graphql/generated";
|
import { GetChronologyItemsQuery } from "graphql/generated";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ╭─────────────╮
|
||||||
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||||
|
*/
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
year: number;
|
year: number;
|
||||||
items: NonNullable<
|
items: NonNullable<
|
||||||
|
@ -10,6 +15,8 @@ interface Props {
|
||||||
langui: AppStaticProps["langui"];
|
langui: AppStaticProps["langui"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const ChronologyYearComponent = ({
|
export const ChronologyYearComponent = ({
|
||||||
langui,
|
langui,
|
||||||
year,
|
year,
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
|
import { useCallback } from "react";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getStatusDescription } from "helpers/others";
|
import { getStatusDescription } from "helpers/others";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { useCallback } from "react";
|
|
||||||
|
/*
|
||||||
|
* ╭─────────────╮
|
||||||
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||||
|
*/
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
source?: string;
|
source?: string;
|
||||||
|
@ -15,14 +20,18 @@ interface Props {
|
||||||
languages: AppStaticProps["languages"];
|
languages: AppStaticProps["languages"];
|
||||||
langui: AppStaticProps["langui"];
|
langui: AppStaticProps["langui"];
|
||||||
index: number;
|
index: number;
|
||||||
|
categories: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
const DefinitionCard = ({
|
const DefinitionCard = ({
|
||||||
source,
|
source,
|
||||||
translations = [],
|
translations = [],
|
||||||
languages,
|
languages,
|
||||||
langui,
|
langui,
|
||||||
index,
|
index,
|
||||||
|
categories,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] =
|
||||||
useSmartLanguage({
|
useSmartLanguage({
|
||||||
|
@ -36,24 +45,51 @@ const DefinitionCard = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex place-items-center gap-2">
|
<div className="flex flex-wrap place-items-center gap-2">
|
||||||
<p className="font-headers text-lg">{`${langui.definition} ${index}`}</p>
|
<p className="font-headers text-lg font-bold">{`${langui.definition} ${index}`}</p>
|
||||||
{selectedTranslation?.status && (
|
|
||||||
<ToolTip
|
|
||||||
content={getStatusDescription(selectedTranslation.status, langui)}
|
|
||||||
maxWidth={"20rem"}
|
|
||||||
>
|
|
||||||
<Chip>{selectedTranslation.status}</Chip>
|
|
||||||
</ToolTip>
|
|
||||||
)}
|
|
||||||
{translations.length > 1 && (
|
{translations.length > 1 && (
|
||||||
<LanguageSwitcher {...languageSwitcherProps} />
|
<>
|
||||||
|
<Separator />
|
||||||
|
<LanguageSwitcher {...languageSwitcherProps} size={"small"} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedTranslation?.status && (
|
||||||
|
<>
|
||||||
|
<Separator />
|
||||||
|
<ToolTip
|
||||||
|
content={getStatusDescription(selectedTranslation.status, langui)}
|
||||||
|
maxWidth={"20rem"}
|
||||||
|
>
|
||||||
|
<Chip>{selectedTranslation.status}</Chip>
|
||||||
|
</ToolTip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{categories.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Separator />
|
||||||
|
<div className="flex flex-row gap-1">
|
||||||
|
{categories.map((category, categoryIndex) => (
|
||||||
|
<Chip key={categoryIndex}>{category}</Chip>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="italic">{`${langui.source}: ${source}`}</p>
|
<p className="italic">{`${langui.source}: ${source}`}</p>
|
||||||
|
|
||||||
<p>{selectedTranslation?.definition}</p>
|
<p>{selectedTranslation?.definition}</p>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default DefinitionCard;
|
export default DefinitionCard;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ╭──────────────────────╮
|
||||||
|
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Separator = () => <div className="mx-1 h-5 w-[1px] bg-dark" />;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import React, { ReactNode, useContext, useState } from "react";
|
||||||
import { isDefined } from "helpers/others";
|
import { isDefined } from "helpers/others";
|
||||||
import { LibraryItemUserStatus, RequiredNonNullable } from "helpers/types";
|
import { LibraryItemUserStatus, RequiredNonNullable } from "helpers/types";
|
||||||
import { useDarkMode } from "hooks/useDarkMode";
|
import { useDarkMode } from "hooks/useDarkMode";
|
||||||
import { useStateWithLocalStorage } from "hooks/useStateWithLocalStorage";
|
import { useStateWithLocalStorage } from "hooks/useStateWithLocalStorage";
|
||||||
import React, { ReactNode, useContext, useState } from "react";
|
|
||||||
|
|
||||||
export interface AppLayoutState {
|
interface AppLayoutState {
|
||||||
subPanelOpen: boolean | undefined;
|
subPanelOpen: boolean | undefined;
|
||||||
toggleSubPanelOpen: () => void;
|
toggleSubPanelOpen: () => void;
|
||||||
setSubPanelOpen: React.Dispatch<
|
setSubPanelOpen: React.Dispatch<
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { GetStaticPropsContext } from "next";
|
||||||
import {
|
import {
|
||||||
GetCurrenciesQuery,
|
GetCurrenciesQuery,
|
||||||
GetLanguagesQuery,
|
GetLanguagesQuery,
|
||||||
|
@ -5,8 +6,6 @@ import {
|
||||||
} from "graphql/generated";
|
} from "graphql/generated";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
|
|
||||||
import { GetStaticPropsContext } from "next";
|
|
||||||
|
|
||||||
export type AppStaticProps = {
|
export type AppStaticProps = {
|
||||||
langui: NonNullable<
|
langui: NonNullable<
|
||||||
NonNullable<
|
NonNullable<
|
||||||
|
@ -21,7 +20,7 @@ export const getAppStaticProps = async (
|
||||||
context: GetStaticPropsContext
|
context: GetStaticPropsContext
|
||||||
): Promise<AppStaticProps> => {
|
): Promise<AppStaticProps> => {
|
||||||
const sdk = getReadySdk();
|
const sdk = getReadySdk();
|
||||||
const languages = (await sdk.getLanguages()).languages;
|
const { languages } = await sdk.getLanguages();
|
||||||
|
|
||||||
if (languages?.data) {
|
if (languages?.data) {
|
||||||
languages.data.sort((a, b) =>
|
languages.data.sort((a, b) =>
|
||||||
|
@ -31,7 +30,7 @@ export const getAppStaticProps = async (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currencies = (await sdk.getCurrencies()).currencies;
|
const { currencies } = await sdk.getCurrencies();
|
||||||
if (currencies?.data) {
|
if (currencies?.data) {
|
||||||
currencies.data.sort((a, b) =>
|
currencies.data.sort((a, b) =>
|
||||||
a.attributes && b.attributes
|
a.attributes && b.attributes
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { PostWithTranslations } from "helpers/types";
|
import { GetStaticProps } from "next";
|
||||||
import { GetStaticProps, GetStaticPropsContext } from "next";
|
|
||||||
import { AppStaticProps, getAppStaticProps } from "./getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "./getAppStaticProps";
|
||||||
import { getReadySdk } from "./sdk";
|
import { getReadySdk } from "./sdk";
|
||||||
|
import { PostWithTranslations } from "helpers/types";
|
||||||
|
|
||||||
export interface PostStaticProps extends AppStaticProps {
|
export interface PostStaticProps extends AppStaticProps {
|
||||||
post: PostWithTranslations;
|
post: PostWithTranslations;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPostStaticProps = (slug: string): GetStaticProps => {
|
export const getPostStaticProps =
|
||||||
return async (context) => {
|
(slug: string): GetStaticProps =>
|
||||||
|
async (context) => {
|
||||||
const sdk = getReadySdk();
|
const sdk = getReadySdk();
|
||||||
const post = await sdk.getPost({
|
const post = await sdk.getPost({
|
||||||
slug: slug,
|
slug: slug,
|
||||||
|
@ -25,4 +26,3 @@ export const getPostStaticProps = (slug: string): GetStaticProps => {
|
||||||
}
|
}
|
||||||
return { notFound: true };
|
return { notFound: true };
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
query getPostsPreview($language_code: String) {
|
query getPostsPreview {
|
||||||
posts(filters: { hidden: { eq: false } }) {
|
posts(filters: { hidden: { eq: false } }) {
|
||||||
data {
|
data {
|
||||||
id
|
id
|
||||||
|
@ -22,7 +22,14 @@ query getPostsPreview($language_code: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
translations(filters: { language: { code: { eq: $language_code } } }) {
|
translations {
|
||||||
|
language {
|
||||||
|
data {
|
||||||
|
attributes {
|
||||||
|
code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
title
|
title
|
||||||
excerpt
|
excerpt
|
||||||
thumbnail {
|
thumbnail {
|
||||||
|
|
|
@ -77,6 +77,15 @@ query getWikiPage($slug: String, $language_code: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
categories {
|
||||||
|
data {
|
||||||
|
id
|
||||||
|
attributes {
|
||||||
|
name
|
||||||
|
short
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
translations {
|
translations {
|
||||||
language {
|
language {
|
||||||
data {
|
data {
|
||||||
|
@ -85,7 +94,6 @@ query getWikiPage($slug: String, $language_code: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source_language {
|
source_language {
|
||||||
data {
|
data {
|
||||||
attributes {
|
attributes {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { GraphQLClient } from "graphql-request";
|
import { GraphQLClient } from "graphql-request";
|
||||||
import { getSdk } from "graphql/generated";
|
import { getSdk } from "graphql/generated";
|
||||||
|
|
||||||
export const getReadySdk = () => {
|
export const getReadySdk = (): ReturnType<typeof getSdk> => {
|
||||||
const client = new GraphQLClient(process.env.URL_GRAPHQL ?? "", {
|
const client = new GraphQLClient(process.env.URL_GRAPHQL ?? "", {
|
||||||
headers: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}` },
|
headers: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}` },
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
export const cIf = (
|
export const cIf = (
|
||||||
condition: boolean | null | undefined | string,
|
condition: boolean | string | null | undefined,
|
||||||
ifTrueCss: string,
|
ifTrueCss: string,
|
||||||
ifFalseCss?: string
|
ifFalseCss?: string
|
||||||
) => {
|
): string => (condition ? ifTrueCss : ifFalseCss ?? "");
|
||||||
return condition ? ifTrueCss : ifFalseCss ?? "";
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cJoin = (...args: (string | undefined)[]): string => {
|
export const cJoin = (...args: (string | undefined)[]): string =>
|
||||||
return args.filter((elem) => elem).join(" ");
|
args.filter((elem) => elem).join(" ");
|
||||||
};
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
|
||||||
import { prettySlug } from "./formatters";
|
import { prettySlug } from "./formatters";
|
||||||
import { isDefined } from "./others";
|
import { isDefined } from "./others";
|
||||||
import { Content } from "./types";
|
import { Content } from "./types";
|
||||||
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
interface Description {
|
interface Description {
|
||||||
langui: AppStaticProps["langui"];
|
langui: AppStaticProps["langui"];
|
||||||
|
@ -49,13 +49,11 @@ export const getDescription = ({
|
||||||
return description;
|
return description;
|
||||||
};
|
};
|
||||||
|
|
||||||
const prettyMarkdown = (markdown: string): string => {
|
const prettyMarkdown = (markdown: string): string =>
|
||||||
return markdown.replace(/[*]/gu, "").replace(/[_]/gu, "");
|
markdown.replace(/[*]/gu, "").replace(/[_]/gu, "");
|
||||||
};
|
|
||||||
|
|
||||||
const prettyChip = (items: (string | undefined)[]): string => {
|
const prettyChip = (items: (string | undefined)[]): string =>
|
||||||
return items
|
items
|
||||||
.filter((item) => isDefined(item))
|
.filter((item) => isDefined(item))
|
||||||
.map((item) => `(${item})`)
|
.map((item) => `(${item})`)
|
||||||
.join(" ");
|
.join(" ");
|
||||||
};
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { DatePickerFragment, PricePickerFragment } from "graphql/generated";
|
|
||||||
import { AppStaticProps } from "../graphql/getAppStaticProps";
|
import { AppStaticProps } from "../graphql/getAppStaticProps";
|
||||||
import { convertPrice } from "./numbers";
|
import { convertPrice } from "./numbers";
|
||||||
|
import { isDefinedAndNotEmpty } from "./others";
|
||||||
|
import { DatePickerFragment, PricePickerFragment } from "graphql/generated";
|
||||||
|
|
||||||
export const prettyDate = (datePicker: DatePickerFragment): string => {
|
export const prettyDate = (datePicker: DatePickerFragment): string => {
|
||||||
let result = "";
|
let result = "";
|
||||||
|
@ -20,7 +21,7 @@ export const prettyPrice = (
|
||||||
if (!targetCurrencyCode) return "";
|
if (!targetCurrencyCode) return "";
|
||||||
let result = "";
|
let result = "";
|
||||||
currencies.map((currency) => {
|
currencies.map((currency) => {
|
||||||
if (currency?.attributes?.code === targetCurrencyCode) {
|
if (currency.attributes?.code === targetCurrencyCode) {
|
||||||
const amountInTargetCurrency = convertPrice(pricePicker, currency);
|
const amountInTargetCurrency = convertPrice(pricePicker, currency);
|
||||||
result =
|
result =
|
||||||
currency.attributes.symbol +
|
currency.attributes.symbol +
|
||||||
|
@ -34,20 +35,20 @@ export const prettyPrice = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prettySlug = (slug?: string, parentSlug?: string): string => {
|
export const prettySlug = (slug?: string, parentSlug?: string): string => {
|
||||||
if (slug) {
|
let newSlug = slug;
|
||||||
if (parentSlug && slug.startsWith(parentSlug))
|
if (newSlug) {
|
||||||
slug = slug.substring(parentSlug.length + 1);
|
if (isDefinedAndNotEmpty(parentSlug) && newSlug.startsWith(parentSlug))
|
||||||
slug = slug.replace(new RegExp("-", "g"), " ");
|
newSlug = newSlug.substring(parentSlug.length + 1);
|
||||||
slug = slug.replace(new RegExp("_", "g"), " ");
|
newSlug = newSlug.replaceAll("-", " ");
|
||||||
return capitalizeString(slug);
|
return capitalizeString(newSlug);
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prettyinlineTitle = (
|
export const prettyinlineTitle = (
|
||||||
pretitle: string | undefined | null,
|
pretitle: string | null | undefined,
|
||||||
title: string | undefined | null,
|
title: string | null | undefined,
|
||||||
subtitle: string | undefined | null
|
subtitle: string | null | undefined
|
||||||
): string => {
|
): string => {
|
||||||
let result = "";
|
let result = "";
|
||||||
if (pretitle) result += `${pretitle}: `;
|
if (pretitle) result += `${pretitle}: `;
|
||||||
|
@ -59,7 +60,7 @@ export const prettyinlineTitle = (
|
||||||
export const prettyItemType = (
|
export const prettyItemType = (
|
||||||
metadata: any,
|
metadata: any,
|
||||||
langui: AppStaticProps["langui"]
|
langui: AppStaticProps["langui"]
|
||||||
): string | undefined | null => {
|
): string | null | undefined => {
|
||||||
switch (metadata.__typename) {
|
switch (metadata.__typename) {
|
||||||
case "ComponentMetadataAudio":
|
case "ComponentMetadataAudio":
|
||||||
return langui.audio;
|
return langui.audio;
|
||||||
|
@ -78,6 +79,7 @@ export const prettyItemType = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* eslint-disable id-denylist */
|
||||||
export const prettyItemSubType = (
|
export const prettyItemSubType = (
|
||||||
metadata:
|
metadata:
|
||||||
| {
|
| {
|
||||||
|
@ -86,9 +88,11 @@ export const prettyItemSubType = (
|
||||||
data?: {
|
data?: {
|
||||||
attributes?: {
|
attributes?: {
|
||||||
slug: string;
|
slug: string;
|
||||||
titles?: Array<{
|
titles?:
|
||||||
title: string;
|
| ({
|
||||||
} | null> | null;
|
title: string;
|
||||||
|
} | null)[]
|
||||||
|
| null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
|
@ -99,9 +103,11 @@ export const prettyItemSubType = (
|
||||||
data?: {
|
data?: {
|
||||||
attributes?: {
|
attributes?: {
|
||||||
slug: string;
|
slug: string;
|
||||||
titles?: Array<{
|
titles?:
|
||||||
title: string;
|
| ({
|
||||||
} | null> | null;
|
title: string;
|
||||||
|
} | null)[]
|
||||||
|
| null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
|
@ -109,12 +115,12 @@ export const prettyItemSubType = (
|
||||||
| {
|
| {
|
||||||
__typename: "ComponentMetadataGame";
|
__typename: "ComponentMetadataGame";
|
||||||
platforms?: {
|
platforms?: {
|
||||||
data: Array<{
|
data: {
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
attributes?: {
|
attributes?: {
|
||||||
short: string;
|
short: string;
|
||||||
} | null;
|
} | null;
|
||||||
}>;
|
}[];
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
@ -123,9 +129,11 @@ export const prettyItemSubType = (
|
||||||
data?: {
|
data?: {
|
||||||
attributes?: {
|
attributes?: {
|
||||||
slug: string;
|
slug: string;
|
||||||
titles?: Array<{
|
titles?:
|
||||||
title: string;
|
| ({
|
||||||
} | null> | null;
|
title: string;
|
||||||
|
} | null)[]
|
||||||
|
| null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
|
@ -133,27 +141,31 @@ export const prettyItemSubType = (
|
||||||
data?: {
|
data?: {
|
||||||
attributes?: {
|
attributes?: {
|
||||||
slug: string;
|
slug: string;
|
||||||
titles?: Array<{
|
titles?:
|
||||||
title: string;
|
| ({
|
||||||
} | null> | null;
|
title: string;
|
||||||
|
} | null)[]
|
||||||
|
| null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
| { __typename: "ComponentMetadataOther" }
|
|
||||||
| {
|
| {
|
||||||
__typename: "ComponentMetadataVideo";
|
__typename: "ComponentMetadataVideo";
|
||||||
subtype?: {
|
subtype?: {
|
||||||
data?: {
|
data?: {
|
||||||
attributes?: {
|
attributes?: {
|
||||||
slug: string;
|
slug: string;
|
||||||
titles?: Array<{
|
titles?:
|
||||||
title: string;
|
| ({
|
||||||
} | null> | null;
|
title: string;
|
||||||
|
} | null)[]
|
||||||
|
| null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
| { __typename: "ComponentMetadataOther" }
|
||||||
| { __typename: "Error" }
|
| { __typename: "Error" }
|
||||||
| null
|
| null
|
||||||
): string => {
|
): string => {
|
||||||
|
@ -163,27 +175,27 @@ export const prettyItemSubType = (
|
||||||
case "ComponentMetadataBooks":
|
case "ComponentMetadataBooks":
|
||||||
case "ComponentMetadataVideo":
|
case "ComponentMetadataVideo":
|
||||||
return metadata.subtype?.data?.attributes?.titles &&
|
return metadata.subtype?.data?.attributes?.titles &&
|
||||||
metadata.subtype?.data?.attributes?.titles.length > 0 &&
|
metadata.subtype.data.attributes.titles.length > 0 &&
|
||||||
metadata.subtype.data.attributes.titles[0]
|
metadata.subtype.data.attributes.titles[0]
|
||||||
? metadata.subtype.data.attributes.titles[0].title
|
? metadata.subtype.data.attributes.titles[0].title
|
||||||
: prettySlug(metadata.subtype?.data?.attributes?.slug);
|
: prettySlug(metadata.subtype?.data?.attributes?.slug);
|
||||||
case "ComponentMetadataGame":
|
case "ComponentMetadataGame":
|
||||||
return metadata.platforms?.data &&
|
return metadata.platforms?.data &&
|
||||||
metadata.platforms?.data.length > 0 &&
|
metadata.platforms.data.length > 0 &&
|
||||||
metadata.platforms.data[0].attributes
|
metadata.platforms.data[0].attributes
|
||||||
? metadata.platforms.data[0].attributes.short
|
? metadata.platforms.data[0].attributes.short
|
||||||
: "";
|
: "";
|
||||||
case "ComponentMetadataGroup": {
|
case "ComponentMetadataGroup": {
|
||||||
const firstPart =
|
const firstPart =
|
||||||
metadata.subtype?.data?.attributes?.titles &&
|
metadata.subtype?.data?.attributes?.titles &&
|
||||||
metadata.subtype?.data?.attributes?.titles.length > 0 &&
|
metadata.subtype.data.attributes.titles.length > 0 &&
|
||||||
metadata.subtype.data.attributes.titles[0]
|
metadata.subtype.data.attributes.titles[0]
|
||||||
? metadata.subtype.data.attributes.titles[0].title
|
? metadata.subtype.data.attributes.titles[0].title
|
||||||
: prettySlug(metadata.subtype?.data?.attributes?.slug);
|
: prettySlug(metadata.subtype?.data?.attributes?.slug);
|
||||||
|
|
||||||
const secondPart =
|
const secondPart =
|
||||||
metadata.subitems_type?.data?.attributes?.titles &&
|
metadata.subitems_type?.data?.attributes?.titles &&
|
||||||
metadata.subitems_type?.data?.attributes?.titles.length > 0 &&
|
metadata.subitems_type.data.attributes.titles.length > 0 &&
|
||||||
metadata.subitems_type.data.attributes.titles[0]
|
metadata.subitems_type.data.attributes.titles[0]
|
||||||
? metadata.subitems_type.data.attributes.titles[0].title
|
? metadata.subitems_type.data.attributes.titles[0].title
|
||||||
: prettySlug(metadata.subitems_type?.data?.attributes?.slug);
|
: prettySlug(metadata.subitems_type?.data?.attributes?.slug);
|
||||||
|
@ -194,8 +206,8 @@ export const prettyItemSubType = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
||||||
};
|
};
|
||||||
|
/* eslint-enable id-denylist */
|
||||||
|
|
||||||
export const prettyShortenNumber = (number: number): string => {
|
export const prettyShortenNumber = (number: number): string => {
|
||||||
if (number > 1000000) {
|
if (number > 1000000) {
|
||||||
|
@ -203,11 +215,9 @@ export const prettyShortenNumber = (number: number): string => {
|
||||||
maximumSignificantDigits: 3,
|
maximumSignificantDigits: 3,
|
||||||
});
|
});
|
||||||
} else if (number > 1000) {
|
} else if (number > 1000) {
|
||||||
return (
|
return `${(number / 1000).toLocaleString(undefined, {
|
||||||
(number / 1000).toLocaleString(undefined, {
|
maximumSignificantDigits: 2,
|
||||||
maximumSignificantDigits: 2,
|
})}K`;
|
||||||
}) + "K"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return number.toLocaleString();
|
return number.toLocaleString();
|
||||||
};
|
};
|
||||||
|
@ -215,18 +225,19 @@ export const prettyShortenNumber = (number: number): string => {
|
||||||
export const prettyDuration = (seconds: number): string => {
|
export const prettyDuration = (seconds: number): string => {
|
||||||
let hours = 0;
|
let hours = 0;
|
||||||
let minutes = 0;
|
let minutes = 0;
|
||||||
while (seconds > 60) {
|
let remainingSeconds = seconds;
|
||||||
minutes += 1;
|
while (remainingSeconds > 60) {
|
||||||
seconds -= 60;
|
minutes++;
|
||||||
|
remainingSeconds -= 60;
|
||||||
}
|
}
|
||||||
while (minutes > 60) {
|
while (minutes > 60) {
|
||||||
hours += 1;
|
hours++;
|
||||||
minutes -= 60;
|
minutes -= 60;
|
||||||
}
|
}
|
||||||
let result = "";
|
let result = "";
|
||||||
if (hours) result += hours.toString().padStart(2, "0") + ":";
|
if (hours) result += `${hours.toString().padStart(2, "0")}:`;
|
||||||
result += minutes.toString().padStart(2, "0") + ":";
|
result += `${minutes.toString().padStart(2, "0")}:`;
|
||||||
result += seconds.toString().padStart(2, "0");
|
result += remainingSeconds.toString().padStart(2, "0");
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -236,21 +247,20 @@ export const prettyLanguage = (
|
||||||
): string => {
|
): string => {
|
||||||
let result = code;
|
let result = code;
|
||||||
languages.forEach((language) => {
|
languages.forEach((language) => {
|
||||||
if (language?.attributes?.code === code)
|
if (language.attributes?.code === code)
|
||||||
result = language.attributes.localized_name;
|
result = language.attributes.localized_name;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prettyURL = (url: string): string => {
|
export const prettyURL = (url: string): string => {
|
||||||
let domain = new URL(url);
|
const domain = new URL(url);
|
||||||
return domain.hostname.replace("www.", "");
|
return domain.hostname.replace("www.", "");
|
||||||
};
|
};
|
||||||
|
|
||||||
const capitalizeString = (string: string): string => {
|
const capitalizeString = (string: string): string => {
|
||||||
const capitalizeWord = (word: string): string => {
|
const capitalizeWord = (word: string): string =>
|
||||||
return word.charAt(0).toUpperCase() + word.substring(1);
|
word.charAt(0).toUpperCase() + word.substring(1);
|
||||||
};
|
|
||||||
|
|
||||||
let words = string.split(" ");
|
let words = string.split(" ");
|
||||||
words = words.map((word) => capitalizeWord(word));
|
words = words.map((word) => capitalizeWord(word));
|
||||||
|
@ -262,7 +272,7 @@ export const slugify = (string: string | undefined): string => {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return string
|
return string
|
||||||
.replace(/[ÀÁÂÃÄÅàáâãäåæÆ]/g, "a")
|
.replace(/[ÀÁÂÃÄÅàáâãäåæÆ]/gu, "a")
|
||||||
.replace(/[çÇ]/gu, "c")
|
.replace(/[çÇ]/gu, "c")
|
||||||
.replace(/[ðÐ]/gu, "d")
|
.replace(/[ðÐ]/gu, "d")
|
||||||
.replace(/[ÈÉÊËéèêë]/gu, "e")
|
.replace(/[ÈÉÊËéèêë]/gu, "e")
|
||||||
|
|
|
@ -73,12 +73,12 @@ export const getOgImage = (
|
||||||
const imgSize = getImgSizesByQuality(
|
const imgSize = getImgSizesByQuality(
|
||||||
image.width ?? 0,
|
image.width ?? 0,
|
||||||
image.height ?? 0,
|
image.height ?? 0,
|
||||||
quality ? quality : ImageQuality.Small
|
quality
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
image: getAssetURL(image.url, quality),
|
image: getAssetURL(image.url, quality),
|
||||||
width: imgSize.width,
|
width: imgSize.width,
|
||||||
height: imgSize.height,
|
height: imgSize.height,
|
||||||
alt: image.alternativeText || "",
|
alt: image.alternativeText ?? "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,252 +1,5 @@
|
||||||
import { AppLayoutState } from "contexts/AppLayoutContext";
|
export const isUntangibleGroupItem = (metadata: any): boolean =>
|
||||||
import { GetLibraryItemsPreviewQuery } from "graphql/generated";
|
metadata &&
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
metadata.__typename === "ComponentMetadataGroup" &&
|
||||||
import { prettyinlineTitle, prettyDate } from "./formatters";
|
(metadata.subtype?.data?.attributes?.slug === "variant-set" ||
|
||||||
import { convertPrice } from "./numbers";
|
metadata.subtype?.data?.attributes?.slug === "relation-set");
|
||||||
import { isDefined, mapRemoveEmptyValues } from "./others";
|
|
||||||
import { LibraryItemUserStatus } from "./types";
|
|
||||||
import LibraryPage from "../pages/library/index";
|
|
||||||
|
|
||||||
type Items = Parameters<typeof LibraryPage>[0]["items"];
|
|
||||||
type GroupLibraryItems = Map<string, Items>;
|
|
||||||
|
|
||||||
export const getGroups = (
|
|
||||||
langui: AppStaticProps["langui"],
|
|
||||||
groupByType: number,
|
|
||||||
items: Items
|
|
||||||
): GroupLibraryItems => {
|
|
||||||
const groups: GroupLibraryItems = new Map();
|
|
||||||
|
|
||||||
switch (groupByType) {
|
|
||||||
case 0: {
|
|
||||||
const noCategory = langui.no_category ?? "No category";
|
|
||||||
groups.set("Drakengard 1", []);
|
|
||||||
groups.set("Drakengard 1.3", []);
|
|
||||||
groups.set("Drakengard 2", []);
|
|
||||||
groups.set("Drakengard 3", []);
|
|
||||||
groups.set("Drakengard 4", []);
|
|
||||||
groups.set("NieR Gestalt", []);
|
|
||||||
groups.set("NieR Replicant", []);
|
|
||||||
groups.set("NieR Replicant ver.1.22474487139...", []);
|
|
||||||
groups.set("NieR:Automata", []);
|
|
||||||
groups.set("NieR Re[in]carnation", []);
|
|
||||||
groups.set("SINoALICE", []);
|
|
||||||
groups.set("Voice of Cards", []);
|
|
||||||
groups.set("Final Fantasy XIV", []);
|
|
||||||
groups.set("Thou Shalt Not Die", []);
|
|
||||||
groups.set("Bakuken", []);
|
|
||||||
groups.set("YoRHa", []);
|
|
||||||
groups.set("YoRHa Boys", []);
|
|
||||||
groups.set(noCategory, []);
|
|
||||||
|
|
||||||
items.map((item) => {
|
|
||||||
if (item.attributes?.categories?.data.length === 0) {
|
|
||||||
groups.get(noCategory)?.push(item);
|
|
||||||
} else {
|
|
||||||
item.attributes?.categories?.data.map((category) => {
|
|
||||||
groups.get(category.attributes?.name ?? noCategory)?.push(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 1: {
|
|
||||||
groups.set(langui.audio ?? "Audio", []);
|
|
||||||
groups.set(langui.game ?? "Game", []);
|
|
||||||
groups.set(langui.textual ?? "Textual", []);
|
|
||||||
groups.set(langui.video ?? "Video", []);
|
|
||||||
groups.set(langui.other ?? "Other", []);
|
|
||||||
groups.set(langui.group ?? "Group", []);
|
|
||||||
groups.set(langui.no_type ?? "No type", []);
|
|
||||||
items.map((item) => {
|
|
||||||
if (item.attributes?.metadata && item.attributes.metadata.length > 0) {
|
|
||||||
switch (item.attributes.metadata[0]?.__typename) {
|
|
||||||
case "ComponentMetadataAudio":
|
|
||||||
groups.get(langui.audio ?? "Audio")?.push(item);
|
|
||||||
break;
|
|
||||||
case "ComponentMetadataGame":
|
|
||||||
groups.get(langui.game ?? "Game")?.push(item);
|
|
||||||
break;
|
|
||||||
case "ComponentMetadataBooks":
|
|
||||||
groups.get(langui.textual ?? "Textual")?.push(item);
|
|
||||||
break;
|
|
||||||
case "ComponentMetadataVideo":
|
|
||||||
groups.get(langui.video ?? "Video")?.push(item);
|
|
||||||
break;
|
|
||||||
case "ComponentMetadataOther":
|
|
||||||
groups.get(langui.other ?? "Other")?.push(item);
|
|
||||||
break;
|
|
||||||
case "ComponentMetadataGroup":
|
|
||||||
switch (
|
|
||||||
item.attributes.metadata[0]?.subitems_type?.data?.attributes
|
|
||||||
?.slug
|
|
||||||
) {
|
|
||||||
case "audio":
|
|
||||||
groups.get(langui.audio ?? "Audio")?.push(item);
|
|
||||||
break;
|
|
||||||
case "video":
|
|
||||||
groups.get(langui.video ?? "Video")?.push(item);
|
|
||||||
break;
|
|
||||||
case "game":
|
|
||||||
groups.get(langui.game ?? "Game")?.push(item);
|
|
||||||
break;
|
|
||||||
case "textual":
|
|
||||||
groups.get(langui.textual ?? "Textual")?.push(item);
|
|
||||||
break;
|
|
||||||
case "mixed":
|
|
||||||
groups.get(langui.group ?? "Group")?.push(item);
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
throw new Error(
|
|
||||||
"An unexpected subtype of group-metadata was given"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
throw new Error("An unexpected type of metadata was given");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
groups.get(langui.no_type ?? "No type")?.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 2: {
|
|
||||||
const years: number[] = [];
|
|
||||||
items.map((item) => {
|
|
||||||
if (item.attributes?.release_date?.year) {
|
|
||||||
if (!years.includes(item.attributes.release_date.year))
|
|
||||||
years.push(item.attributes.release_date.year);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
years.sort((a, b) => a - b);
|
|
||||||
years.map((year) => {
|
|
||||||
groups.set(year.toString(), []);
|
|
||||||
});
|
|
||||||
groups.set(langui.no_year ?? "No year", []);
|
|
||||||
items.map((item) => {
|
|
||||||
if (item.attributes?.release_date?.year) {
|
|
||||||
groups.get(item.attributes.release_date.year.toString())?.push(item);
|
|
||||||
} else {
|
|
||||||
groups.get(langui.no_year ?? "No year")?.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
groups.set("", items);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mapRemoveEmptyValues(groups);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const filterItems = (
|
|
||||||
appLayout: AppLayoutState,
|
|
||||||
items: Items,
|
|
||||||
searchName: string,
|
|
||||||
showSubitems: boolean,
|
|
||||||
showPrimaryItems: boolean,
|
|
||||||
showSecondaryItems: boolean,
|
|
||||||
filterUserStatus: LibraryItemUserStatus | undefined
|
|
||||||
): Items => {
|
|
||||||
return items.filter((item) => {
|
|
||||||
if (!showSubitems && !item.attributes?.root_item) return false;
|
|
||||||
if (showSubitems && isUntangibleGroupItem(item.attributes?.metadata?.[0])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (item.attributes?.primary && !showPrimaryItems) return false;
|
|
||||||
if (!item.attributes?.primary && !showSecondaryItems) return false;
|
|
||||||
|
|
||||||
if (
|
|
||||||
searchName.length > 1 &&
|
|
||||||
!prettyinlineTitle("", item.attributes?.title, item.attributes?.subtitle)
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchName.toLowerCase())
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
isDefined(filterUserStatus) &&
|
|
||||||
item.id &&
|
|
||||||
appLayout.libraryItemUserStatus
|
|
||||||
) {
|
|
||||||
if (isUntangibleGroupItem(item.attributes?.metadata?.[0])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (filterUserStatus === LibraryItemUserStatus.None) {
|
|
||||||
if (appLayout.libraryItemUserStatus[item.id]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
filterUserStatus !== appLayout.libraryItemUserStatus[item.id]
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Properly type this shit
|
|
||||||
export const isUntangibleGroupItem = (metadata: any) => {
|
|
||||||
return (
|
|
||||||
metadata &&
|
|
||||||
metadata.__typename === "ComponentMetadataGroup" &&
|
|
||||||
(metadata.subtype?.data?.attributes?.slug === "variant-set" ||
|
|
||||||
metadata.subtype?.data?.attributes?.slug === "relation-set")
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sortBy = (
|
|
||||||
orderByType: number,
|
|
||||||
items: Items,
|
|
||||||
currencies: AppStaticProps["currencies"]
|
|
||||||
) => {
|
|
||||||
switch (orderByType) {
|
|
||||||
case 0:
|
|
||||||
return items.sort((a, b) => {
|
|
||||||
const titleA = prettyinlineTitle(
|
|
||||||
"",
|
|
||||||
a.attributes?.title,
|
|
||||||
a.attributes?.subtitle
|
|
||||||
);
|
|
||||||
const titleB = prettyinlineTitle(
|
|
||||||
"",
|
|
||||||
b.attributes?.title,
|
|
||||||
b.attributes?.subtitle
|
|
||||||
);
|
|
||||||
return titleA.localeCompare(titleB);
|
|
||||||
});
|
|
||||||
case 1:
|
|
||||||
return items.sort((a, b) => {
|
|
||||||
const priceA = a.attributes?.price
|
|
||||||
? convertPrice(a.attributes.price, currencies[0])
|
|
||||||
: 99999;
|
|
||||||
const priceB = b.attributes?.price
|
|
||||||
? convertPrice(b.attributes.price, currencies[0])
|
|
||||||
: 99999;
|
|
||||||
return priceA - priceB;
|
|
||||||
});
|
|
||||||
case 2:
|
|
||||||
return items.sort((a, b) => {
|
|
||||||
const dateA = a.attributes?.release_date
|
|
||||||
? prettyDate(a.attributes.release_date)
|
|
||||||
: "9999";
|
|
||||||
const dateB = b.attributes?.release_date
|
|
||||||
? prettyDate(b.attributes.release_date)
|
|
||||||
: "9999";
|
|
||||||
return dateA.localeCompare(dateB);
|
|
||||||
});
|
|
||||||
default:
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -16,15 +16,11 @@ export const convertPrice = (
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const convertMmToInch = (mm: number | null | undefined): string => {
|
export const convertMmToInch = (mm: number | null | undefined): string =>
|
||||||
return mm ? (mm * 0.03937008).toPrecision(3) : "";
|
mm ? (mm * 0.03937008).toPrecision(3) : "";
|
||||||
};
|
|
||||||
|
|
||||||
export const randomInt = (min: number, max: number) => {
|
export const randomInt = (min: number, max: number): number =>
|
||||||
return Math.floor(Math.random() * (max - min)) + min;
|
Math.floor(Math.random() * (max - min)) + min;
|
||||||
};
|
|
||||||
|
|
||||||
export const isInteger = (value: string): boolean => {
|
export const isInteger = (value: string): boolean =>
|
||||||
// eslint-disable-next-line require-unicode-regexp
|
/^[+-]?[0-9]+$/u.test(value);
|
||||||
return /^[+-]?[0-9]+$/.test(value);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
import { AppStaticProps } from "../graphql/getAppStaticProps";
|
||||||
|
import { SelectiveRequiredNonNullable } from "./types";
|
||||||
import {
|
import {
|
||||||
Enum_Componentsetstextset_Status,
|
Enum_Componentsetstextset_Status,
|
||||||
GetLibraryItemQuery,
|
GetLibraryItemQuery,
|
||||||
GetLibraryItemScansQuery,
|
GetLibraryItemScansQuery,
|
||||||
} from "graphql/generated";
|
} from "graphql/generated";
|
||||||
import { AppStaticProps } from "../graphql/getAppStaticProps";
|
|
||||||
import { SelectiveRequiredNonNullable } from "./types";
|
|
||||||
|
|
||||||
type SortContentProps =
|
type SortRangedContentProps =
|
||||||
| NonNullable<
|
| NonNullable<
|
||||||
NonNullable<
|
NonNullable<
|
||||||
GetLibraryItemQuery["libraryItems"]
|
GetLibraryItemQuery["libraryItems"]
|
||||||
|
@ -18,7 +18,7 @@ type SortContentProps =
|
||||||
>["data"][number]["attributes"]
|
>["data"][number]["attributes"]
|
||||||
>["contents"];
|
>["contents"];
|
||||||
|
|
||||||
export const sortContent = (contents: SortContentProps) => {
|
export const sortRangedContent = (contents: SortRangedContentProps): void => {
|
||||||
contents?.data.sort((a, b) => {
|
contents?.data.sort((a, b) => {
|
||||||
if (
|
if (
|
||||||
a.attributes?.range[0]?.__typename === "ComponentRangePageRange" &&
|
a.attributes?.range[0]?.__typename === "ComponentRangePageRange" &&
|
||||||
|
@ -59,20 +59,20 @@ export const isDefined = <T>(t: T): t is NonNullable<T> =>
|
||||||
t !== null && t !== undefined;
|
t !== null && t !== undefined;
|
||||||
|
|
||||||
export const isUndefined = <T>(
|
export const isUndefined = <T>(
|
||||||
t: T | undefined | null
|
t: T | null | undefined
|
||||||
): t is undefined | null => t === null || t === undefined;
|
): t is null | undefined => t === null || t === undefined;
|
||||||
|
|
||||||
export const isDefinedAndNotEmpty = (
|
export const isDefinedAndNotEmpty = (
|
||||||
string: string | undefined | null
|
string: string | null | undefined
|
||||||
): string is string => isDefined(string) && string.length > 0;
|
): string is string => isDefined(string) && string.length > 0;
|
||||||
|
|
||||||
export const filterDefined = <T>(t: T[] | undefined | null): NonNullable<T>[] =>
|
export const filterDefined = <T>(t: T[] | null | undefined): NonNullable<T>[] =>
|
||||||
isUndefined(t)
|
isUndefined(t)
|
||||||
? []
|
? []
|
||||||
: (t.filter((item) => isDefined(item)) as NonNullable<T>[]);
|
: (t.filter((item) => isDefined(item)) as NonNullable<T>[]);
|
||||||
|
|
||||||
export const filterHasAttributes = <T, P extends keyof NonNullable<T>>(
|
export const filterHasAttributes = <T, P extends keyof NonNullable<T>>(
|
||||||
t: T[] | undefined | null,
|
t: T[] | null | undefined,
|
||||||
attributes?: P[]
|
attributes?: P[]
|
||||||
): SelectiveRequiredNonNullable<NonNullable<T>, P>[] =>
|
): SelectiveRequiredNonNullable<NonNullable<T>, P>[] =>
|
||||||
isUndefined(t)
|
isUndefined(t)
|
||||||
|
@ -89,27 +89,24 @@ export const filterHasAttributes = <T, P extends keyof NonNullable<T>>(
|
||||||
|
|
||||||
export const iterateMap = <K, V, U>(
|
export const iterateMap = <K, V, U>(
|
||||||
map: Map<K, V>,
|
map: Map<K, V>,
|
||||||
callbackfn: (key: K, value: V, index: number) => U
|
callbackfn: (key: K, value: V, index: number) => U,
|
||||||
|
sortingFunction?: (a: [K, V], b: [K, V]) => number
|
||||||
): U[] => {
|
): U[] => {
|
||||||
const result: U[] = [];
|
const toList = [...map];
|
||||||
let index = 0;
|
if (isDefined(sortingFunction)) {
|
||||||
for (const [key, value] of map.entries()) {
|
toList.sort(sortingFunction);
|
||||||
result.push(callbackfn(key, value, index));
|
console.log(toList.sort(sortingFunction));
|
||||||
index += 1;
|
|
||||||
}
|
}
|
||||||
return result;
|
return toList.map(([key, value], index) => callbackfn(key, value, index));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mapMoveEntry = <K, V>(
|
export const mapMoveEntry = <K, V>(
|
||||||
map: Map<K, V>,
|
map: Map<K, V>,
|
||||||
sourceIndex: number,
|
sourceIndex: number,
|
||||||
targetIndex: number
|
targetIndex: number
|
||||||
) => new Map(arrayMove([...map], sourceIndex, targetIndex));
|
): Map<K, V> => new Map(arrayMove([...map], sourceIndex, targetIndex));
|
||||||
|
|
||||||
const arrayMove = <T>(arr: T[], sourceIndex: number, targetIndex: number) => {
|
const arrayMove = <T>(arr: T[], sourceIndex: number, targetIndex: number) => {
|
||||||
arr.splice(targetIndex, 0, arr.splice(sourceIndex, 1)[0]);
|
arr.splice(targetIndex, 0, arr.splice(sourceIndex, 1)[0]);
|
||||||
return arr;
|
return arr;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mapRemoveEmptyValues = <K, V>(groups: Map<K, V[]>): Map<K, V[]> =>
|
|
||||||
new Map([...groups].filter(([_, items]) => items.length > 0));
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { LightBox } from "components/LightBox";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { LightBox } from "components/LightBox";
|
||||||
|
|
||||||
export const useLightBox = (): [
|
export const useLightBox = (): [
|
||||||
(images: string[], index?: number) => void,
|
(images: string[], index?: number) => void,
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { breaks } from "../../design.config";
|
import { breaks } from "../../design.config";
|
||||||
|
|
||||||
const useMediaQuery = (query: string): boolean => {
|
const useMediaQuery = (query: string): boolean => {
|
||||||
const getMatches = (query: string): boolean => {
|
const getMatches = useCallback((): boolean => {
|
||||||
// Prevents SSR issues
|
// Prevents SSR issues
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
return window.matchMedia(query).matches;
|
return window.matchMedia(query).matches;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
}, [query]);
|
||||||
|
|
||||||
const [matches, setMatches] = useState<boolean>(getMatches(query));
|
const [matches, setMatches] = useState<boolean>(getMatches());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleChange = () => {
|
const handleChange = () => {
|
||||||
setMatches(getMatches(query));
|
setMatches(getMatches());
|
||||||
};
|
};
|
||||||
|
|
||||||
const matchMedia = window.matchMedia(query);
|
const matchMedia = window.matchMedia(query);
|
||||||
|
@ -28,19 +28,19 @@ const useMediaQuery = (query: string): boolean => {
|
||||||
return () => {
|
return () => {
|
||||||
matchMedia.removeEventListener("change", handleChange);
|
matchMedia.removeEventListener("change", handleChange);
|
||||||
};
|
};
|
||||||
}, [query]);
|
}, [getMatches, query]);
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ts-unused-exports:disable-next-line
|
// ts-unused-exports:disable-next-line
|
||||||
export const useMediaThin = () => useMediaQuery(breaks.thin.raw);
|
export const useMediaThin = (): boolean => useMediaQuery(breaks.thin.raw);
|
||||||
|
|
||||||
export const useMediaMobile = () => useMediaQuery(breaks.mobile.raw);
|
export const useMediaMobile = (): boolean => useMediaQuery(breaks.mobile.raw);
|
||||||
|
|
||||||
export const useMediaDesktop = () => useMediaQuery(breaks.desktop.raw);
|
export const useMediaDesktop = (): boolean => useMediaQuery(breaks.desktop.raw);
|
||||||
|
|
||||||
export const useMediaHoverable = () => useMediaQuery("(hover: hover)");
|
export const useMediaHoverable = (): boolean => useMediaQuery("(hover: hover)");
|
||||||
|
|
||||||
export const usePrefersDarkMode = () =>
|
export const usePrefersDarkMode = (): boolean =>
|
||||||
useMediaQuery("(prefers-color-scheme: dark)");
|
useMediaQuery("(prefers-color-scheme: dark)");
|
||||||
|
|
|
@ -5,8 +5,15 @@ export enum AnchorIds {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to top of element "id" when "deps" update.
|
// Scroll to top of element "id" when "deps" update.
|
||||||
export const useScrollTopOnChange = (id: AnchorIds, deps: DependencyList) => {
|
export const useScrollTopOnChange = (
|
||||||
|
id: AnchorIds,
|
||||||
|
deps: DependencyList,
|
||||||
|
enabled = true
|
||||||
|
): void => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.querySelector(`#${id}`)?.scrollTo({ top: 0, behavior: "smooth" });
|
if (enabled)
|
||||||
}, deps);
|
document
|
||||||
|
.querySelector(`#${id}`)
|
||||||
|
?.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
|
}, [id, deps, enabled]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { LanguageSwitcher } from "components/Inputs/LanguageSwitcher";
|
import { LanguageSwitcher } from "components/Inputs/LanguageSwitcher";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { filterDefined, isDefined } from "helpers/others";
|
import { filterDefined, isDefined } from "helpers/others";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
|
|
||||||
interface Props<T> {
|
interface Props<T> {
|
||||||
items: T[];
|
items: T[];
|
||||||
languages: AppStaticProps["languages"];
|
languages: AppStaticProps["languages"];
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { isDefined } from "helpers/others";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { isDefined } from "helpers/others";
|
||||||
|
|
||||||
export const useStateWithLocalStorage = <T>(
|
export const useStateWithLocalStorage = <T>(
|
||||||
key: string,
|
key: string,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Dispatch, SetStateAction, useCallback } from "react";
|
import { Dispatch, SetStateAction, useCallback } from "react";
|
||||||
|
|
||||||
export const useToggle = (setState: Dispatch<SetStateAction<boolean>>) =>
|
export const useToggle = (
|
||||||
|
setState: Dispatch<SetStateAction<boolean>>
|
||||||
|
): (() => void) =>
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
setState((current) => !current);
|
setState((current) => !current);
|
||||||
}, []);
|
}, [setState]);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import {
|
import {
|
||||||
ReturnButton,
|
ReturnButton,
|
||||||
|
@ -5,7 +6,6 @@ import {
|
||||||
} from "components/PanelComponents/ReturnButton";
|
} from "components/PanelComponents/ReturnButton";
|
||||||
import { ContentPanel } from "components/Panels/ContentPanel";
|
import { ContentPanel } from "components/Panels/ContentPanel";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import {
|
import {
|
||||||
ReturnButton,
|
ReturnButton,
|
||||||
|
@ -5,7 +6,6 @@ import {
|
||||||
} from "components/PanelComponents/ReturnButton";
|
} from "components/PanelComponents/ReturnButton";
|
||||||
import { ContentPanel } from "components/Panels/ContentPanel";
|
import { ContentPanel } from "components/Panels/ContentPanel";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -5,8 +5,8 @@ import "@fontsource/opendyslexic/700.css";
|
||||||
import "@fontsource/vollkorn/700.css";
|
import "@fontsource/vollkorn/700.css";
|
||||||
import "@fontsource/zen-maru-gothic/500.css";
|
import "@fontsource/zen-maru-gothic/500.css";
|
||||||
import "@fontsource/zen-maru-gothic/900.css";
|
import "@fontsource/zen-maru-gothic/900.css";
|
||||||
import { AppContextProvider } from "contexts/AppLayoutContext";
|
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
|
import { AppContextProvider } from "contexts/AppLayoutContext";
|
||||||
import "tailwind.css";
|
import "tailwind.css";
|
||||||
|
|
||||||
const AccordsLibraryApp = (props: AppProps): JSX.Element => (
|
const AccordsLibraryApp = (props: AppProps): JSX.Element => (
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
import { InsetBox } from "components/InsetBox";
|
import { InsetBox } from "components/InsetBox";
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import {
|
import {
|
||||||
|
@ -6,10 +8,7 @@ import {
|
||||||
} from "graphql/getPostStaticProps";
|
} from "graphql/getPostStaticProps";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { randomInt } from "helpers/numbers";
|
import { randomInt } from "helpers/numbers";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { NavOption } from "components/PanelComponents/NavOption";
|
import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import { SubPanel } from "components/Panels/SubPanel";
|
import { SubPanel } from "components/Panels/SubPanel";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -17,7 +17,7 @@ export interface RequestMailProps {
|
||||||
const Mail = async (
|
const Mail = async (
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse<ResponseMailProps>
|
res: NextApiResponse<ResponseMailProps>
|
||||||
) => {
|
): Promise<void> => {
|
||||||
if (req.method === "POST") {
|
if (req.method === "POST") {
|
||||||
const body = req.body as RequestMailProps;
|
const body = req.body as RequestMailProps;
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,17 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
import getConfig from "next/config";
|
import getConfig from "next/config";
|
||||||
|
|
||||||
type RequestProps =
|
type RequestProps =
|
||||||
| HookRangedContent
|
|
||||||
| HookPostContent
|
|
||||||
| HookLibraryItem
|
|
||||||
| HookChronology
|
| HookChronology
|
||||||
| HookContent
|
| HookContent
|
||||||
| HookContentGroup
|
| HookContentGroup
|
||||||
| HookCustom
|
| HookCustom
|
||||||
|
| HookLibraryItem
|
||||||
|
| HookPostContent
|
||||||
|
| HookRangedContent
|
||||||
| HookWiki;
|
| HookWiki;
|
||||||
|
|
||||||
type HookRangedContent = {
|
type HookRangedContent = {
|
||||||
event: "entry.update" | "entry.delete" | "entry.create";
|
event: "entry.create" | "entry.delete" | "entry.update";
|
||||||
model: "ranged-content";
|
model: "ranged-content";
|
||||||
entry: {
|
entry: {
|
||||||
library_item?: {
|
library_item?: {
|
||||||
|
@ -33,16 +33,14 @@ type HookContent = {
|
||||||
model: "content";
|
model: "content";
|
||||||
entry: {
|
entry: {
|
||||||
slug: string;
|
slug: string;
|
||||||
ranged_contents: [
|
ranged_contents: {
|
||||||
{
|
slug: string;
|
||||||
slug: string;
|
}[];
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type HookPostContent = {
|
type HookPostContent = {
|
||||||
event: "entry.update" | "entry.delete" | "entry.create";
|
event: "entry.create" | "entry.delete" | "entry.update";
|
||||||
model: "post";
|
model: "post";
|
||||||
entry: {
|
entry: {
|
||||||
slug: string;
|
slug: string;
|
||||||
|
@ -50,7 +48,7 @@ type HookPostContent = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type HookLibraryItem = {
|
type HookLibraryItem = {
|
||||||
event: "entry.update" | "entry.delete" | "entry.create";
|
event: "entry.create" | "entry.delete" | "entry.update";
|
||||||
model: "library-item";
|
model: "library-item";
|
||||||
entry: {
|
entry: {
|
||||||
slug: string;
|
slug: string;
|
||||||
|
@ -63,7 +61,7 @@ type HookLibraryItem = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type HookContentGroup = {
|
type HookContentGroup = {
|
||||||
event: "entry.update" | "entry.delete" | "entry.create";
|
event: "entry.create" | "entry.delete" | "entry.update";
|
||||||
model: "contents-group";
|
model: "contents-group";
|
||||||
entry: {
|
entry: {
|
||||||
contents: {
|
contents: {
|
||||||
|
@ -73,12 +71,12 @@ type HookContentGroup = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type HookChronology = {
|
type HookChronology = {
|
||||||
event: "entry.update" | "entry.delete" | "entry.create";
|
event: "entry.create" | "entry.delete" | "entry.update";
|
||||||
model: "chronology-era" | "chronology-item";
|
model: "chronology-era" | "chronology-item";
|
||||||
};
|
};
|
||||||
|
|
||||||
type HookWiki = {
|
type HookWiki = {
|
||||||
event: "entry.update" | "entry.delete" | "entry.create";
|
event: "entry.create" | "entry.delete" | "entry.update";
|
||||||
model: "wiki-page";
|
model: "wiki-page";
|
||||||
entry: {
|
entry: {
|
||||||
slug: string;
|
slug: string;
|
||||||
|
@ -90,10 +88,10 @@ type ResponseMailProps = {
|
||||||
revalidated: boolean;
|
revalidated: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Revalidate = async (
|
const Revalidate = (
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse<ResponseMailProps>
|
res: NextApiResponse<ResponseMailProps>
|
||||||
) => {
|
): void => {
|
||||||
const body = req.body as RequestProps;
|
const body = req.body as RequestProps;
|
||||||
const { serverRuntimeConfig } = getConfig();
|
const { serverRuntimeConfig } = getConfig();
|
||||||
|
|
||||||
|
@ -101,9 +99,8 @@ const Revalidate = async (
|
||||||
if (
|
if (
|
||||||
req.headers.authorization !== `Bearer ${process.env.REVALIDATION_TOKEN}`
|
req.headers.authorization !== `Bearer ${process.env.REVALIDATION_TOKEN}`
|
||||||
) {
|
) {
|
||||||
return res
|
res.status(401).json({ message: "Invalid token", revalidated: false });
|
||||||
.status(401)
|
return;
|
||||||
.json({ message: "Invalid token", revalidated: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const paths: string[] = [];
|
const paths: string[] = [];
|
||||||
|
@ -229,13 +226,12 @@ const Revalidate = async (
|
||||||
await res.revalidate(path);
|
await res.revalidate(path);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return res.json({ message: "Success!", revalidated: true });
|
res.json({ message: "Success!", revalidated: true });
|
||||||
} catch (err) {
|
return;
|
||||||
// If there was an error, Next.js will continue
|
} catch (error) {
|
||||||
// to show the last successfully generated page
|
res
|
||||||
return res
|
|
||||||
.status(500)
|
.status(500)
|
||||||
.send({ message: "Error revalidating", revalidated: false });
|
.send({ message: `Error revalidating: ${error}`, revalidated: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export default Revalidate;
|
export default Revalidate;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { NavOption } from "components/PanelComponents/NavOption";
|
import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import { SubPanel } from "components/Panels/SubPanel";
|
import { SubPanel } from "components/Panels/SubPanel";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||||
|
import { Fragment, useState, useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Switch } from "components/Inputs/Switch";
|
import { Switch } from "components/Inputs/Switch";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
|
@ -15,8 +17,6 @@ import { GetVideoChannelQuery } from "graphql/generated";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { getVideoThumbnailURL } from "helpers/videos";
|
import { getVideoThumbnailURL } from "helpers/videos";
|
||||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
|
||||||
import { Fragment, useState, useMemo } from "react";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||||
import { WithLabel } from "components/Inputs/WithLabel";
|
import { WithLabel } from "components/Inputs/WithLabel";
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
|
import { SmartList } from "components/SmartList";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { PageSelector } from "components/Inputs/PageSelector";
|
|
||||||
import { Switch } from "components/Inputs/Switch";
|
import { Switch } from "components/Inputs/Switch";
|
||||||
|
import { TextInput } from "components/Inputs/TextInput";
|
||||||
import { WithLabel } from "components/Inputs/WithLabel";
|
import { WithLabel } from "components/Inputs/WithLabel";
|
||||||
|
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import {
|
import {
|
||||||
ReturnButton,
|
ReturnButton,
|
||||||
|
@ -21,15 +25,15 @@ import { prettyDate } from "helpers/formatters";
|
||||||
import { filterHasAttributes } from "helpers/others";
|
import { filterHasAttributes } from "helpers/others";
|
||||||
import { getVideoThumbnailURL } from "helpers/videos";
|
import { getVideoThumbnailURL } from "helpers/videos";
|
||||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { Fragment, useMemo, useState } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const ITEM_PER_PAGE = 50;
|
const DEFAULT_FILTERS_STATE = {
|
||||||
|
searchName: "",
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -42,18 +46,10 @@ interface Props extends AppStaticProps {
|
||||||
|
|
||||||
const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
|
const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
|
||||||
const hoverable = useMediaHoverable();
|
const hoverable = useMediaHoverable();
|
||||||
const [page, setPage] = useState(0);
|
|
||||||
const [keepInfoVisible, setKeepInfoVisible] = useState(true);
|
const [keepInfoVisible, setKeepInfoVisible] = useState(true);
|
||||||
|
const [searchName, setSearchName] = useState(
|
||||||
const paginatedVideos = useMemo(() => {
|
DEFAULT_FILTERS_STATE.searchName
|
||||||
const memo = [];
|
);
|
||||||
for (let index = 0; ITEM_PER_PAGE * index < videos.length; index += 1) {
|
|
||||||
memo.push(
|
|
||||||
videos.slice(index * ITEM_PER_PAGE, (index + 1) * ITEM_PER_PAGE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return memo;
|
|
||||||
}, [videos]);
|
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
@ -72,6 +68,13 @@ const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
|
||||||
description={langui.archives_description}
|
description={langui.archives_description}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
className="mb-6 w-full"
|
||||||
|
placeholder={langui.search_title ?? undefined}
|
||||||
|
state={searchName}
|
||||||
|
setState={setSearchName}
|
||||||
|
/>
|
||||||
|
|
||||||
{hoverable && (
|
{hoverable && (
|
||||||
<WithLabel
|
<WithLabel
|
||||||
label={langui.always_show_info}
|
label={langui.always_show_info}
|
||||||
|
@ -82,56 +85,53 @@ const Videos = ({ langui, videos, ...otherProps }: Props): JSX.Element => {
|
||||||
)}
|
)}
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
),
|
),
|
||||||
[hoverable, keepInfoVisible, langui]
|
[hoverable, keepInfoVisible, langui, searchName]
|
||||||
);
|
);
|
||||||
|
|
||||||
const contentPanel = useMemo(
|
const contentPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||||
<PageSelector
|
<SmartList
|
||||||
maxPage={Math.floor(videos.length / ITEM_PER_PAGE)}
|
items={filterHasAttributes(videos)}
|
||||||
page={page}
|
getItemId={(item) => item.id}
|
||||||
setPage={setPage}
|
renderItem={({ item }) => (
|
||||||
className="mb-12"
|
<>
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="grid items-start gap-8 border-b-[3px] border-dotted pb-12 last-of-type:border-0
|
|
||||||
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:grid-cols-2
|
|
||||||
thin:grid-cols-1"
|
|
||||||
>
|
|
||||||
{filterHasAttributes(paginatedVideos[page]).map((video) => (
|
|
||||||
<Fragment key={video.id}>
|
|
||||||
<PreviewCard
|
<PreviewCard
|
||||||
href={`/archives/videos/v/${video.attributes.uid}`}
|
href={`/archives/videos/v/${item.attributes.uid}`}
|
||||||
title={video.attributes.title}
|
title={item.attributes.title}
|
||||||
thumbnail={getVideoThumbnailURL(video.attributes.uid)}
|
thumbnail={getVideoThumbnailURL(item.attributes.uid)}
|
||||||
thumbnailAspectRatio="16/9"
|
thumbnailAspectRatio="16/9"
|
||||||
|
thumbnailForceAspectRatio
|
||||||
keepInfoVisible={keepInfoVisible}
|
keepInfoVisible={keepInfoVisible}
|
||||||
metadata={{
|
metadata={{
|
||||||
release_date: video.attributes.published_date,
|
release_date: item.attributes.published_date,
|
||||||
views: video.attributes.views,
|
views: item.attributes.views,
|
||||||
author: video.attributes.channel?.data?.attributes?.title,
|
author: item.attributes.channel?.data?.attributes?.title,
|
||||||
position: "Top",
|
position: "Top",
|
||||||
}}
|
}}
|
||||||
hoverlay={{
|
hoverlay={{
|
||||||
__typename: "Video",
|
__typename: "Video",
|
||||||
duration: video.attributes.duration,
|
duration: item.attributes.duration,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</>
|
||||||
))}
|
)}
|
||||||
</div>
|
renderWhenEmpty={() => (
|
||||||
|
<ContentPlaceholder
|
||||||
<PageSelector
|
message={langui.no_results_message ?? "No results"}
|
||||||
maxPage={Math.floor(videos.length / ITEM_PER_PAGE)}
|
icon={Icon.ChevronLeft}
|
||||||
page={page}
|
/>
|
||||||
setPage={setPage}
|
)}
|
||||||
className="mt-12"
|
className="desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:grid-cols-2
|
||||||
|
thin:grid-cols-1"
|
||||||
|
paginationItemPerPage={20}
|
||||||
|
searchingTerm={searchName}
|
||||||
|
searchingBy={(item) => item.attributes.title}
|
||||||
|
langui={langui}
|
||||||
/>
|
/>
|
||||||
</ContentPanel>
|
</ContentPanel>
|
||||||
),
|
),
|
||||||
[keepInfoVisible, page, paginatedVideos, videos.length]
|
[keepInfoVisible, langui, searchName, videos]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
|
@ -21,8 +23,6 @@ import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
||||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||||
import { getVideoFile } from "helpers/videos";
|
import { getVideoFile } from "helpers/videos";
|
||||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import { SubPanel } from "components/Panels/SubPanel";
|
import { SubPanel } from "components/Panels/SubPanel";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||||
|
import { Fragment, useCallback, useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
|
@ -34,8 +36,6 @@ import { ContentWithTranslations } from "helpers/types";
|
||||||
import { useMediaMobile } from "hooks/useMediaQuery";
|
import { useMediaMobile } from "hooks/useMediaQuery";
|
||||||
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
|
||||||
import { Fragment, useCallback, useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -111,7 +111,9 @@ const Content = ({
|
||||||
.code !==
|
.code !==
|
||||||
selectedTranslation.language?.data?.attributes?.code && (
|
selectedTranslation.language?.data?.attributes?.code && (
|
||||||
<div className="grid place-items-center gap-2">
|
<div className="grid place-items-center gap-2">
|
||||||
<p className="font-headers">{langui.source_language}:</p>
|
<p className="font-headers font-bold">
|
||||||
|
{langui.source_language}:
|
||||||
|
</p>
|
||||||
<Chip>
|
<Chip>
|
||||||
{prettyLanguage(
|
{prettyLanguage(
|
||||||
selectedTranslation.text_set.source_language.data.attributes
|
selectedTranslation.text_set.source_language.data.attributes
|
||||||
|
@ -123,7 +125,7 @@ const Content = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
<div className="grid grid-flow-col place-content-center place-items-center gap-2">
|
||||||
<p className="font-headers">{langui.status}:</p>
|
<p className="font-headers font-bold">{langui.status}:</p>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={getStatusDescription(
|
content={getStatusDescription(
|
||||||
|
@ -139,7 +141,9 @@ const Content = ({
|
||||||
{selectedTranslation.text_set.transcribers &&
|
{selectedTranslation.text_set.transcribers &&
|
||||||
selectedTranslation.text_set.transcribers.data.length > 0 && (
|
selectedTranslation.text_set.transcribers.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{langui.transcribers}:</p>
|
<p className="font-headers font-bold">
|
||||||
|
{langui.transcribers}:
|
||||||
|
</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(
|
{filterHasAttributes(
|
||||||
selectedTranslation.text_set.transcribers.data
|
selectedTranslation.text_set.transcribers.data
|
||||||
|
@ -158,7 +162,9 @@ const Content = ({
|
||||||
{selectedTranslation.text_set.translators &&
|
{selectedTranslation.text_set.translators &&
|
||||||
selectedTranslation.text_set.translators.data.length > 0 && (
|
selectedTranslation.text_set.translators.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{langui.translators}:</p>
|
<p className="font-headers font-bold">
|
||||||
|
{langui.translators}:
|
||||||
|
</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(
|
{filterHasAttributes(
|
||||||
selectedTranslation.text_set.translators.data
|
selectedTranslation.text_set.translators.data
|
||||||
|
@ -177,7 +183,9 @@ const Content = ({
|
||||||
{selectedTranslation.text_set.proofreaders &&
|
{selectedTranslation.text_set.proofreaders &&
|
||||||
selectedTranslation.text_set.proofreaders.data.length > 0 && (
|
selectedTranslation.text_set.proofreaders.data.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{langui.proofreaders}:</p>
|
<p className="font-headers font-bold">
|
||||||
|
{langui.proofreaders}:
|
||||||
|
</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
{filterHasAttributes(
|
{filterHasAttributes(
|
||||||
selectedTranslation.text_set.proofreaders.data
|
selectedTranslation.text_set.proofreaders.data
|
||||||
|
@ -195,7 +203,7 @@ const Content = ({
|
||||||
|
|
||||||
{isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (
|
{isDefinedAndNotEmpty(selectedTranslation.text_set.notes) && (
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers">{"Notes"}:</p>
|
<p className="font-headers font-bold">{"Notes"}:</p>
|
||||||
<div className="grid place-content-center place-items-center gap-2">
|
<div className="grid place-content-center place-items-center gap-2">
|
||||||
<Markdawn text={selectedTranslation.text_set.notes} />
|
<Markdawn text={selectedTranslation.text_set.notes} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -209,7 +217,9 @@ const Content = ({
|
||||||
<>
|
<>
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-headers text-2xl">{langui.source}</p>
|
<p className="font-headers text-2xl font-bold">
|
||||||
|
{langui.source}
|
||||||
|
</p>
|
||||||
<div className="mt-6 grid place-items-center gap-6 text-left">
|
<div className="mt-6 grid place-items-center gap-6 text-left">
|
||||||
{content.ranged_contents.data.map((rangedContent) => {
|
{content.ranged_contents.data.map((rangedContent) => {
|
||||||
const libraryItem =
|
const libraryItem =
|
||||||
|
@ -525,7 +535,7 @@ type Group = NonNullable<
|
||||||
>["data"];
|
>["data"];
|
||||||
|
|
||||||
const getPreviousContent = (group: Group, currentSlug: string) => {
|
const getPreviousContent = (group: Group, currentSlug: string) => {
|
||||||
for (let index = 0; index < group.length; index += 1) {
|
for (let index = 0; index < group.length; index++) {
|
||||||
const content = group[index];
|
const content = group[index];
|
||||||
if (content.attributes?.slug === currentSlug && index > 0) {
|
if (content.attributes?.slug === currentSlug && index > 0) {
|
||||||
return group[index - 1];
|
return group[index - 1];
|
||||||
|
@ -537,7 +547,7 @@ const getPreviousContent = (group: Group, currentSlug: string) => {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
const getNextContent = (group: Group, currentSlug: string) => {
|
const getNextContent = (group: Group, currentSlug: string) => {
|
||||||
for (let index = 0; index < group.length; index += 1) {
|
for (let index = 0; index < group.length; index++) {
|
||||||
const content = group[index];
|
const content = group[index];
|
||||||
if (content.attributes?.slug === currentSlug && index < group.length - 1) {
|
if (content.attributes?.slug === currentSlug && index < group.length - 1) {
|
||||||
return group[index + 1];
|
return group[index + 1];
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useState, useMemo, useCallback } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Chip } from "components/Chip";
|
|
||||||
import { Select } from "components/Inputs/Select";
|
import { Select } from "components/Inputs/Select";
|
||||||
import { Switch } from "components/Inputs/Switch";
|
import { Switch } from "components/Inputs/Switch";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
|
@ -12,20 +13,16 @@ import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
||||||
import { GetStaticProps } from "next";
|
import { TextInput } from "components/Inputs/TextInput";
|
||||||
import { Fragment, useState, useMemo } from "react";
|
|
||||||
import { Icon } from "components/Ico";
|
|
||||||
import { WithLabel } from "components/Inputs/WithLabel";
|
import { WithLabel } from "components/Inputs/WithLabel";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { TextInput } from "components/Inputs/TextInput";
|
|
||||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||||
import {
|
import { Icon } from "components/Ico";
|
||||||
filterHasAttributes,
|
import { filterDefined, filterHasAttributes } from "helpers/others";
|
||||||
iterateMap,
|
|
||||||
mapRemoveEmptyValues,
|
|
||||||
} from "helpers/others";
|
|
||||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
|
||||||
import { GetContentsQuery } from "graphql/generated";
|
import { GetContentsQuery } from "graphql/generated";
|
||||||
|
import { SmartList } from "components/SmartList";
|
||||||
|
import { SelectiveRequiredNonNullable } from "helpers/types";
|
||||||
|
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -74,14 +71,72 @@ const Contents = ({
|
||||||
[combineRelatedContent, searchName.length]
|
[combineRelatedContent, searchName.length]
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredItems = useMemo(
|
const groupingFunction = useCallback(
|
||||||
() => filterContents(contents, effectiveCombineRelatedContent, searchName),
|
(
|
||||||
[effectiveCombineRelatedContent, contents, searchName]
|
item: SelectiveRequiredNonNullable<
|
||||||
|
NonNullable<GetContentsQuery["contents"]>["data"][number],
|
||||||
|
"attributes" | "id"
|
||||||
|
>
|
||||||
|
): string[] => {
|
||||||
|
switch (groupingMethod) {
|
||||||
|
case 0: {
|
||||||
|
const categories = filterHasAttributes(
|
||||||
|
item.attributes.categories?.data
|
||||||
|
);
|
||||||
|
if (categories.length > 0) {
|
||||||
|
return categories.map((category) => category.attributes.name);
|
||||||
|
}
|
||||||
|
return [langui.no_category ?? "No category"];
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
return [
|
||||||
|
item.attributes.type?.data?.attributes?.titles?.[0]?.title ??
|
||||||
|
item.attributes.type?.data?.attributes?.slug
|
||||||
|
? prettySlug(item.attributes.type.data.attributes.slug)
|
||||||
|
: langui.no_type ?? "No type",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return [""];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[groupingMethod, langui]
|
||||||
);
|
);
|
||||||
|
|
||||||
const groups = useMemo(
|
const filteringFunction = useCallback(
|
||||||
() => getGroups(langui, groupingMethod, filteredItems),
|
(
|
||||||
[langui, groupingMethod, filteredItems]
|
item: SelectiveRequiredNonNullable<
|
||||||
|
Props["contents"][number],
|
||||||
|
"attributes" | "id"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
effectiveCombineRelatedContent &&
|
||||||
|
item.attributes.group?.data?.attributes?.combine === true &&
|
||||||
|
item.attributes.group.data.attributes.contents?.data[0].id !== item.id
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (searchName.length > 1) {
|
||||||
|
if (
|
||||||
|
filterDefined(item.attributes.translations).find((translation) =>
|
||||||
|
prettyinlineTitle(
|
||||||
|
translation.pre_title,
|
||||||
|
translation.title,
|
||||||
|
translation.subtitle
|
||||||
|
)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(searchName.toLowerCase())
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[effectiveCombineRelatedContent, searchName]
|
||||||
);
|
);
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
|
@ -161,107 +216,91 @@ const Contents = ({
|
||||||
const contentPanel = useMemo(
|
const contentPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||||
{groups.size === 0 && (
|
<SmartList
|
||||||
<ContentPlaceholder
|
items={filterHasAttributes(contents)}
|
||||||
message={langui.no_results_message ?? "No results"}
|
getItemId={(item) => item.id}
|
||||||
icon={Icon.ChevronLeft}
|
renderItem={({ item }) => (
|
||||||
/>
|
<>
|
||||||
)}
|
{item.attributes.translations && (
|
||||||
{iterateMap(
|
<TranslatedPreviewCard
|
||||||
groups,
|
href={`/contents/${item.attributes.slug}`}
|
||||||
(name, items, index) =>
|
translations={item.attributes.translations.map(
|
||||||
items.length > 0 && (
|
(translation) => ({
|
||||||
<Fragment key={index}>
|
pre_title: translation?.pre_title,
|
||||||
{name && (
|
title: translation?.title,
|
||||||
<h2
|
subtitle: translation?.subtitle,
|
||||||
className="flex flex-row place-items-center gap-2 pb-2 pt-10 text-2xl
|
language: translation?.language?.data?.attributes?.code,
|
||||||
first-of-type:pt-0"
|
})
|
||||||
>
|
)}
|
||||||
{name}
|
slug={item.attributes.slug}
|
||||||
<Chip>{`${items.reduce((currentSum, item) => {
|
languages={languages}
|
||||||
if (effectiveCombineRelatedContent) {
|
thumbnail={item.attributes.thumbnail?.data?.attributes}
|
||||||
if (
|
thumbnailAspectRatio="3/2"
|
||||||
item.attributes?.group?.data?.attributes?.combine ===
|
thumbnailForceAspectRatio
|
||||||
true
|
stackNumber={
|
||||||
) {
|
effectiveCombineRelatedContent &&
|
||||||
return (
|
item.attributes.group?.data?.attributes?.combine === true
|
||||||
currentSum +
|
? item.attributes.group.data.attributes.contents?.data
|
||||||
(item.attributes.group.data.attributes.contents
|
.length
|
||||||
?.data.length ?? 1)
|
: 0
|
||||||
);
|
}
|
||||||
}
|
topChips={
|
||||||
}
|
item.attributes.type?.data?.attributes
|
||||||
return currentSum + 1;
|
? [
|
||||||
}, 0)} ${
|
item.attributes.type.data.attributes.titles?.[0]
|
||||||
items.length <= 1
|
? item.attributes.type.data.attributes.titles[0]
|
||||||
? langui.result?.toLowerCase() ?? ""
|
?.title
|
||||||
: langui.results?.toLowerCase() ?? ""
|
: prettySlug(
|
||||||
}`}</Chip>
|
item.attributes.type.data.attributes.slug
|
||||||
</h2>
|
),
|
||||||
)}
|
]
|
||||||
|
: undefined
|
||||||
<div
|
}
|
||||||
className="grid grid-cols-2 items-end gap-8
|
bottomChips={item.attributes.categories?.data.map(
|
||||||
desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))] mobile:gap-4"
|
(category) => category.attributes?.short ?? ""
|
||||||
>
|
)}
|
||||||
{filterHasAttributes(items).map((item) => (
|
keepInfoVisible={keepInfoVisible}
|
||||||
<Fragment key={item.id}>
|
/>
|
||||||
{item.attributes.translations && (
|
)}
|
||||||
<TranslatedPreviewCard
|
</>
|
||||||
href={`/contents/${item.attributes.slug}`}
|
)}
|
||||||
translations={item.attributes.translations.map(
|
renderWhenEmpty={() => (
|
||||||
(translation) => ({
|
<ContentPlaceholder
|
||||||
pre_title: translation?.pre_title,
|
message={langui.no_results_message ?? "No results"}
|
||||||
title: translation?.title,
|
icon={Icon.ChevronLeft}
|
||||||
subtitle: translation?.subtitle,
|
/>
|
||||||
language:
|
)}
|
||||||
translation?.language?.data?.attributes?.code,
|
className="grid-cols-2 items-end desktop:grid-cols-[repeat(auto-fill,_minmax(15rem,1fr))]"
|
||||||
})
|
groupingFunction={groupingFunction}
|
||||||
)}
|
filteringFunction={filteringFunction}
|
||||||
slug={item.attributes.slug}
|
searchingTerm={searchName}
|
||||||
languages={languages}
|
searchingBy={(item) =>
|
||||||
thumbnail={
|
`
|
||||||
item.attributes.thumbnail?.data?.attributes
|
${item.attributes.slug}
|
||||||
}
|
${filterDefined(item.attributes.translations)
|
||||||
thumbnailAspectRatio="3/2"
|
.map((translation) =>
|
||||||
thumbnailForceAspectRatio
|
prettyinlineTitle(
|
||||||
stackNumber={
|
translation.pre_title,
|
||||||
effectiveCombineRelatedContent &&
|
translation.title,
|
||||||
item.attributes.group?.data?.attributes?.combine ===
|
translation.subtitle
|
||||||
true
|
)
|
||||||
? item.attributes.group.data.attributes.contents
|
)
|
||||||
?.data.length
|
.join(" ")}`
|
||||||
: 0
|
}
|
||||||
}
|
langui={langui}
|
||||||
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}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</ContentPanel>
|
</ContentPanel>
|
||||||
),
|
),
|
||||||
[effectiveCombineRelatedContent, groups, keepInfoVisible, languages, langui]
|
[
|
||||||
|
contents,
|
||||||
|
effectiveCombineRelatedContent,
|
||||||
|
filteringFunction,
|
||||||
|
groupingFunction,
|
||||||
|
keepInfoVisible,
|
||||||
|
languages,
|
||||||
|
langui,
|
||||||
|
searchName,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -303,107 +342,3 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
props: props,
|
props: props,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* ╭───────────────────╮
|
|
||||||
* ─────────────────────────────────────╯ PRIVATE METHODS ╰───────────────────────────────────────
|
|
||||||
*/
|
|
||||||
|
|
||||||
type GroupContentItems = Map<string, Props["contents"]>;
|
|
||||||
|
|
||||||
export const getGroups = (
|
|
||||||
langui: AppStaticProps["langui"],
|
|
||||||
groupByType: number,
|
|
||||||
items: Props["contents"]
|
|
||||||
): GroupContentItems => {
|
|
||||||
const groups: GroupContentItems = new Map();
|
|
||||||
|
|
||||||
switch (groupByType) {
|
|
||||||
case 0: {
|
|
||||||
const noCategory = langui.no_category ?? "No category";
|
|
||||||
groups.set("Drakengard 1", []);
|
|
||||||
groups.set("Drakengard 1.3", []);
|
|
||||||
groups.set("Drakengard 2", []);
|
|
||||||
groups.set("Drakengard 3", []);
|
|
||||||
groups.set("Drakengard 4", []);
|
|
||||||
groups.set("NieR Gestalt", []);
|
|
||||||
groups.set("NieR Replicant", []);
|
|
||||||
groups.set("NieR Replicant ver.1.22474487139...", []);
|
|
||||||
groups.set("NieR:Automata", []);
|
|
||||||
groups.set("NieR Re[in]carnation", []);
|
|
||||||
groups.set("SINoALICE", []);
|
|
||||||
groups.set("Voice of Cards", []);
|
|
||||||
groups.set("Final Fantasy XIV", []);
|
|
||||||
groups.set("Thou Shalt Not Die", []);
|
|
||||||
groups.set("Bakuken", []);
|
|
||||||
groups.set("YoRHa", []);
|
|
||||||
groups.set("YoRHa Boys", []);
|
|
||||||
groups.set(noCategory, []);
|
|
||||||
|
|
||||||
items.map((item) => {
|
|
||||||
if (item.attributes?.categories?.data.length === 0) {
|
|
||||||
groups.get(noCategory)?.push(item);
|
|
||||||
} else {
|
|
||||||
item.attributes?.categories?.data.map((category) => {
|
|
||||||
groups.get(category.attributes?.name ?? noCategory)?.push(item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 1: {
|
|
||||||
items.map((item) => {
|
|
||||||
const noType = langui.no_type ?? "No type";
|
|
||||||
const type =
|
|
||||||
item.attributes?.type?.data?.attributes?.titles?.[0]?.title ??
|
|
||||||
item.attributes?.type?.data?.attributes?.slug
|
|
||||||
? prettySlug(item.attributes.type.data.attributes.slug)
|
|
||||||
: langui.no_type;
|
|
||||||
if (!groups.has(type ?? noType)) groups.set(type ?? noType, []);
|
|
||||||
groups.get(type ?? noType)?.push(item);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
groups.set("", items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mapRemoveEmptyValues(groups);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
||||||
|
|
||||||
export const filterContents = (
|
|
||||||
contents: Props["contents"],
|
|
||||||
combineRelatedContent: boolean,
|
|
||||||
searchName: string
|
|
||||||
): Props["contents"] =>
|
|
||||||
contents.filter((content) => {
|
|
||||||
if (
|
|
||||||
combineRelatedContent &&
|
|
||||||
content.attributes?.group?.data?.attributes?.combine === true &&
|
|
||||||
content.attributes.group.data.attributes.contents?.data[0].id !==
|
|
||||||
content.id
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (searchName.length > 1) {
|
|
||||||
if (
|
|
||||||
content.attributes?.translations?.find((translation) =>
|
|
||||||
prettyinlineTitle(
|
|
||||||
translation?.pre_title,
|
|
||||||
translation?.title,
|
|
||||||
translation?.subtitle
|
|
||||||
)
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchName.toLowerCase())
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
|
@ -10,8 +12,6 @@ import { DevGetContentsQuery } from "graphql/generated";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { filterDefined, filterHasAttributes } from "helpers/others";
|
import { filterDefined, filterHasAttributes } from "helpers/others";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
|
@ -13,9 +15,6 @@ import {
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
|
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useCallback, useMemo, useRef, useState } from "react";
|
||||||
|
import TurndownService from "turndown";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { Markdawn, TableOfContents } from "components/Markdown/Markdawn";
|
import { Markdawn, TableOfContents } from "components/Markdown/Markdawn";
|
||||||
|
@ -8,9 +11,6 @@ import {
|
||||||
import { Popup } from "components/Popup";
|
import { Popup } from "components/Popup";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { useCallback, useMemo, useRef, useState } from "react";
|
|
||||||
import TurndownService from "turndown";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useCallback, useMemo, useRef, useState } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
||||||
|
@ -7,8 +9,6 @@ import {
|
||||||
} from "components/Panels/ContentPanel";
|
} from "components/Panels/ContentPanel";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { useCallback, useMemo, useRef, useState } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Fragment, useCallback, useMemo, useState } from "react";
|
||||||
|
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { Img } from "components/Img";
|
import { Img } from "components/Img";
|
||||||
|
@ -40,12 +42,10 @@ import {
|
||||||
filterHasAttributes,
|
filterHasAttributes,
|
||||||
isDefined,
|
isDefined,
|
||||||
isDefinedAndNotEmpty,
|
isDefinedAndNotEmpty,
|
||||||
sortContent,
|
sortRangedContent,
|
||||||
} from "helpers/others";
|
} from "helpers/others";
|
||||||
import { useLightBox } from "hooks/useLightBox";
|
import { useLightBox } from "hooks/useLightBox";
|
||||||
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
import { AnchorIds, useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
|
||||||
import { Fragment, useCallback, useMemo, useState } from "react";
|
|
||||||
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||||
import { WithLabel } from "components/Inputs/WithLabel";
|
import { WithLabel } from "components/Inputs/WithLabel";
|
||||||
|
@ -626,7 +626,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
language_code: context.locale ?? "en",
|
language_code: context.locale ?? "en",
|
||||||
});
|
});
|
||||||
if (!item.libraryItems?.data[0]?.attributes) return { notFound: true };
|
if (!item.libraryItems?.data[0]?.attributes) return { notFound: true };
|
||||||
sortContent(item.libraryItems.data[0].attributes.contents);
|
sortRangedContent(item.libraryItems.data[0].attributes.contents);
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...(await getAppStaticProps(context)),
|
...(await getAppStaticProps(context)),
|
||||||
item: item.libraryItems.data[0].attributes,
|
item: item.libraryItems.data[0].attributes,
|
||||||
|
@ -702,6 +702,8 @@ const ContentLine = ({
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(prettySlug(slug, parentSlug));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
|
@ -722,7 +724,7 @@ const ContentLine = ({
|
||||||
selectedTranslation.subtitle
|
selectedTranslation.subtitle
|
||||||
)
|
)
|
||||||
: content
|
: content
|
||||||
? prettySlug(content.slug)
|
? prettySlug(content.slug, parentSlug)
|
||||||
: prettySlug(slug, parentSlug)}
|
: prettySlug(slug, parentSlug)}
|
||||||
</h3>
|
</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||||
|
import { Fragment, useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { ScanSet } from "components/Library/ScanSet";
|
import { ScanSet } from "components/Library/ScanSet";
|
||||||
import { ScanSetCover } from "components/Library/ScanSetCover";
|
import { ScanSetCover } from "components/Library/ScanSetCover";
|
||||||
|
@ -15,11 +17,12 @@ import { GetLibraryItemScansQuery } from "graphql/generated";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
import { prettyinlineTitle, prettySlug } from "helpers/formatters";
|
||||||
import { filterHasAttributes, isDefined, sortContent } from "helpers/others";
|
import {
|
||||||
|
filterHasAttributes,
|
||||||
|
isDefined,
|
||||||
|
sortRangedContent,
|
||||||
|
} from "helpers/others";
|
||||||
import { useLightBox } from "hooks/useLightBox";
|
import { useLightBox } from "hooks/useLightBox";
|
||||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
|
||||||
import { Fragment, useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -44,7 +47,6 @@ const LibrarySlug = ({
|
||||||
...otherProps
|
...otherProps
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const [openLightBox, LightBox] = useLightBox();
|
const [openLightBox, LightBox] = useLightBox();
|
||||||
sortContent(item.contents);
|
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
@ -158,6 +160,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
});
|
});
|
||||||
if (!item.libraryItems?.data[0]?.attributes || !item.libraryItems.data[0]?.id)
|
if (!item.libraryItems?.data[0]?.attributes || !item.libraryItems.data[0]?.id)
|
||||||
return { notFound: true };
|
return { notFound: true };
|
||||||
|
sortRangedContent(item.libraryItems.data[0].attributes.contents);
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...(await getAppStaticProps(context)),
|
...(await getAppStaticProps(context)),
|
||||||
item: item.libraryItems.data[0].attributes,
|
item: item.libraryItems.data[0].attributes,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useState, useMemo, useCallback } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Chip } from "components/Chip";
|
|
||||||
import { Select } from "components/Inputs/Select";
|
import { Select } from "components/Inputs/Select";
|
||||||
import { Switch } from "components/Inputs/Switch";
|
import { Switch } from "components/Inputs/Switch";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
|
@ -11,32 +12,29 @@ import { SubPanel } from "components/Panels/SubPanel";
|
||||||
import { GetLibraryItemsPreviewQuery } from "graphql/generated";
|
import { GetLibraryItemsPreviewQuery } from "graphql/generated";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { prettyItemSubType } from "helpers/formatters";
|
import {
|
||||||
import { LibraryItemUserStatus } from "helpers/types";
|
prettyDate,
|
||||||
import { GetStaticProps } from "next";
|
prettyinlineTitle,
|
||||||
import { Fragment, useState, useMemo } from "react";
|
prettyItemSubType,
|
||||||
|
} from "helpers/formatters";
|
||||||
|
import {
|
||||||
|
LibraryItemUserStatus,
|
||||||
|
SelectiveRequiredNonNullable,
|
||||||
|
} from "helpers/types";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { WithLabel } from "components/Inputs/WithLabel";
|
import { WithLabel } from "components/Inputs/WithLabel";
|
||||||
import { TextInput } from "components/Inputs/TextInput";
|
import { TextInput } from "components/Inputs/TextInput";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
|
import { PreviewCardCTAs } from "components/Library/PreviewCardCTAs";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||||
import {
|
|
||||||
filterItems,
|
|
||||||
getGroups,
|
|
||||||
sortBy,
|
|
||||||
isUntangibleGroupItem,
|
|
||||||
} from "helpers/libraryItem";
|
|
||||||
import { PreviewCard } from "components/PreviewCard";
|
import { PreviewCard } from "components/PreviewCard";
|
||||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||||
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
||||||
import {
|
import { filterHasAttributes, isDefined, isUndefined } from "helpers/others";
|
||||||
filterHasAttributes,
|
|
||||||
isDefinedAndNotEmpty,
|
|
||||||
isUndefined,
|
|
||||||
iterateMap,
|
|
||||||
} from "helpers/others";
|
|
||||||
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
|
||||||
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
|
import { convertPrice } from "helpers/numbers";
|
||||||
|
import { SmartList } from "components/SmartList";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -65,12 +63,12 @@ interface Props extends AppStaticProps {
|
||||||
|
|
||||||
const Library = ({
|
const Library = ({
|
||||||
langui,
|
langui,
|
||||||
items: libraryItems,
|
items,
|
||||||
currencies,
|
currencies,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const appLayout = useAppLayout();
|
|
||||||
const hoverable = useMediaHoverable();
|
const hoverable = useMediaHoverable();
|
||||||
|
const appLayout = useAppLayout();
|
||||||
|
|
||||||
const [searchName, setSearchName] = useState(
|
const [searchName, setSearchName] = useState(
|
||||||
DEFAULT_FILTERS_STATE.searchName
|
DEFAULT_FILTERS_STATE.searchName
|
||||||
|
@ -97,36 +95,170 @@ const Library = ({
|
||||||
LibraryItemUserStatus | undefined
|
LibraryItemUserStatus | undefined
|
||||||
>(DEFAULT_FILTERS_STATE.filterUserStatus);
|
>(DEFAULT_FILTERS_STATE.filterUserStatus);
|
||||||
|
|
||||||
const filteredItems = useMemo(
|
const filteringFunction = useCallback(
|
||||||
() =>
|
(
|
||||||
filterItems(
|
item: SelectiveRequiredNonNullable<
|
||||||
appLayout,
|
Props["items"][number],
|
||||||
libraryItems,
|
"attributes" | "id"
|
||||||
searchName,
|
>
|
||||||
showSubitems,
|
) => {
|
||||||
showPrimaryItems,
|
if (!showSubitems && !item.attributes.root_item) return false;
|
||||||
showSecondaryItems,
|
if (
|
||||||
filterUserStatus
|
showSubitems &&
|
||||||
),
|
isUntangibleGroupItem(item.attributes.metadata?.[0])
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (item.attributes.primary && !showPrimaryItems) return false;
|
||||||
|
if (!item.attributes.primary && !showSecondaryItems) return false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
isDefined(filterUserStatus) &&
|
||||||
|
item.id &&
|
||||||
|
appLayout.libraryItemUserStatus
|
||||||
|
) {
|
||||||
|
if (isUntangibleGroupItem(item.attributes.metadata?.[0])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (filterUserStatus === LibraryItemUserStatus.None) {
|
||||||
|
if (appLayout.libraryItemUserStatus[item.id]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
filterUserStatus !== appLayout.libraryItemUserStatus[item.id]
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
[
|
[
|
||||||
appLayout,
|
appLayout.libraryItemUserStatus,
|
||||||
filterUserStatus,
|
filterUserStatus,
|
||||||
libraryItems,
|
|
||||||
searchName,
|
|
||||||
showPrimaryItems,
|
showPrimaryItems,
|
||||||
showSecondaryItems,
|
showSecondaryItems,
|
||||||
showSubitems,
|
showSubitems,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortedItems = useMemo(
|
const sortingFunction = useCallback(
|
||||||
() => sortBy(sortingMethod, filteredItems, currencies),
|
(
|
||||||
[currencies, filteredItems, sortingMethod]
|
a: SelectiveRequiredNonNullable<
|
||||||
|
Props["items"][number],
|
||||||
|
"attributes" | "id"
|
||||||
|
>,
|
||||||
|
b: SelectiveRequiredNonNullable<
|
||||||
|
Props["items"][number],
|
||||||
|
"attributes" | "id"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
switch (sortingMethod) {
|
||||||
|
case 0: {
|
||||||
|
const titleA = prettyinlineTitle(
|
||||||
|
"",
|
||||||
|
a.attributes.title,
|
||||||
|
a.attributes.subtitle
|
||||||
|
);
|
||||||
|
const titleB = prettyinlineTitle(
|
||||||
|
"",
|
||||||
|
b.attributes.title,
|
||||||
|
b.attributes.subtitle
|
||||||
|
);
|
||||||
|
return titleA.localeCompare(titleB);
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
const priceA = a.attributes.price
|
||||||
|
? convertPrice(a.attributes.price, currencies[0])
|
||||||
|
: 99999;
|
||||||
|
const priceB = b.attributes.price
|
||||||
|
? convertPrice(b.attributes.price, currencies[0])
|
||||||
|
: 99999;
|
||||||
|
return priceA - priceB;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
const dateA = a.attributes.release_date
|
||||||
|
? prettyDate(a.attributes.release_date)
|
||||||
|
: "9999";
|
||||||
|
const dateB = b.attributes.release_date
|
||||||
|
? prettyDate(b.attributes.release_date)
|
||||||
|
: "9999";
|
||||||
|
return dateA.localeCompare(dateB);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currencies, sortingMethod]
|
||||||
);
|
);
|
||||||
|
|
||||||
const groups = useMemo(
|
const groupingFunction = useCallback(
|
||||||
() => getGroups(langui, groupingMethod, sortedItems),
|
(
|
||||||
[langui, groupingMethod, sortedItems]
|
item: SelectiveRequiredNonNullable<
|
||||||
|
Props["items"][number],
|
||||||
|
"attributes" | "id"
|
||||||
|
>
|
||||||
|
): string[] => {
|
||||||
|
switch (groupingMethod) {
|
||||||
|
case 0: {
|
||||||
|
const categories = filterHasAttributes(
|
||||||
|
item.attributes.categories?.data
|
||||||
|
);
|
||||||
|
if (categories.length > 0) {
|
||||||
|
return categories.map((category) => category.attributes.name);
|
||||||
|
}
|
||||||
|
return [langui.no_category ?? "No category"];
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
if (item.attributes.metadata && item.attributes.metadata.length > 0) {
|
||||||
|
switch (item.attributes.metadata[0]?.__typename) {
|
||||||
|
case "ComponentMetadataAudio":
|
||||||
|
return [langui.audio ?? "Audio"];
|
||||||
|
case "ComponentMetadataGame":
|
||||||
|
return [langui.game ?? "Game"];
|
||||||
|
case "ComponentMetadataBooks":
|
||||||
|
return [langui.textual ?? "Textual"];
|
||||||
|
case "ComponentMetadataVideo":
|
||||||
|
return [langui.video ?? "Video"];
|
||||||
|
case "ComponentMetadataOther":
|
||||||
|
return [langui.other ?? "Other"];
|
||||||
|
case "ComponentMetadataGroup": {
|
||||||
|
switch (
|
||||||
|
item.attributes.metadata[0]?.subitems_type?.data?.attributes
|
||||||
|
?.slug
|
||||||
|
) {
|
||||||
|
case "audio":
|
||||||
|
return [langui.audio ?? "Audio"];
|
||||||
|
case "video":
|
||||||
|
return [langui.video ?? "Video"];
|
||||||
|
case "game":
|
||||||
|
return [langui.game ?? "Game"];
|
||||||
|
case "textual":
|
||||||
|
return [langui.textual ?? "Textual"];
|
||||||
|
case "mixed":
|
||||||
|
return [langui.group ?? "Group"];
|
||||||
|
default: {
|
||||||
|
return [langui.no_type ?? "No type"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return [langui.no_type ?? "No type"];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return [langui.no_type ?? "No type"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
if (item.attributes.release_date?.year) {
|
||||||
|
return [item.attributes.release_date.year.toString()];
|
||||||
|
}
|
||||||
|
return [langui.no_year ?? "No year"];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return [""];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[groupingMethod, langui]
|
||||||
);
|
);
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
|
@ -273,72 +405,73 @@ const Library = ({
|
||||||
const contentPanel = useMemo(
|
const contentPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||||
{groups.size === 0 && (
|
<SmartList
|
||||||
<ContentPlaceholder
|
items={filterHasAttributes(items)}
|
||||||
message={langui.no_results_message ?? "No results"}
|
getItemId={(item) => item.id}
|
||||||
icon={Icon.ChevronLeft}
|
renderItem={({ item }) => (
|
||||||
/>
|
<PreviewCard
|
||||||
)}
|
href={`/library/${item.attributes.slug}`}
|
||||||
{iterateMap(groups, (name, items) => (
|
title={item.attributes.title}
|
||||||
<Fragment key={name}>
|
subtitle={item.attributes.subtitle}
|
||||||
{isDefinedAndNotEmpty(name) && (
|
thumbnail={item.attributes.thumbnail?.data?.attributes}
|
||||||
<h2
|
thumbnailAspectRatio="21/29.7"
|
||||||
className="flex flex-row place-items-center gap-2
|
thumbnailRounded={false}
|
||||||
pb-2 pt-10 text-2xl first-of-type:pt-0"
|
keepInfoVisible={keepInfoVisible}
|
||||||
>
|
topChips={
|
||||||
{name}
|
item.attributes.metadata &&
|
||||||
<Chip>{`${items.length} ${
|
item.attributes.metadata.length > 0 &&
|
||||||
items.length <= 1
|
item.attributes.metadata[0]
|
||||||
? langui.result?.toLowerCase() ?? "result"
|
? [prettyItemSubType(item.attributes.metadata[0])]
|
||||||
: langui.results?.toLowerCase() ?? "results"
|
: []
|
||||||
}`}</Chip>
|
}
|
||||||
</h2>
|
bottomChips={item.attributes.categories?.data.map(
|
||||||
)}
|
(category) => category.attributes?.short ?? ""
|
||||||
<div
|
)}
|
||||||
className="grid items-end gap-8 border-b-[3px] border-dotted pb-12
|
metadata={{
|
||||||
last-of-type:border-0 desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]
|
currencies: currencies,
|
||||||
mobile:grid-cols-2 mobile:gap-4"
|
release_date: item.attributes.release_date,
|
||||||
>
|
price: item.attributes.price,
|
||||||
{filterHasAttributes(items).map((item) => (
|
position: "Bottom",
|
||||||
<Fragment key={item.id}>
|
}}
|
||||||
<PreviewCard
|
infoAppend={
|
||||||
href={`/library/${item.attributes.slug}`}
|
!isUntangibleGroupItem(item.attributes.metadata?.[0]) && (
|
||||||
title={item.attributes.title}
|
<PreviewCardCTAs id={item.id} langui={langui} />
|
||||||
subtitle={item.attributes.subtitle}
|
)
|
||||||
thumbnail={item.attributes.thumbnail?.data?.attributes}
|
}
|
||||||
thumbnailAspectRatio="21/29.7"
|
/>
|
||||||
thumbnailRounded={false}
|
)}
|
||||||
keepInfoVisible={keepInfoVisible}
|
renderWhenEmpty={() => (
|
||||||
topChips={
|
<ContentPlaceholder
|
||||||
item.attributes.metadata &&
|
message={langui.no_results_message ?? "No results"}
|
||||||
item.attributes.metadata.length > 0 &&
|
icon={Icon.ChevronLeft}
|
||||||
item.attributes.metadata[0]
|
/>
|
||||||
? [prettyItemSubType(item.attributes.metadata[0])]
|
)}
|
||||||
: []
|
className="grid-cols-2 items-end desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]"
|
||||||
}
|
searchingTerm={searchName}
|
||||||
bottomChips={item.attributes.categories?.data.map(
|
sortingFunction={sortingFunction}
|
||||||
(category) => category.attributes?.short ?? ""
|
groupingFunction={groupingFunction}
|
||||||
)}
|
searchingBy={(item) =>
|
||||||
metadata={{
|
prettyinlineTitle(
|
||||||
currencies: currencies,
|
"",
|
||||||
release_date: item.attributes.release_date,
|
item.attributes.title,
|
||||||
price: item.attributes.price,
|
item.attributes.subtitle
|
||||||
position: "Bottom",
|
)
|
||||||
}}
|
}
|
||||||
infoAppend={
|
filteringFunction={filteringFunction}
|
||||||
!isUntangibleGroupItem(item.attributes.metadata?.[0]) && (
|
langui={langui}
|
||||||
<PreviewCardCTAs id={item.id} langui={langui} />
|
/>
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</ContentPanel>
|
</ContentPanel>
|
||||||
),
|
),
|
||||||
[currencies, groups, keepInfoVisible, langui]
|
[
|
||||||
|
currencies,
|
||||||
|
filteringFunction,
|
||||||
|
groupingFunction,
|
||||||
|
items,
|
||||||
|
keepInfoVisible,
|
||||||
|
langui,
|
||||||
|
searchName,
|
||||||
|
sortingFunction,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import { SubPanel } from "components/Panels/SubPanel";
|
import { SubPanel } from "components/Panels/SubPanel";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import {
|
import {
|
||||||
|
@ -6,7 +7,6 @@ import {
|
||||||
} from "graphql/getPostStaticProps";
|
} from "graphql/getPostStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Switch } from "components/Inputs/Switch";
|
import { Switch } from "components/Inputs/Switch";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
|
@ -6,20 +8,18 @@ import {
|
||||||
ContentPanelWidthSizes,
|
ContentPanelWidthSizes,
|
||||||
} from "components/Panels/ContentPanel";
|
} from "components/Panels/ContentPanel";
|
||||||
import { SubPanel } from "components/Panels/SubPanel";
|
import { SubPanel } from "components/Panels/SubPanel";
|
||||||
import { PreviewCard } from "components/PreviewCard";
|
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
import { GetPostsPreviewQuery } from "graphql/generated";
|
import { GetPostsPreviewQuery } from "graphql/generated";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { prettyDate, prettySlug } from "helpers/formatters";
|
import { prettyDate, prettySlug } from "helpers/formatters";
|
||||||
|
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { Fragment, useMemo, useState } from "react";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { WithLabel } from "components/Inputs/WithLabel";
|
import { WithLabel } from "components/Inputs/WithLabel";
|
||||||
import { TextInput } from "components/Inputs/TextInput";
|
import { TextInput } from "components/Inputs/TextInput";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { useMediaHoverable } from "hooks/useMediaQuery";
|
import { useMediaHoverable } from "hooks/useMediaQuery";
|
||||||
import { filterHasAttributes } from "helpers/others";
|
import { filterDefined, filterHasAttributes } from "helpers/others";
|
||||||
|
import { SmartList } from "components/SmartList";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -40,7 +40,12 @@ interface Props extends AppStaticProps {
|
||||||
posts: NonNullable<GetPostsPreviewQuery["posts"]>["data"];
|
posts: NonNullable<GetPostsPreviewQuery["posts"]>["data"];
|
||||||
}
|
}
|
||||||
|
|
||||||
const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
|
const News = ({
|
||||||
|
langui,
|
||||||
|
posts,
|
||||||
|
languages,
|
||||||
|
...otherProps
|
||||||
|
}: Props): JSX.Element => {
|
||||||
const hoverable = useMediaHoverable();
|
const hoverable = useMediaHoverable();
|
||||||
const [searchName, setSearchName] = useState(
|
const [searchName, setSearchName] = useState(
|
||||||
DEFAULT_FILTERS_STATE.searchName
|
DEFAULT_FILTERS_STATE.searchName
|
||||||
|
@ -49,11 +54,6 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
|
||||||
DEFAULT_FILTERS_STATE.keepInfoVisible
|
DEFAULT_FILTERS_STATE.keepInfoVisible
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredItems = useMemo(
|
|
||||||
() => filterItems(posts, searchName),
|
|
||||||
[posts, searchName]
|
|
||||||
);
|
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
|
@ -96,37 +96,46 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
|
||||||
const contentPanel = useMemo(
|
const contentPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||||
<div
|
<SmartList
|
||||||
className="grid grid-cols-1 items-end gap-8
|
items={filterHasAttributes(posts)}
|
||||||
desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]"
|
getItemId={(post) => post.id}
|
||||||
>
|
langui={langui}
|
||||||
{filterHasAttributes(filteredItems).map((post) => (
|
renderItem={({ item: post }) => (
|
||||||
<Fragment key={post.id}>
|
<TranslatedPreviewCard
|
||||||
<PreviewCard
|
href={`/news/${post.attributes.slug}`}
|
||||||
href={`/news/${post.attributes.slug}`}
|
translations={filterDefined(post.attributes.translations).map(
|
||||||
title={
|
(translation) => ({
|
||||||
post.attributes.translations?.[0]?.title ??
|
language: translation.language?.data?.attributes?.code,
|
||||||
prettySlug(post.attributes.slug)
|
title: translation.title,
|
||||||
}
|
description: translation.excerpt,
|
||||||
description={post.attributes.translations?.[0]?.excerpt}
|
})
|
||||||
thumbnail={post.attributes.thumbnail?.data?.attributes}
|
)}
|
||||||
thumbnailAspectRatio="3/2"
|
languages={languages}
|
||||||
thumbnailForceAspectRatio
|
slug={post.attributes.slug}
|
||||||
bottomChips={post.attributes.categories?.data.map(
|
thumbnail={post.attributes.thumbnail?.data?.attributes}
|
||||||
(category) => category.attributes?.short ?? ""
|
thumbnailAspectRatio="3/2"
|
||||||
)}
|
thumbnailForceAspectRatio
|
||||||
keepInfoVisible={keepInfoVisible}
|
bottomChips={post.attributes.categories?.data.map(
|
||||||
metadata={{
|
(category) => category.attributes?.short ?? ""
|
||||||
release_date: post.attributes.date,
|
)}
|
||||||
position: "Top",
|
keepInfoVisible={keepInfoVisible}
|
||||||
}}
|
metadata={{
|
||||||
/>
|
release_date: post.attributes.date,
|
||||||
</Fragment>
|
position: "Top",
|
||||||
))}
|
}}
|
||||||
</div>
|
/>
|
||||||
|
)}
|
||||||
|
className="grid-cols-1 desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]"
|
||||||
|
searchingTerm={searchName}
|
||||||
|
searchingBy={(post) =>
|
||||||
|
`${prettySlug(post.attributes.slug)} ${post.attributes.translations
|
||||||
|
?.map((translation) => translation?.title)
|
||||||
|
.join(" ")}`
|
||||||
|
}
|
||||||
|
/>
|
||||||
</ContentPanel>
|
</ContentPanel>
|
||||||
),
|
),
|
||||||
[filteredItems, keepInfoVisible]
|
[keepInfoVisible, languages, langui, posts, searchName]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -136,6 +145,7 @@ const News = ({ langui, posts, ...otherProps }: Props): JSX.Element => {
|
||||||
contentPanel={contentPanel}
|
contentPanel={contentPanel}
|
||||||
subPanelIcon={Icon.Search}
|
subPanelIcon={Icon.Search}
|
||||||
langui={langui}
|
langui={langui}
|
||||||
|
languages={languages}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -149,9 +159,7 @@ export default News;
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async (context) => {
|
export const getStaticProps: GetStaticProps = async (context) => {
|
||||||
const sdk = getReadySdk();
|
const sdk = getReadySdk();
|
||||||
const posts = await sdk.getPostsPreview({
|
const posts = await sdk.getPostsPreview();
|
||||||
language_code: context.locale ?? "en",
|
|
||||||
});
|
|
||||||
if (!posts.posts) return { notFound: true };
|
if (!posts.posts) return { notFound: true };
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
...(await getAppStaticProps(context)),
|
...(await getAppStaticProps(context)),
|
||||||
|
@ -175,20 +183,3 @@ const sortPosts = (posts: Props["posts"]): Props["posts"] =>
|
||||||
return dateA.localeCompare(dateB);
|
return dateA.localeCompare(dateB);
|
||||||
})
|
})
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
||||||
|
|
||||||
const filterItems = (posts: Props["posts"], searchName: string) =>
|
|
||||||
posts.filter((post) => {
|
|
||||||
if (searchName.length > 1) {
|
|
||||||
if (
|
|
||||||
post.attributes?.translations?.[0]?.title
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchName.toLowerCase()) === true
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
|
@ -21,8 +23,6 @@ import {
|
||||||
} from "helpers/others";
|
} from "helpers/others";
|
||||||
import { WikiPageWithTranslations } from "helpers/types";
|
import { WikiPageWithTranslations } from "helpers/types";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
|
||||||
import { useCallback, useMemo } from "react";
|
|
||||||
|
|
||||||
interface Props extends AppStaticProps {
|
interface Props extends AppStaticProps {
|
||||||
page: WikiPageWithTranslations;
|
page: WikiPageWithTranslations;
|
||||||
|
@ -71,8 +71,16 @@ const WikiPage = ({
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex place-content-center gap-4">
|
<div className="flex place-content-center gap-3">
|
||||||
<h1 className="text-center text-3xl">{selectedTranslation?.title}</h1>
|
<h1 className="text-center text-3xl">{selectedTranslation?.title}</h1>
|
||||||
|
{selectedTranslation?.aliases &&
|
||||||
|
selectedTranslation.aliases.length > 0 && (
|
||||||
|
<p className="mr-3 text-center text-2xl">
|
||||||
|
{`(${selectedTranslation.aliases
|
||||||
|
.map((alias) => alias?.alias)
|
||||||
|
.join(", ")})`}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<LanguageSwitcher {...languageSwitcherProps} />
|
<LanguageSwitcher {...languageSwitcherProps} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -88,7 +96,9 @@ const WikiPage = ({
|
||||||
<Img image={page.thumbnail.data.attributes} />
|
<Img image={page.thumbnail.data.attributes} />
|
||||||
)}
|
)}
|
||||||
<div className="my-4 grid gap-4 p-4">
|
<div className="my-4 grid gap-4 p-4">
|
||||||
<p className="font-headers text-xl">{langui.categories}</p>
|
<p className="font-headers text-xl font-bold">
|
||||||
|
{langui.categories}
|
||||||
|
</p>
|
||||||
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
||||||
{page.categories?.data.map((category) => (
|
{page.categories?.data.map((category) => (
|
||||||
<Chip key={category.id}>{category.attributes?.name}</Chip>
|
<Chip key={category.id}>{category.attributes?.name}</Chip>
|
||||||
|
@ -96,29 +106,38 @@ const WikiPage = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isDefinedAndNotEmpty(selectedTranslation.summary) && (
|
{isDefinedAndNotEmpty(selectedTranslation.summary) && (
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<p className="font-headers text-lg">{langui.summary}</p>
|
<p className="font-headers text-lg font-bold">
|
||||||
|
{langui.summary}
|
||||||
|
</p>
|
||||||
<p>{selectedTranslation.summary}</p>
|
<p>{selectedTranslation.summary}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{filterHasAttributes(page.definitions, ["translations"]).map(
|
{filterHasAttributes(page.definitions, ["translations"]).map(
|
||||||
(definition, index) => (
|
(definition, index) => (
|
||||||
<DefinitionCard
|
<>
|
||||||
key={index}
|
<DefinitionCard
|
||||||
source={definition.source?.data?.attributes?.name}
|
key={index}
|
||||||
translations={filterHasAttributes(
|
source={definition.source?.data?.attributes?.name}
|
||||||
definition.translations
|
translations={filterHasAttributes(
|
||||||
).map((translation) => ({
|
definition.translations
|
||||||
language: translation.language.data?.attributes?.code,
|
).map((translation) => ({
|
||||||
definition: translation.definition,
|
language: translation.language.data?.attributes?.code,
|
||||||
status: translation.status,
|
definition: translation.definition,
|
||||||
}))}
|
status: translation.status,
|
||||||
index={index + 1}
|
}))}
|
||||||
languages={languages}
|
index={index + 1}
|
||||||
langui={langui}
|
languages={languages}
|
||||||
/>
|
langui={langui}
|
||||||
|
categories={filterHasAttributes(
|
||||||
|
definition.categories?.data
|
||||||
|
).map((category) => category.attributes.short)}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { Fragment, useMemo } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { InsetBox } from "components/InsetBox";
|
import { InsetBox } from "components/InsetBox";
|
||||||
import { NavOption } from "components/PanelComponents/NavOption";
|
import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
|
@ -13,8 +15,6 @@ import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { prettySlug } from "helpers/formatters";
|
import { prettySlug } from "helpers/formatters";
|
||||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { Fragment, useMemo } from "react";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -49,13 +49,10 @@ const Chronology = ({
|
||||||
(chronologyEras[currentChronologyEraIndex].attributes?.ending_year ??
|
(chronologyEras[currentChronologyEraIndex].attributes?.ending_year ??
|
||||||
999999)
|
999999)
|
||||||
) {
|
) {
|
||||||
currentChronologyEraIndex += 1;
|
currentChronologyEraIndex++;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
Object.prototype.hasOwnProperty.call(
|
Object.hasOwn(memo[currentChronologyEraIndex], item.attributes.year)
|
||||||
memo[currentChronologyEraIndex],
|
|
||||||
item.attributes.year
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
memo[currentChronologyEraIndex][item.attributes.year].push(item);
|
memo[currentChronologyEraIndex][item.attributes.year].push(item);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { GetStaticProps } from "next";
|
||||||
|
import { Fragment, useMemo, useState } from "react";
|
||||||
import { AppLayout } from "components/AppLayout";
|
import { AppLayout } from "components/AppLayout";
|
||||||
import { NavOption } from "components/PanelComponents/NavOption";
|
import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
import { PanelHeader } from "components/PanelComponents/PanelHeader";
|
||||||
import { SubPanel } from "components/Panels/SubPanel";
|
import { SubPanel } from "components/Panels/SubPanel";
|
||||||
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
|
||||||
import { GetStaticProps } from "next";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { GetWikiPagesPreviewsQuery } from "graphql/generated";
|
import { GetWikiPagesPreviewsQuery } from "graphql/generated";
|
||||||
|
@ -12,7 +12,6 @@ import {
|
||||||
ContentPanel,
|
ContentPanel,
|
||||||
ContentPanelWidthSizes,
|
ContentPanelWidthSizes,
|
||||||
} from "components/Panels/ContentPanel";
|
} from "components/Panels/ContentPanel";
|
||||||
import { Fragment, useMemo, useState } from "react";
|
|
||||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
|
@ -98,7 +97,9 @@ const Wiki = ({
|
||||||
/>
|
/>
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<p className="mb-4 font-headers text-xl">{langui.special_pages}</p>
|
<p className="mb-4 font-headers text-xl font-bold">
|
||||||
|
{langui.special_pages}
|
||||||
|
</p>
|
||||||
|
|
||||||
<NavOption title={langui.chronology} url="/wiki/chronology" border />
|
<NavOption title={langui.chronology} url="/wiki/chronology" border />
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
|
@ -127,6 +128,12 @@ const Wiki = ({
|
||||||
translations={page.attributes.translations.map(
|
translations={page.attributes.translations.map(
|
||||||
(translation) => ({
|
(translation) => ({
|
||||||
title: translation?.title,
|
title: translation?.title,
|
||||||
|
subtitle:
|
||||||
|
translation?.aliases && translation.aliases.length > 0
|
||||||
|
? translation.aliases
|
||||||
|
.map((alias) => alias?.alias)
|
||||||
|
.join(" | ")
|
||||||
|
: undefined,
|
||||||
description: translation?.summary,
|
description: translation?.summary,
|
||||||
language: translation?.language?.data?.attributes?.code,
|
language: translation?.language?.data?.attributes?.code,
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue