From 9f63334786bf13fb42baae1df568b5cfb1980403 Mon Sep 17 00:00:00 2001 From: nijesmik Date: Wed, 22 Oct 2025 03:59:45 +0900 Subject: [PATCH 01/78] =?UTF-8?q?:heavy=5Fplus=5Fsign:=20@tanstack/react-q?= =?UTF-8?q?uery=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 27 +++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 28 insertions(+) diff --git a/package-lock.json b/package-lock.json index d2ff10b6..857dbdef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@tanstack/react-query": "^5.90.5", "axios": "^1.6.8", "lucide-react": "^0.522.0", "phaser": "^3.80.0", @@ -1787,6 +1788,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@tanstack/query-core": { + "version": "5.90.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.5.tgz", + "integrity": "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.5.tgz", + "integrity": "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", diff --git a/package.json b/package.json index d443313b..cb6d4d2e 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "@tanstack/react-query": "^5.90.5", "axios": "^1.6.8", "lucide-react": "^0.522.0", "phaser": "^3.80.0", From c27704b6fcbd73aebd03a981e2178b16bb086920 Mon Sep 17 00:00:00 2001 From: nijesmik Date: Wed, 22 Oct 2025 16:08:00 +0900 Subject: [PATCH 02/78] =?UTF-8?q?:sparkles:=20React=20Query=20Provider=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/index.tsx b/src/app/index.tsx index 5795af09..3d7f76f0 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,3 +1,4 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { RouterProvider } from 'react-router-dom'; import router from './router'; @@ -6,13 +7,17 @@ import FullScreenPrompt from '@/components/prompt'; import { useSoundSetting } from '@/features/sound'; +const queryClient = new QueryClient(); + const App = () => { const { playBgm } = useSoundSetting(); return (
- + + +
); }; From 00565f69612ffcf3dc3ebfb89b0a44b0207dbe98 Mon Sep 17 00:00:00 2001 From: nijesmik Date: Wed, 22 Oct 2025 18:45:07 +0900 Subject: [PATCH 03/78] =?UTF-8?q?:heavy=5Fplus=5Fsign:=20jotai=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 108 +++++++++++++++++++++++++++++----------------- package.json | 1 + 2 files changed, 70 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 857dbdef..32b25834 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@tanstack/react-query": "^5.90.5", "axios": "^1.6.8", + "jotai": "^2.15.0", "lucide-react": "^0.522.0", "phaser": "^3.80.0", "react": "^18.2.0", @@ -89,7 +90,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", @@ -104,7 +105,7 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -114,7 +115,7 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -145,7 +146,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, + "devOptional": true, "bin": { "semver": "bin/semver.js" } @@ -154,7 +155,7 @@ "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.3", @@ -171,7 +172,7 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.2", @@ -188,7 +189,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -198,7 +199,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -208,7 +209,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -222,7 +223,7 @@ "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", @@ -249,7 +250,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -259,7 +260,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -269,7 +270,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -279,7 +280,7 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", @@ -293,7 +294,7 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.4" @@ -379,7 +380,7 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -394,7 +395,7 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -413,7 +414,7 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -1128,7 +1129,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1139,7 +1140,7 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1150,7 +1151,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6.0.0" } @@ -1171,14 +1172,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -3078,7 +3079,7 @@ "version": "2.8.7", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.7.tgz", "integrity": "sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -3122,7 +3123,7 @@ "version": "4.26.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -3201,7 +3202,7 @@ "version": "1.0.30001745", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -3324,7 +3325,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "devOptional": true }, "node_modules/core-js-pure": { "version": "3.36.0", @@ -3399,7 +3400,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3563,7 +3564,7 @@ "version": "1.5.224", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.224.tgz", "integrity": "sha512-kWAoUu/bwzvnhpdZSIc6KUyvkI1rbRXMT0Eq8pKReyOyaPZcctMli+EgvcN1PAvwVc7Tdo4Fxi2PsLNDU05mdg==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/emoji-regex": { @@ -3763,7 +3764,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -4665,7 +4666,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6.9.0" } @@ -5367,6 +5368,35 @@ "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", "dev": true }, + "node_modules/jotai": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.15.0.tgz", + "integrity": "sha512-nbp/6jN2Ftxgw0VwoVnOg0m5qYM1rVcfvij+MZx99Z5IK13eGve9FJoCwGv+17JvVthTjhSmNtT5e1coJnr6aw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0", + "@babel/template": ">=7.0.0", + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@babel/template": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5388,7 +5418,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -5419,7 +5449,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, + "devOptional": true, "bin": { "json5": "lib/cli.js" }, @@ -5560,7 +5590,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -5749,7 +5779,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/nanoid": { @@ -5780,7 +5810,7 @@ "version": "2.0.21", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/object-assign": { @@ -6082,7 +6112,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/picomatch": { @@ -7528,7 +7558,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -7988,7 +8018,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index cb6d4d2e..5f7ecba9 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@tanstack/react-query": "^5.90.5", "axios": "^1.6.8", + "jotai": "^2.15.0", "lucide-react": "^0.522.0", "phaser": "^3.80.0", "react": "^18.2.0", From 48be1afe4bb4f7982789ad897f06029111e34f4b Mon Sep 17 00:00:00 2001 From: nijesmik Date: Wed, 22 Oct 2025 22:15:57 +0900 Subject: [PATCH 04/78] =?UTF-8?q?:truck:=20`App.tsx`=EC=97=90=EC=84=9C=20`?= =?UTF-8?q?queryClient`=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/index.tsx | 5 +-- src/features/api/axios.ts | 73 ++++++++++++++++++++++++++++++++ src/features/api/constants.ts | 1 - src/features/api/index.ts | 75 +-------------------------------- src/features/api/react-query.ts | 3 ++ 5 files changed, 80 insertions(+), 77 deletions(-) create mode 100644 src/features/api/axios.ts delete mode 100644 src/features/api/constants.ts create mode 100644 src/features/api/react-query.ts diff --git a/src/app/index.tsx b/src/app/index.tsx index 3d7f76f0..0d2b281d 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,14 +1,13 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import { RouterProvider } from 'react-router-dom'; import router from './router'; import FullScreenPrompt from '@/components/prompt'; +import { queryClient } from '@/features/api'; import { useSoundSetting } from '@/features/sound'; -const queryClient = new QueryClient(); - const App = () => { const { playBgm } = useSoundSetting(); diff --git a/src/features/api/axios.ts b/src/features/api/axios.ts new file mode 100644 index 00000000..4d89576b --- /dev/null +++ b/src/features/api/axios.ts @@ -0,0 +1,73 @@ +import axios, { type AxiosResponse } from 'axios'; + +const baseURL = import.meta.env.VITE_API_BASE_URL; + +const client = axios.create({ + baseURL, + headers: { + 'Content-Type': 'application/json', + }, + // withCredentials: true, +}); + +client.interceptors.request.use((config) => { + if (config.headers['X-Bypass-Authorization']) { + return config; + } + + const token = localStorage.getItem('token'); + if (token) { + config.headers.Authorization = token; + } + return config; +}); + +const api = { + post: async ( + requiredToken: boolean, + url: string, + data: P, + ): Promise> => { + return client.post(url, data, { + headers: { + 'X-Bypass-Authorization': !requiredToken, + }, + }); + }, + + get: async ( + requiredToken: boolean, + url: string, + ): Promise> => { + return client.get(url, { + headers: { + 'X-Bypass-Authorization': !requiredToken, + }, + }); + }, + + patch: async ( + requiredToken: boolean, + url: string, + data: P, + ): Promise> => { + return client.patch(url, data, { + headers: { + 'X-Bypass-Authorization': !requiredToken, + }, + }); + }, + + delete: async ( + requiredToken: boolean, + url: string, + ): Promise> => { + return client.delete(url, { + headers: { + 'X-Bypass-Authorization': !requiredToken, + }, + }); + }, +}; + +export { api, client }; diff --git a/src/features/api/constants.ts b/src/features/api/constants.ts deleted file mode 100644 index 1b098b18..00000000 --- a/src/features/api/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; diff --git a/src/features/api/index.ts b/src/features/api/index.ts index 3e55b925..0165cbb1 100644 --- a/src/features/api/index.ts +++ b/src/features/api/index.ts @@ -1,73 +1,2 @@ -import axios, { type AxiosResponse } from 'axios'; - -import { API_BASE_URL } from '@/features/api/constants'; - -const client = axios.create({ - baseURL: API_BASE_URL, - headers: { - 'Content-Type': 'application/json', - }, - // withCredentials: true, -}); - -client.interceptors.request.use((config) => { - if (config.headers['X-Bypass-Authorization']) { - return config; - } - - const token = localStorage.getItem('token'); - if (token) { - config.headers.Authorization = token; - } - return config; -}); - -const api = { - post: async ( - requiredToken: boolean, - url: string, - data: P, - ): Promise> => { - return client.post(url, data, { - headers: { - 'X-Bypass-Authorization': !requiredToken, - }, - }); - }, - - get: async ( - requiredToken: boolean, - url: string, - ): Promise> => { - return client.get(url, { - headers: { - 'X-Bypass-Authorization': !requiredToken, - }, - }); - }, - - patch: async ( - requiredToken: boolean, - url: string, - data: P, - ): Promise> => { - return client.patch(url, data, { - headers: { - 'X-Bypass-Authorization': !requiredToken, - }, - }); - }, - - delete: async ( - requiredToken: boolean, - url: string, - ): Promise> => { - return client.delete(url, { - headers: { - 'X-Bypass-Authorization': !requiredToken, - }, - }); - }, -}; - -export { api }; +export { api, client } from './axios'; +export { queryClient } from './react-query'; diff --git a/src/features/api/react-query.ts b/src/features/api/react-query.ts new file mode 100644 index 00000000..6d46de59 --- /dev/null +++ b/src/features/api/react-query.ts @@ -0,0 +1,3 @@ +import { QueryClient } from '@tanstack/react-query'; + +export const queryClient = new QueryClient(); From 13bf938fde8c5cb5365ed8377eed71b094fb6cbb Mon Sep 17 00:00:00 2001 From: nijesmik Date: Wed, 22 Oct 2025 22:19:53 +0900 Subject: [PATCH 05/78] =?UTF-8?q?:truck:=20`User`=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/user/type.d.ts | 13 +++++++++++++ src/types/auth.d.ts | 14 +------------- 2 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 src/features/user/type.d.ts diff --git a/src/features/user/type.d.ts b/src/features/user/type.d.ts new file mode 100644 index 00000000..243ea424 --- /dev/null +++ b/src/features/user/type.d.ts @@ -0,0 +1,13 @@ +type User = { + exp: number; + expRequire: number; + guest: boolean; + level: number; + nickname: string; + rating: number; + skin: string; + tier: tierRange; + label: string; + rank: number; + userCode: string; +}; diff --git a/src/types/auth.d.ts b/src/types/auth.d.ts index 0fe7c993..02ae42ed 100644 --- a/src/types/auth.d.ts +++ b/src/types/auth.d.ts @@ -1,16 +1,4 @@ -export interface User { - exp: number; - expRequire: number; - guest: boolean; - level: number; - nickname: string; - rating: number; - skin: string; - tier: tierRange; - label: string; - rank: number; - userCode: string; -} +export { User } from '@/features/user/type'; export interface LogInInfo { id: string; From 2908e2ce96279c41c11811abf536d6cd16f30f74 Mon Sep 17 00:00:00 2001 From: nijesmik Date: Wed, 22 Oct 2025 22:41:59 +0900 Subject: [PATCH 06/78] =?UTF-8?q?:recycle:=20`Collection`=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B0=8F=20=EC=83=81=ED=83=9C=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20#62?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/user/hooks.ts | 29 ++++++ src/features/user/index.ts | 2 +- src/pages/collection/Achievement.tsx | 50 ++++------- src/pages/collection/Label.tsx | 63 ++++--------- src/pages/collection/Skin.tsx | 114 +++++++++++++---------- src/pages/collection/api.ts | 31 +++++++ src/pages/collection/hooks.ts | 24 +++++ src/pages/collection/index.css.ts | 45 ++++++---- src/pages/collection/index.tsx | 120 +------------------------ src/pages/collection/provider.tsx | 25 ++++++ src/pages/collection/store.ts | 27 ++++++ src/pages/collection/ui/menu/index.tsx | 63 +++++++++++++ src/pages/collection/ui/page.tsx | 92 +++++++++++++++++++ src/services/collections.ts | 49 ---------- src/states/collection.ts | 31 ------- src/types/collections.d.ts | 10 +-- 16 files changed, 426 insertions(+), 349 deletions(-) create mode 100644 src/pages/collection/api.ts create mode 100644 src/pages/collection/hooks.ts create mode 100644 src/pages/collection/provider.tsx create mode 100644 src/pages/collection/store.ts create mode 100644 src/pages/collection/ui/menu/index.tsx create mode 100644 src/pages/collection/ui/page.tsx delete mode 100644 src/services/collections.ts delete mode 100644 src/states/collection.ts diff --git a/src/features/user/hooks.ts b/src/features/user/hooks.ts index bf0178e9..54747706 100644 --- a/src/features/user/hooks.ts +++ b/src/features/user/hooks.ts @@ -1,6 +1,10 @@ +import { useQuery } from '@tanstack/react-query'; + import { USER_STATUS } from './constants'; import { useUserStore } from './store'; +import { client, queryClient } from '@/features/api'; + export const useUserStatus = () => { const status = useUserStore((state) => state.loginStatus); @@ -9,3 +13,28 @@ export const useUserStatus = () => { isMember: status === USER_STATUS.member, }; }; + +const getUser = async () => { + return client.get(`/user`).then((response) => { + const { data } = response; + console.debug('user:', data); + return data; + }); +}; + +const queryKey = ['user']; + +export const useUserInfo = () => { + const { data } = useQuery({ + queryKey, + queryFn: getUser, + refetchOnMount: false, + }); + + const refetchUser = () => + queryClient.invalidateQueries({ + queryKey, + }); + + return { user: data, refetchUser }; +}; diff --git a/src/features/user/index.ts b/src/features/user/index.ts index c123d7c5..f3153ae8 100644 --- a/src/features/user/index.ts +++ b/src/features/user/index.ts @@ -1,3 +1,3 @@ export { useUserStore } from './store'; export { USER_STATUS } from './constants'; -export { useUserStatus } from './hooks'; +export { useUserStatus, useUserInfo } from './hooks'; diff --git a/src/pages/collection/Achievement.tsx b/src/pages/collection/Achievement.tsx index d2aad89f..977edf39 100644 --- a/src/pages/collection/Achievement.tsx +++ b/src/pages/collection/Achievement.tsx @@ -1,41 +1,25 @@ -import { useEffect, useState } from 'react'; - import * as styles from './index.css'; -import { Achievements } from '@/types/collections'; - -import { getCollections } from '@/services/collections'; - -const Achievement = () => { - const [achievements, setAchievement] = useState([]); - - useEffect(() => { - getCollections().then((collections) => { - if (collections && collections.achievements) { - setAchievement(collections.achievements); - } - }); - }, []); +interface Props { + achievements: Achievements[]; +} +const Achievement = ({ achievements }: Props) => { return ( -
-
- {achievements.map((achievement) => ( -
-
-
{achievement.name}
-
{achievement.reward}
-
+
+ {achievements.map((achievement) => ( +
+
+
{achievement.name}
+
{achievement.reward}
- ))} -
+
+ ))}
); }; diff --git a/src/pages/collection/Label.tsx b/src/pages/collection/Label.tsx index b46a8767..89bbc74d 100644 --- a/src/pages/collection/Label.tsx +++ b/src/pages/collection/Label.tsx @@ -1,56 +1,23 @@ -import { useEffect, useState } from 'react'; -import {useLoaderData} from "react-router-dom"; - +import { useCollectionActions } from './hooks'; import * as styles from './index.css'; -import {User} from "@/types/auth"; -import { Labels } from '@/types/collections'; - -import { getCollections } from '@/services/collections'; - -import { useCollectionStateStore } from '@/states/collection'; - -const Label = () => { - const user = useLoaderData() as User; - const { setLabel, setLabelName } = useCollectionStateStore(); - const [labels, setLabels] = useState([]); - - useEffect(() => { - if(user.label){ - setLabelName(user.label); - } - - getCollections().then((collections) => { - if (collections && collections.labels) { - setLabels(collections.labels); - } - }); - }, []); - - const labelClick = (label: Labels) => { - if (label.own) { - setLabel(label.id); - setLabelName(label.name); - } - }; +const Label = ({ labels }: { labels: Labels[] }) => { + const { setSelectedLabel } = useCollectionActions(); return ( -
-
- {labels.map((label) => ( -
labelClick(label)} - className={label.own ? styles.achievedBox : styles.notAchievedBox} - > -
-
{label.name}
-
+
+ {labels.map((label) => ( +
+ + ))}
); }; diff --git a/src/pages/collection/Skin.tsx b/src/pages/collection/Skin.tsx index 10b42eb8..048d0b66 100644 --- a/src/pages/collection/Skin.tsx +++ b/src/pages/collection/Skin.tsx @@ -1,59 +1,79 @@ -import { useEffect, useState } from 'react'; +import { memo, useState } from 'react'; +import { useCollectionActions } from './hooks'; import * as styles from './index.css'; -import { Skins } from '@/types/collections'; - import RoundCornerImageBox from '@/components/image-box'; -import { getCollections } from '@/services/collections'; - -import { useCollectionStateStore } from '@/states/collection'; +const getBorderColor = (equipped: boolean, selected: boolean) => { + if (equipped) { + return 'blue'; + } + if (selected) { + return 'yellow'; + } + return 'black'; +}; -const Skin = () => { - const { skinId, setSkinId, setSkinUrl } = useCollectionStateStore(); - const [skins, setSkins] = useState([]); +const SkinItem = memo( + ({ + skin, + onClick, + selected, + }: { + skin: Skins; + onClick: () => void; + selected: boolean; + }) => { + return ( + + ); + }, + (prevProps, nextProps) => { + return ( + prevProps.skin.equipped === nextProps.skin.equipped && + prevProps.selected === nextProps.selected + ); + }, +); - useEffect(() => { - getCollections().then((collections) => { - if (collections && collections.skins) { - setSkins(collections.skins); - setSkinId(-1); - } - }); - }, []); +const Skin = ({ skins }: { skins: Skins[] }) => { + const [selectedId, setSelectedId] = useState(null); + const { setSelectedSkin } = useCollectionActions(); return ( -
-
- {skins.map((skin) => ( -
{ - if (skin.own) { - setSkinId(skin.id); - setSkinUrl(skin.link); - } - }} - > - - {!skin.own && ( -
-
-
- )} - -
- ))} -
+
+ {skins.map((skin) => ( + { + if (skin.own) { + setSelectedId(skin.id); + setSelectedSkin(skin); + } + }} + selected={skin.id === selectedId} + /> + ))}
); }; diff --git a/src/pages/collection/api.ts b/src/pages/collection/api.ts new file mode 100644 index 00000000..94acfb86 --- /dev/null +++ b/src/pages/collection/api.ts @@ -0,0 +1,31 @@ +import { client } from '@/features/api'; + +export const getCollections = async () => { + return client.get('/collection').then((response) => { + const { data } = response; + console.debug(data); + return data; + }); +}; + +export const updateSkin = async (skinId: number) => { + return client + .patch('/collection/skin', { skinId }) + .then((response) => { + const { data } = response; + console.debug(data); + return data; + }); +}; + +export const updateLabel = async (labelId: number) => { + return client + .patch('/collection/label', { + labelId, + }) + .then((response) => { + const { data } = response; + console.debug(data); + return data; + }); +}; diff --git a/src/pages/collection/hooks.ts b/src/pages/collection/hooks.ts new file mode 100644 index 00000000..2268a29f --- /dev/null +++ b/src/pages/collection/hooks.ts @@ -0,0 +1,24 @@ +import { useAtomValue, useSetAtom } from 'jotai'; + +import { label, labelName, skin, skinUrl } from './store'; + +export const useSelectedSkin = () => useAtomValue(skinUrl); + +export const useSelectedLabel = () => useAtomValue(labelName); + +export const useSelectedCollection = () => { + return { + skin: useAtomValue(skin), + label: useAtomValue(label), + }; +}; + +export const useCollectionActions = () => { + const setSelectedSkin = useSetAtom(skin); + const setSelectedLabel = useSetAtom(label); + + return { + setSelectedSkin, + setSelectedLabel, + }; +}; diff --git a/src/pages/collection/index.css.ts b/src/pages/collection/index.css.ts index 96461852..a5590124 100644 --- a/src/pages/collection/index.css.ts +++ b/src/pages/collection/index.css.ts @@ -1,4 +1,5 @@ import { style } from '@vanilla-extract/css'; +import { recipe } from '@vanilla-extract/recipes'; import * as constants from './constatns'; @@ -6,19 +7,13 @@ import { flexOptions } from '@/styles/common.css'; import { sprinkles } from '@/styles/sprinkles.css'; export const gridBox = style({ - display: 'grid', - gridTemplateColumns: '35% 65%', - gridTemplateRows: '15% 1fr', - gridTemplateAreas: ` - ". a" - "b c" - `, + display: 'flex', height: '100%', overflow: 'hidden', }); export const characterWrapper = style({ - gridArea: 'b', + width: '35%', display: 'flex', flexDirection: 'column', alignItems: 'center', @@ -48,6 +43,7 @@ export const categoryButton = style([ gridArea: 'a', gap: constants.CATEGORY_BUTTON_GAP, }, + sprinkles({ paddingBottom: '1x' }), ]); export const label = style([ @@ -57,24 +53,19 @@ export const label = style([ width: 'full', }), { - position: 'absolute', textAlign: 'center', }, ]); export const mySkin = style([ - flexOptions({ option: 'column' }), sprinkles({ width: 'full', + aspectRatio: 'square', }), { position: 'relative', - overflow: 'hidden', - aspectRatio: '1 / 1', - backgroundRepeat: 'no-repeat', - backgroundClip: 'border-box', - backgroundSize: 'cover', - backgroundPosition: 'center', + objectFit: 'cover', + objectPosition: 'center', }, ]); @@ -198,3 +189,25 @@ export const mosaic = style({ width: '65%', height: '20%', }); + +export const skinItem = recipe({ + base: { + backgroundColor: 'transparent', + }, + variants: { + owned: { + false: { + ':hover': { + cursor: 'not-allowed', + }, + }, + }, + }, +}); + +export const menu = style([ + flexOptions({ option: 'column' }), + { + width: '65%', + }, +]); diff --git a/src/pages/collection/index.tsx b/src/pages/collection/index.tsx index 00f4b164..0c0a8664 100644 --- a/src/pages/collection/index.tsx +++ b/src/pages/collection/index.tsx @@ -1,119 +1 @@ -import { useEffect, useState } from 'react'; -import { useLoaderData } from 'react-router-dom'; - -import * as styles from './index.css'; - -import { User } from '@/types/auth'; - -import SettingTextButton from '@/components/button/SettingTextButton'; -import BasicContentFrame from '@/components/frame/with-buttons'; - -import Achievement from '@/pages/collection/Achievement'; -import Label from '@/pages/collection/Label'; -import Skin from '@/pages/collection/Skin'; - -import { patchLabelChange, patchSkinChange } from '@/services/collections'; - -import { useCollectionStateStore } from '@/states/collection'; - -const Collection = () => { - const user = useLoaderData() as User; - const { skinId, skinUrl, labelId, labelName, setLabelName, setSkinUrl } = - useCollectionStateStore(); - const [saveSuccess, setSaveSuccess] = useState(false); - const [selectedCategory, setSelectedCategory] = useState('skin'); - - const renderComponent = () => { - switch (selectedCategory) { - case 'skin': - return ; - case 'label': - return