From 564b839e9ed9dfa72ad6b5859d5d11891f9fdb75 Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 22 Jun 2023 23:09:34 +0800 Subject: [PATCH 1/7] refactor: note layout and data Signed-off-by: Innei --- package.json | 8 +- pnpm-lock.yaml | 154 ++++++++---------- src/app/notes/[id]/layout.tsx | 13 +- src/app/notes/[id]/page.tsx | 90 ++++------ src/app/notes/layout.tsx | 25 ++- src/components/common/QueryHydration.tsx | 2 + .../layout/header/internal/HeaderContent.tsx | 9 +- .../layout/header/internal/UserAuth.tsx | 25 ++- .../widgets/note/NoteActionAside.tsx | 25 +++ .../widgets/note/NoteFooterNavigation.tsx | 9 +- src/components/widgets/note/NoteMetaBar.tsx | 128 ++++++++------- src/components/widgets/note/NoteTimeline.tsx | 7 + src/components/widgets/note/NoteTopicInfo.tsx | 6 + src/hooks/common/use-before-mounted.ts | 5 +- src/hooks/data/use-note.ts | 38 ++++- src/providers/note/CurrentNodeData.tsx | 29 ++++ src/providers/note/CurrentNoteIdProvider.tsx | 46 +++--- .../root/aggregation-data-provider.tsx | 11 +- 18 files changed, 346 insertions(+), 284 deletions(-) create mode 100644 src/providers/note/CurrentNodeData.tsx diff --git a/package.json b/package.json index 8b01c7570d..c83cbd4eef 100644 --- a/package.json +++ b/package.json @@ -65,16 +65,16 @@ "mdast-util-toc": "6.1.1", "medium-zoom": "1.0.8", "mermaid": "10.2.3", - "next": "13.4.6", + "next": "13.4.7", "next-themes": "0.2.1", "react": "18.2.0", "react-dom": "18.2.0", "react-intersection-observer": "9.5.1", "react-toastify": "9.1.3", - "react-tweet": "2.0.1", + "react-tweet": "2.0.2", "react-wrap-balancer": "1.0.0", "remove-markdown": "0.5.0", - "socket.io-client": "4.6.2", + "socket.io-client": "4.7.0", "tailwind-merge": "1.13.2", "validator": "13.9.0" }, @@ -84,7 +84,7 @@ "@iconify/tailwind": "0.1.3", "@innei/eslint-config-react-ts": "0.10.1", "@innei/prettier": "0.10.1", - "@next/bundle-analyzer": "13.4.6", + "@next/bundle-analyzer": "13.4.7", "@sentry/cli": "2.19.1", "@tailwindcss/typography": "0.5.9", "@types/color": "3.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af2df159ae..11ca9a3103 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,7 @@ settings: dependencies: '@clerk/nextjs': specifier: 4.21.8 - version: 4.21.8(next@13.4.6)(react-dom@18.2.0)(react@18.2.0) + version: 4.21.8(next@13.4.7)(react-dom@18.2.0)(react@18.2.0) '@floating-ui/react-dom': specifier: 2.0.1 version: 2.0.1(react-dom@18.2.0)(react@18.2.0) @@ -22,10 +22,10 @@ dependencies: version: 1.0.4(@types/react-dom@18.2.6)(@types/react@18.2.13)(react-dom@18.2.0)(react@18.2.0) '@sentry/nextjs': specifier: 7.56.0 - version: 7.56.0(next@13.4.6)(react@18.2.0)(webpack@5.87.0) + version: 7.56.0(next@13.4.7)(react@18.2.0)(webpack@5.88.0) '@sentry/webpack-plugin': specifier: 2.3.0 - version: 2.3.0(webpack@5.87.0) + version: 2.3.0(webpack@5.88.0) '@tanstack/react-query': specifier: 4.29.15 version: 4.29.15(react-dom@18.2.0)(react@18.2.0) @@ -90,11 +90,11 @@ dependencies: specifier: 10.2.3 version: 10.2.3 next: - specifier: 13.4.6 - version: 13.4.6(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) + specifier: 13.4.7 + version: 13.4.7(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) next-themes: specifier: 0.2.1 - version: 0.2.1(next@13.4.6)(react-dom@18.2.0)(react@18.2.0) + version: 0.2.1(next@13.4.7)(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -108,8 +108,8 @@ dependencies: specifier: 9.1.3 version: 9.1.3(react-dom@18.2.0)(react@18.2.0) react-tweet: - specifier: 2.0.1 - version: 2.0.1(react-dom@18.2.0)(react@18.2.0) + specifier: 2.0.2 + version: 2.0.2(react-dom@18.2.0)(react@18.2.0) react-wrap-balancer: specifier: 1.0.0 version: 1.0.0(react@18.2.0) @@ -117,8 +117,8 @@ dependencies: specifier: 0.5.0 version: 0.5.0 socket.io-client: - specifier: 4.6.2 - version: 4.6.2 + specifier: 4.7.0 + version: 4.7.0 tailwind-merge: specifier: 1.13.2 version: 1.13.2 @@ -143,8 +143,8 @@ devDependencies: specifier: 0.10.1 version: 0.10.1 '@next/bundle-analyzer': - specifier: 13.4.6 - version: 13.4.6 + specifier: 13.4.7 + version: 13.4.7 '@sentry/cli': specifier: 2.19.1 version: 2.19.1 @@ -814,7 +814,7 @@ packages: tslib: 2.4.1 dev: false - /@clerk/nextjs@4.21.8(next@13.4.6)(react-dom@18.2.0)(react@18.2.0): + /@clerk/nextjs@4.21.8(next@13.4.7)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Z32BsnoTg5C4ZstCHhKtoV90yr062mQRgkjhfZs5mAMtAMJfZ/UjNSfdBkBLVfxqUiBcLQXRxZbVayytkeTlEQ==} engines: {node: '>=14'} peerDependencies: @@ -826,7 +826,7 @@ packages: '@clerk/clerk-react': 4.20.5(react@18.2.0) '@clerk/clerk-sdk-node': 4.10.12 '@clerk/types': 3.46.0 - next: 13.4.6(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) + next: 13.4.7(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) path-to-regexp: 6.2.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1647,8 +1647,8 @@ packages: resolution: {integrity: sha512-GwG0SRlhglQjlZv35sf7MG6j2HHXBqX7hcBPKZu7/JurU3uT8gb49F6GYxFcoBL6BC+6VP62PoSy0AEQPvjkSQ==} dev: false - /@next/bundle-analyzer@13.4.6: - resolution: {integrity: sha512-HqqQxZ4y71az1TYCKILmJXjjY/645/21q/EUVFCGIzKkST+DrvgcJcBpWwlnI0wAuMRbdxvN1FMi7jSlSUIByw==} + /@next/bundle-analyzer@13.4.7: + resolution: {integrity: sha512-cIcU07u6WdDUF1nrFYOSPvx/pie8euxSnWB1Y/XMYj2g5ls+iyaDr/ChyZugD5hNW754x0NFy1CwzpObA0bffQ==} dependencies: webpack-bundle-analyzer: 4.7.0 transitivePeerDependencies: @@ -1656,12 +1656,12 @@ packages: - utf-8-validate dev: true - /@next/env@13.4.6: - resolution: {integrity: sha512-nqUxEtvDqFhmV1/awSg0K2XHNwkftNaiUqCYO9e6+MYmqNObpKVl7OgMkGaQ2SZnFx5YqF0t60ZJTlyJIDAijg==} + /@next/env@13.4.7: + resolution: {integrity: sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw==} dev: false - /@next/swc-darwin-arm64@13.4.6: - resolution: {integrity: sha512-ahi6VP98o4HV19rkOXPSUu+ovfHfUxbJQ7VVJ7gL2FnZRr7onEFC1oGQ6NQHpm8CxpIzSSBW79kumlFMOmZVjg==} + /@next/swc-darwin-arm64@13.4.7: + resolution: {integrity: sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -1669,8 +1669,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@13.4.6: - resolution: {integrity: sha512-13cXxKFsPJIJKzUqrU5XB1mc0xbUgYsRcdH6/rB8c4NMEbWGdtD4QoK9ShN31TZdePpD4k416Ur7p+deMIxnnA==} + /@next/swc-darwin-x64@13.4.7: + resolution: {integrity: sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -1678,8 +1678,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@13.4.6: - resolution: {integrity: sha512-Ti+NMHEjTNktCVxNjeWbYgmZvA2AqMMI2AMlzkXsU7W4pXCMhrryAmAIoo+7YdJbsx01JQWYVxGe62G6DoCLaA==} + /@next/swc-linux-arm64-gnu@13.4.7: + resolution: {integrity: sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1687,8 +1687,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@13.4.6: - resolution: {integrity: sha512-OHoC6gO7XfjstgwR+z6UHKlvhqJfyMtNaJidjx3sEcfaDwS7R2lqR5AABi8PuilGgi0BO0O0sCXqLlpp3a0emQ==} + /@next/swc-linux-arm64-musl@13.4.7: + resolution: {integrity: sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -1696,8 +1696,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@13.4.6: - resolution: {integrity: sha512-zHZxPGkUlpfNJCboUrFqwlwEX5vI9LSN70b8XEb0DYzzlrZyCyOi7hwDp/+3Urm9AB7YCAJkgR5Sp1XBVjHdfQ==} + /@next/swc-linux-x64-gnu@13.4.7: + resolution: {integrity: sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1705,8 +1705,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@13.4.6: - resolution: {integrity: sha512-K/Y8lYGTwTpv5ME8PSJxwxLolaDRdVy+lOd9yMRMiQE0BLUhtxtCWC9ypV42uh9WpLjoaD0joOsB9Q6mbrSGJg==} + /@next/swc-linux-x64-musl@13.4.7: + resolution: {integrity: sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -1714,8 +1714,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@13.4.6: - resolution: {integrity: sha512-U6LtxEUrjBL2tpW+Kr1nHCSJWNeIed7U7l5o7FiKGGwGgIlFi4UHDiLI6TQ2lxi20fAU33CsruV3U0GuzMlXIw==} + /@next/swc-win32-arm64-msvc@13.4.7: + resolution: {integrity: sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -1723,8 +1723,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@13.4.6: - resolution: {integrity: sha512-eEBeAqpCfhdPSlCZCayjCiyIllVqy4tcqvm1xmg3BgJG0G5ITiMM4Cw2WVeRSgWDJqQGRyyb+q8Y2ltzhXOWsQ==} + /@next/swc-win32-ia32-msvc@13.4.7: + resolution: {integrity: sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -1732,8 +1732,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@13.4.6: - resolution: {integrity: sha512-OrZs94AuO3ZS5tnqlyPRNgfWvboXaDQCi5aXGve3o3C+Sj0ctMUV9+Do+0zMvvLRumR8E0PTWKvtz9n5vzIsWw==} + /@next/swc-win32-x64-msvc@13.4.7: + resolution: {integrity: sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2219,7 +2219,7 @@ packages: tslib: 1.14.1 dev: false - /@sentry/nextjs@7.56.0(next@13.4.6)(react@18.2.0)(webpack@5.87.0): + /@sentry/nextjs@7.56.0(next@13.4.7)(react@18.2.0)(webpack@5.88.0): resolution: {integrity: sha512-s/HX6e3r8dS/683mbKfJymKg2DSqfQBeMRMxfp34Lg7Pzed/VLE9JLPkZq7BcS7leD0XQEflyxGYBKEq8iqeKA==} engines: {node: '>=8'} peerDependencies: @@ -2239,12 +2239,12 @@ packages: '@sentry/utils': 7.56.0 '@sentry/webpack-plugin': 1.20.0 chalk: 3.0.0 - next: 13.4.6(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) + next: 13.4.7(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 rollup: 2.78.0 stacktrace-parser: 0.1.10 tslib: 1.14.1 - webpack: 5.87.0(esbuild@0.17.11) + webpack: 5.88.0(esbuild@0.17.11) transitivePeerDependencies: - encoding - supports-color @@ -2342,7 +2342,7 @@ packages: - supports-color dev: false - /@sentry/webpack-plugin@2.3.0(webpack@5.87.0): + /@sentry/webpack-plugin@2.3.0(webpack@5.88.0): resolution: {integrity: sha512-rdrL9LISNIYMlJhRWn5NNXXVvovwNMWSC81qXgwu9PHbNp1RFKJiZ6oCGDvZRJEyvNTxpiyPMJGQzUPGSFSTRg==} engines: {node: '>= 10'} peerDependencies: @@ -2351,7 +2351,7 @@ packages: '@sentry/bundler-plugin-core': 2.3.0 unplugin: 1.0.1 uuid: 9.0.0 - webpack: 5.87.0(esbuild@0.17.11) + webpack: 5.88.0(esbuild@0.17.11) transitivePeerDependencies: - encoding - supports-color @@ -3002,12 +3002,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /acorn@8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - /acorn@8.9.0: resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==} engines: {node: '>=0.4.0'} @@ -3313,10 +3307,6 @@ packages: /caniuse-lite@1.0.30001497: resolution: {integrity: sha512-I4/duVK4wL6rAK/aKZl3HXB4g+lIZvaT4VLAn2rCgJ38jVLb0lv2Xug6QuqmxXFVRJMF74SPPWPJ/1Sdm3vCzw==} - /caniuse-lite@1.0.30001503: - resolution: {integrity: sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==} - dev: false - /caniuse-lite@1.0.30001506: resolution: {integrity: sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==} @@ -4051,12 +4041,12 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true - /engine.io-client@6.4.0: - resolution: {integrity: sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==} + /engine.io-client@6.5.0: + resolution: {integrity: sha512-C7eN3OKggSfd5g8IDgUA9guC8TNS6CEganKT7dL6Fp3q+FobcQ/WBn2Qq2XTL1vNTiFZfDzXohvqLuR9dWejdg==} dependencies: '@socket.io/component-emitter': 3.1.0 debug: 4.3.4 - engine.io-parser: 5.0.7 + engine.io-parser: 5.1.0 ws: 8.11.0 xmlhttprequest-ssl: 2.0.0 transitivePeerDependencies: @@ -4065,8 +4055,8 @@ packages: - utf-8-validate dev: false - /engine.io-parser@5.0.7: - resolution: {integrity: sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==} + /engine.io-parser@5.1.0: + resolution: {integrity: sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==} engines: {node: '>=10.0.0'} dev: false @@ -5934,20 +5924,20 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: false - /next-themes@0.2.1(next@13.4.6)(react-dom@18.2.0)(react@18.2.0): + /next-themes@0.2.1(next@13.4.7)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} peerDependencies: next: '*' react: '*' react-dom: '*' dependencies: - next: 13.4.6(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) + next: 13.4.7(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /next@13.4.6(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sjVqjxU+U2aXZnYt4Ud6CTLNNwWjdSfMgemGpIQJcN3Z7Jni9xRWbR0ie5fQzCg87aLqQVhKA2ud2gPoqJ9lGw==} + /next@13.4.7(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==} engines: {node: '>=16.8.0'} hasBin: true peerDependencies: @@ -5964,10 +5954,10 @@ packages: sass: optional: true dependencies: - '@next/env': 13.4.6 + '@next/env': 13.4.7 '@swc/helpers': 0.5.1 busboy: 1.6.0 - caniuse-lite: 1.0.30001503 + caniuse-lite: 1.0.30001506 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -5975,15 +5965,15 @@ packages: watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: - '@next/swc-darwin-arm64': 13.4.6 - '@next/swc-darwin-x64': 13.4.6 - '@next/swc-linux-arm64-gnu': 13.4.6 - '@next/swc-linux-arm64-musl': 13.4.6 - '@next/swc-linux-x64-gnu': 13.4.6 - '@next/swc-linux-x64-musl': 13.4.6 - '@next/swc-win32-arm64-msvc': 13.4.6 - '@next/swc-win32-ia32-msvc': 13.4.6 - '@next/swc-win32-x64-msvc': 13.4.6 + '@next/swc-darwin-arm64': 13.4.7 + '@next/swc-darwin-x64': 13.4.7 + '@next/swc-linux-arm64-gnu': 13.4.7 + '@next/swc-linux-arm64-musl': 13.4.7 + '@next/swc-linux-x64-gnu': 13.4.7 + '@next/swc-linux-x64-musl': 13.4.7 + '@next/swc-win32-arm64-msvc': 13.4.7 + '@next/swc-win32-ia32-msvc': 13.4.7 + '@next/swc-win32-x64-msvc': 13.4.7 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -6917,8 +6907,8 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /react-tweet@2.0.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-r8Yrv+cW8ajpCcT2AJCIji9crbbVxiaYSagqU4IW4Btsm3b3Ui65pFOV9gZmnt2xorn8L98kqj8REEuGxwnaeA==} + /react-tweet@2.0.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cTyExNUHyWHiUZ2Uuted2/b4ePClOe6q3cyQklP5qZrpiAldnQhXzA7dYBO5pPtg09uoriG+LZxqauZ2Yd/Rqw==} peerDependencies: react: '>= 18.0.0' react-dom: '>= 18.0.0' @@ -7260,13 +7250,13 @@ packages: type-fest: 2.19.0 dev: false - /socket.io-client@4.6.2: - resolution: {integrity: sha512-OwWrMbbA8wSqhBAR0yoPK6EdQLERQAYjXb3A0zLpgxfM1ZGLKoxHx8gVmCHA6pcclRX5oA/zvQf7bghAS11jRA==} + /socket.io-client@4.7.0: + resolution: {integrity: sha512-7Q8CeDrhuZzg4QLXl3tXlk5yb086oxYzehAVZRLiGCzCmtDneiHz1qHyyWcxhTgxXiokVpWQXoG/u60HoXSQew==} engines: {node: '>=10.0.0'} dependencies: '@socket.io/component-emitter': 3.1.0 debug: 4.3.4 - engine.io-client: 6.4.0 + engine.io-client: 6.5.0 socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -7582,7 +7572,7 @@ packages: engines: {node: '>=6'} dev: false - /terser-webpack-plugin@5.3.9(esbuild@0.17.11)(webpack@5.87.0): + /terser-webpack-plugin@5.3.9(esbuild@0.17.11)(webpack@5.88.0): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -7604,7 +7594,7 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.18.1 - webpack: 5.87.0(esbuild@0.17.11) + webpack: 5.88.0(esbuild@0.17.11) dev: false /terser@5.18.1: @@ -8001,7 +7991,7 @@ packages: engines: {node: '>= 10.13.0'} hasBin: true dependencies: - acorn: 8.8.2 + acorn: 8.9.0 acorn-walk: 8.2.0 chalk: 4.1.2 commander: 7.2.0 @@ -8024,8 +8014,8 @@ packages: resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} dev: false - /webpack@5.87.0(esbuild@0.17.11): - resolution: {integrity: sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==} + /webpack@5.88.0(esbuild@0.17.11): + resolution: {integrity: sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -8055,7 +8045,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(esbuild@0.17.11)(webpack@5.87.0) + terser-webpack-plugin: 5.3.9(esbuild@0.17.11)(webpack@5.88.0) watchpack: 2.4.0 webpack-sources: 3.2.3 transitivePeerDependencies: diff --git a/src/app/notes/[id]/layout.tsx b/src/app/notes/[id]/layout.tsx index d19d6f634c..7637456c34 100644 --- a/src/app/notes/[id]/layout.tsx +++ b/src/app/notes/[id]/layout.tsx @@ -6,6 +6,7 @@ import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpT import { REQUEST_QUERY } from '~/constants/system' import { attachUA } from '~/lib/attach-ua' import { getSummaryFromMd } from '~/lib/markdown' +import { CurrentNoteIdProvider } from '~/providers/note/CurrentNoteIdProvider' import { queries } from '~/queries/definition' import { getQueryClient } from '~/utils/query-client.server' @@ -60,16 +61,22 @@ export default async ( ) => { attachUA() const searchParams = new URLSearchParams(headers().get(REQUEST_QUERY) || '') + const id = props.params.id const query = queries.note.byNid( - props.params.id, + id, searchParams.get('password') || undefined, ) const data = await getQueryClient().fetchQuery(query) + return ( - + <> + + {/* */} + + {props.children} - + ) } diff --git a/src/app/notes/[id]/page.tsx b/src/app/notes/[id]/page.tsx index dc50027e63..9bb38ca098 100644 --- a/src/app/notes/[id]/page.tsx +++ b/src/app/notes/[id]/page.tsx @@ -7,88 +7,60 @@ import { Balancer } from 'react-wrap-balancer' import clsx from 'clsx' import dayjs from 'dayjs' import dynamic from 'next/dynamic' -import { useParams } from 'next/navigation' import type { Image, NoteModel } from '@mx-space/api-client' import type { MarkdownToJSX } from '~/components/ui/markdown' import { BanCopyWrapper } from '~/components/common/BanCopyWrapper' import { ClientOnly } from '~/components/common/ClientOnly' -import { PageDataHolder } from '~/components/common/PageHolder' import { MdiClockOutline } from '~/components/icons/clock' import { useSetHeaderMetaInfo } from '~/components/layout/header/hooks' import { FloatPopover } from '~/components/ui/float-popover' import { Loading } from '~/components/ui/loading' import { Markdown } from '~/components/ui/markdown' import { XLogInfoForNote, XLogSummaryForNote } from '~/components/widgets/xlog' -import { useCurrentNoteData, useNoteByNidQuery } from '~/hooks/data/use-note' +import { useCurrentNoteData } from '~/hooks/data/use-note' import { noopArr } from '~/lib/noop' import { MarkdownImageRecordProvider } from '~/providers/article/MarkdownImageRecordProvider' -import { - CurrentNoteIdProvider, - useSetCurrentNoteId, -} from '~/providers/note/CurrentNoteIdProvider' import { LayoutRightSidePortal } from '~/providers/shared/LayoutRightSideProvider' import { WrappedElementProvider } from '~/providers/shared/WrappedElementProvider' import { parseDate } from '~/utils/datetime' -import { springScrollToTop } from '~/utils/scroller' import { ReadIndicator } from '../../../components/common/ReadIndicator' import { NoteHideIfSecret } from '../../../components/widgets/note/NoteHideIfSecret' import { NoteMetaBar } from '../../../components/widgets/note/NoteMetaBar' import styles from './page.module.css' -const NoteActionAside = dynamic( - () => - import('~/components/widgets/note/NoteActionAside').then( - (mod) => mod.NoteActionAside, - ), - { ssr: false }, +const NoteActionAside = dynamic(() => + import('~/components/widgets/note/NoteActionAside').then( + (mod) => mod.NoteActionAside, + ), ) -const NoteFooterNavigationBarForMobile = dynamic( - () => - import('~/components/widgets/note/NoteFooterNavigation').then( - (mod) => mod.NoteFooterNavigationBarForMobile, - ), - { ssr: false }, +const NoteFooterNavigationBarForMobile = dynamic(() => + import('~/components/widgets/note/NoteFooterNavigation').then( + (mod) => mod.NoteFooterNavigationBarForMobile, + ), ) -const NoteTopic = dynamic( - () => - import('~/components/widgets/note/NoteTopic').then((mod) => mod.NoteTopic), - { ssr: false }, +const NoteTopic = dynamic(() => + import('~/components/widgets/note/NoteTopic').then((mod) => mod.NoteTopic), ) -const SubscribeBell = dynamic( - () => - import('~/components/widgets/subscribe/SubscribeBell').then( - (mod) => mod.SubscribeBell, - ), - { ssr: false }, +const SubscribeBell = dynamic(() => + import('~/components/widgets/subscribe/SubscribeBell').then( + (mod) => mod.SubscribeBell, + ), ) -const TocAside = dynamic( - () => import('~/components/widgets/toc').then((mod) => mod.TocAside), - { ssr: false }, +const TocAside = dynamic(() => + import('~/components/widgets/toc').then((mod) => mod.TocAside), ) -const TocAutoScroll = dynamic( - () => import('~/components/widgets/toc').then((mod) => mod.TocAutoScroll), - { ssr: false }, +const TocAutoScroll = dynamic(() => + import('~/components/widgets/toc').then((mod) => mod.TocAutoScroll), ) const PageImpl = () => { - const { id } = useParams() as { id: string } - const { data } = useNoteByNidQuery(id) - - // Why do this, I mean why do set NoteId to context, don't use `useParams().id` for children components. - // Because any router params or query changes, will cause components that use `useParams()` hook, this hook is a context hook, - // For example, `ComA` use `useParams()` just want to get value `id`, - // but if router params or query changes `page` params, will cause `CompA` re - render. - const setNoteId = useSetCurrentNoteId() - useEffect(() => { - setNoteId(id) - }, [id]) + const note = useCurrentNoteData() - const note = data?.data const setHeaderMetaInfo = useSetHeaderMetaInfo() useEffect(() => { if (!note?.title) return @@ -103,11 +75,8 @@ const PageImpl = () => { return } - return ( - - - - ) + // return null + return } const NotePage = memo(({ note }: { note: NoteModel }) => { @@ -217,11 +186,12 @@ const MarkdownRenderers: { [name: string]: Partial } = { }, } -export default PageDataHolder(PageImpl, () => { - const { id } = useParams() as { id: string } +export default PageImpl +// export default PageDataHolder(PageImpl, () => { +// const { id } = useParams() as { id: string } - useEffect(() => { - springScrollToTop() - }, [id]) - return useNoteByNidQuery(id) -}) +// useEffect(() => { +// springScrollToTop() +// }, [id]) +// return useNoteByNidQuery(id) +// }) diff --git a/src/app/notes/layout.tsx b/src/app/notes/layout.tsx index 27ac35953c..d949342c83 100644 --- a/src/app/notes/layout.tsx +++ b/src/app/notes/layout.tsx @@ -2,25 +2,22 @@ import clsx from 'clsx' import type { PropsWithChildren } from 'react' import { NoteLeftSidebar } from '~/components/widgets/note/NoteLeftSidebar' -import { CurrentNoteIdProvider } from '~/providers/note/CurrentNoteIdProvider' import { LayoutRightSideProvider } from '~/providers/shared/LayoutRightSideProvider' export default async (props: PropsWithChildren) => { return ( - -
- +
+ - {props.children} + {props.children} - -
- + +
) } diff --git a/src/components/common/QueryHydration.tsx b/src/components/common/QueryHydration.tsx index 6d47f111b3..2576783400 100644 --- a/src/components/common/QueryHydration.tsx +++ b/src/components/common/QueryHydration.tsx @@ -12,6 +12,8 @@ export const QueryHydration = memo( const client = useQueryClient() useBeforeMounted(() => { client.setQueriesData(props.queryKey, props.data) + + console.log('QueryHydration', props.queryKey, props.data) }) return props.children diff --git a/src/components/layout/header/internal/HeaderContent.tsx b/src/components/layout/header/internal/HeaderContent.tsx index c9ae0af90d..8d689dae21 100644 --- a/src/components/layout/header/internal/HeaderContent.tsx +++ b/src/components/layout/header/internal/HeaderContent.tsx @@ -67,7 +67,7 @@ const ForDesktop: Component = ({ className }) => { className, )} > -
    +
    {headerMenuConfig.map((section) => { return ( { /> ) })} -
+ ) } @@ -127,7 +127,6 @@ const MenuPopover: Component<{ if (!subMenu) return children return ( +
)} - +
) } diff --git a/src/components/layout/header/internal/UserAuth.tsx b/src/components/layout/header/internal/UserAuth.tsx index 895cfd59c4..33b301027a 100644 --- a/src/components/layout/header/internal/UserAuth.tsx +++ b/src/components/layout/header/internal/UserAuth.tsx @@ -11,26 +11,21 @@ import { urlBuilder } from '~/lib/url-builder' import { HeaderActionButton } from './HeaderActionButton' -const UserAuthFromIcon = dynamic( - () => import('./UserAuthFromIcon').then((mod) => mod.UserAuthFromIcon), - { ssr: false }, +const UserAuthFromIcon = dynamic(() => + import('./UserAuthFromIcon').then((mod) => mod.UserAuthFromIcon), ) -const SignedIn = dynamic( - () => import('@clerk/nextjs').then((mod) => mod.SignedIn), - { ssr: false }, +const SignedIn = dynamic(() => + import('@clerk/nextjs').then((mod) => mod.SignedIn), ) -const SignedOut = dynamic( - () => import('@clerk/nextjs').then((mod) => mod.SignedOut), - { ssr: false }, +const SignedOut = dynamic(() => + import('@clerk/nextjs').then((mod) => mod.SignedOut), ) -const UserButton = dynamic( - () => import('@clerk/nextjs').then((mod) => mod.UserButton), - { ssr: false }, +const UserButton = dynamic(() => + import('@clerk/nextjs').then((mod) => mod.UserButton), ) -const SignInButton = dynamic( - () => import('@clerk/nextjs').then((mod) => mod.SignInButton), - { ssr: false }, +const SignInButton = dynamic(() => + import('@clerk/nextjs').then((mod) => mod.SignInButton), ) export function UserAuth() { diff --git a/src/components/widgets/note/NoteActionAside.tsx b/src/components/widgets/note/NoteActionAside.tsx index b743e6760a..9ba6ccbc42 100644 --- a/src/components/widgets/note/NoteActionAside.tsx +++ b/src/components/widgets/note/NoteActionAside.tsx @@ -43,6 +43,31 @@ const LikeButton = () => { if (!note) return null const id = note.id const handleLike = () => { + // queryClient.setQueriesData( + // queries.note.byNid(note.nid.toString()), + // (old: any) => { + // // return produce(old as NoteWrappedPayload, (draft) => { + // // draft.data.count.like += 1 + // // draft + // // }) + // // old.data.count.like += 1 + // // old.data.count = { ...old.data.count } + // // old.data = { ...old.data } + // return { + // ...old, + // data: { + // ...old.data, + // text: `1${Math.random()}`, + // // count: { + // // ...old.data.count, + // // like: old.data.count.like + 1, + // // }, + // }, + // } + // }, + // ) + + // return if (isLikedBefore(id)) return apiClient.note.likeIt(id).then(() => { setLikeId(id) diff --git a/src/components/widgets/note/NoteFooterNavigation.tsx b/src/components/widgets/note/NoteFooterNavigation.tsx index fa4815d927..1860dd751b 100644 --- a/src/components/widgets/note/NoteFooterNavigation.tsx +++ b/src/components/widgets/note/NoteFooterNavigation.tsx @@ -14,15 +14,16 @@ import { springScrollToTop } from '~/utils/scroller' export const NoteFooterNavigation: FC<{ noteId: string }> = ({ noteId: id, }) => { - const { data } = useNoteByNidQuery(id) + const { data } = useNoteByNidQuery(id, (data) => ({ + nextNid: data.next?.nid, + prevNid: data.prev?.nid, + })) const router = useRouter() if (!data) return null - const { prev, next } = data - const prevNid = prev?.nid - const nextNid = next?.nid + const { nextNid, prevNid } = data return ( <> diff --git a/src/components/widgets/note/NoteMetaBar.tsx b/src/components/widgets/note/NoteMetaBar.tsx index c2a19c3512..dd047b20d8 100644 --- a/src/components/widgets/note/NoteMetaBar.tsx +++ b/src/components/widgets/note/NoteMetaBar.tsx @@ -1,82 +1,98 @@ -/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ 'use client' -import { cloneElement } from 'react' -import type { ReactNode } from 'react' - import { CreativeCommonsIcon } from '~/components/icons/cc' import { DividerVertical } from '~/components/ui/divider' import { useCurrentNoteData } from '~/hooks/data/use-note' import { mood2icon, weather2icon } from '~/lib/meta-icon' const dividerVertical = -const dividerVerticalWithKey = () => - cloneElement(dividerVertical, { key: `divider-${Math.random()}` }) + const sectionBlockClassName = 'flex items-center space-x-1 flex-shrink-0' export const NoteMetaBar = () => { const note = useCurrentNoteData() if (!note) return null + const { weather, mood, count } = note + return ( + <> + + + + + + + ) +} - const children = [] as ReactNode[] - - if (note.weather) { - children.push( - dividerVerticalWithKey(), +export const NoteMetaWeather = ({ weather }: { weather?: string }) => { + if (!weather) return null + return ( + <> + {dividerVertical} - {weather2icon(note.weather)} - {note.weather} - , - ) - } + {weather2icon(weather)} + {weather} + + + ) +} - if (note.mood) { - children.push( - dividerVerticalWithKey(), +export const NoteMetaMood = ({ mood }: { mood?: string }) => { + if (!mood) return null + return ( + <> + {dividerVertical} - {mood2icon(note.mood)} - {note.mood} - , - ) - } + {mood2icon(mood)} + {mood} + + + ) +} - if (note.count.read > 0) { - children.push( - dividerVerticalWithKey(), +export const NoteMetaReadCount = ({ read }: { read?: number }) => { + if (!read) return null + return ( + <> + {dividerVertical} - {note.count.read} - , - ) - } + {read} + + + ) +} - if (note.count.like > 0) { - children.push( - dividerVerticalWithKey(), +export const NoteMetaLikeCount = ({ like }: { like?: number }) => { + if (!like) return null + return ( + <> + {dividerVertical} - {note.count.like} - , - ) - } + {like} + + + ) +} - children.push( - dividerVerticalWithKey(), - - - { + return ( + <> + {dividerVertical} + + - - - - , + + + + + + ) - - return children } diff --git a/src/components/widgets/note/NoteTimeline.tsx b/src/components/widgets/note/NoteTimeline.tsx index 5b12b747e8..b2abf23955 100644 --- a/src/components/widgets/note/NoteTimeline.tsx +++ b/src/components/widgets/note/NoteTimeline.tsx @@ -9,10 +9,17 @@ import { tv } from 'tailwind-variants' import { LeftToRightTransitionView } from '~/components/ui/transition/LeftToRightTransitionView' import { useCurrentNoteData } from '~/hooks/data/use-note' import { routeBuilder, Routes } from '~/lib/route-builder' +import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { clsxm } from '~/utils/helper' import { apiClient } from '~/utils/request' export const NoteTimeline = () => { + const noteId = useCurrentNoteId() + if (!noteId) return null + return +} + +const NoteTimelineImpl = () => { const note = useCurrentNoteData() const noteId = note?.id diff --git a/src/components/widgets/note/NoteTopicInfo.tsx b/src/components/widgets/note/NoteTopicInfo.tsx index 50c9218068..8cd8c5a97e 100644 --- a/src/components/widgets/note/NoteTopicInfo.tsx +++ b/src/components/widgets/note/NoteTopicInfo.tsx @@ -3,10 +3,16 @@ import { Divider } from '~/components/ui/divider' import { FloatPopover } from '~/components/ui/float-popover' import { useCurrentNoteData } from '~/hooks/data/use-note' +import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { NoteTopicDetail, ToTopicLink } from './NoteTopicDetail' export const NoteTopicInfo = () => { + const noteId = useCurrentNoteId() + if (!noteId) return null + return +} +const NoteTopicInfoImpl = () => { const note = useCurrentNoteData() if (!note?.topic) return null diff --git a/src/hooks/common/use-before-mounted.ts b/src/hooks/common/use-before-mounted.ts index 8a26a70534..82a6f144b7 100644 --- a/src/hooks/common/use-before-mounted.ts +++ b/src/hooks/common/use-before-mounted.ts @@ -1,12 +1,9 @@ import { useRef } from 'react' -import { useIsClient } from './use-is-client' - export const useBeforeMounted = (fn: () => any) => { - const isClient = useIsClient() const effectOnce = useRef(false) - if (isClient && !effectOnce.current) { + if (!effectOnce.current) { effectOnce.current = true fn?.() } diff --git a/src/hooks/data/use-note.ts b/src/hooks/data/use-note.ts index 3e2f554f20..eed64280f8 100644 --- a/src/hooks/data/use-note.ts +++ b/src/hooks/data/use-note.ts @@ -1,35 +1,54 @@ import { useQuery, useQueryClient } from '@tanstack/react-query' import { useMemo } from 'react' +import type { NoteModel, NoteWrappedPayload } from '@mx-space/api-client' +import type { UseQueryResult } from '@tanstack/react-query' import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { queries } from '~/queries/definition' import { isClientSide } from '~/utils/env' -export const useCurrentNoteData = () => { - const nid = useCurrentNoteNId() +export function useCurrentNoteData(): NoteModel +export function useCurrentNoteData( + select?: (data: NoteWrappedPayload) => T, +): T +export function useCurrentNoteData(select?: any) { + const nid = useCurrentNoteId() + + if (!nid) { + throw 'nid not ready' + } const searchParams = useMemo( () => (isClientSide ? new URLSearchParams(location.search) : null), [nid], ) const password = searchParams?.get('password') + const queryClient = useQueryClient() + const query = queries.note.byNid(nid, password!) + const key = query.queryKey + const { data: noteAggregation } = useQuery({ - ...queries.note.byNid(nid || '', password!), - enabled: !!nid, + ...query, + // enabled: nid, select(data) { + if (typeof select === 'function') return select(data) return data.data }, + initialData: queryClient.getQueryData(key), keepPreviousData: true, }) return noteAggregation } -export const useCurrentNoteNId = () => { - return useCurrentNoteId() -} - -export const useNoteByNidQuery = (nid: string) => { +export function useNoteByNidQuery( + nid: string, +): UseQueryResult +export function useNoteByNidQuery( + nid: string, + select?: (data: NoteWrappedPayload) => T, +): UseQueryResult +export function useNoteByNidQuery(nid: string, select?: any) { const searchParams = useMemo( () => (isClientSide ? new URLSearchParams(location.search) : null), [nid], @@ -42,5 +61,6 @@ export const useNoteByNidQuery = (nid: string) => { ...queries.note.byNid(nid, password!), enabled: !!nid, initialData: queryClient.getQueryData(key), + select, }) } diff --git a/src/providers/note/CurrentNodeData.tsx b/src/providers/note/CurrentNodeData.tsx new file mode 100644 index 0000000000..d6176d450c --- /dev/null +++ b/src/providers/note/CurrentNodeData.tsx @@ -0,0 +1,29 @@ +'use client' + +import { memo, useEffect } from 'react' +import { atom, useAtomValue } from 'jotai' +import type { NoteWrappedPayload } from '@mx-space/api-client' +import type { FC, PropsWithChildren } from 'react' + +import { useBeforeMounted } from '~/hooks/common/use-before-mounted' +import { jotaiStore } from '~/lib/store' + +const currentNoteDataAtom = atom(null) +export const CurrentNoteDataProvider: FC< + { + data: NoteWrappedPayload + } & PropsWithChildren +> = memo(({ data, children }) => { + useBeforeMounted(() => { + jotaiStore.set(currentNoteDataAtom, data) + }) + + useEffect(() => { + jotaiStore.set(currentNoteDataAtom, data) + }, [data]) + + return children +}) +export const useCurrentNoteData = () => { + return useAtomValue(currentNoteDataAtom) +} diff --git a/src/providers/note/CurrentNoteIdProvider.tsx b/src/providers/note/CurrentNoteIdProvider.tsx index 4c09447e5b..fe131e6be4 100644 --- a/src/providers/note/CurrentNoteIdProvider.tsx +++ b/src/providers/note/CurrentNoteIdProvider.tsx @@ -1,36 +1,32 @@ 'use client' -import { createContext, memo, useContext, useState } from 'react' -import type { Dispatch, FC, PropsWithChildren, SetStateAction } from 'react' +import { memo, useEffect } from 'react' +import { atom, useAtomValue, useSetAtom } from 'jotai' +import type { FC, PropsWithChildren } from 'react' -// export const [CurrentNoteIdProvider, useCurrentNoteId, useSetCurrentNoteId] = -// createContextState(undefined) - -const CurrentNoteIdContext = createContext(undefined as undefined | string) - -const SetCurrentNoteIdContext = createContext< - Dispatch> ->(() => void 0) +import { useBeforeMounted } from '~/hooks/common/use-before-mounted' +import { jotaiStore } from '~/lib/store' +const currentNoteIdAtom = atom(null) const CurrentNoteIdProvider: FC< { - initialNoteId?: string + noteId: string } & PropsWithChildren -> = memo(({ initialNoteId, children }) => { - const [currentNoteId, setCurrentNoteId] = useState(initialNoteId) - return ( - - - {children} - - - ) +> = memo(({ noteId, children }) => { + const setNoteId = useSetAtom(currentNoteIdAtom) + useBeforeMounted(() => { + // setNoteId(noteId) + jotaiStore.set(currentNoteIdAtom, noteId) + }) + + useEffect(() => { + setNoteId(noteId) + }, [noteId]) + + return children }) const useCurrentNoteId = () => { - return useContext(CurrentNoteIdContext) -} -const useSetCurrentNoteId = () => { - return useContext(SetCurrentNoteIdContext) + return useAtomValue(currentNoteIdAtom) } -export { useCurrentNoteId, useSetCurrentNoteId, CurrentNoteIdProvider } +export { useCurrentNoteId, CurrentNoteIdProvider } diff --git a/src/providers/root/aggregation-data-provider.tsx b/src/providers/root/aggregation-data-provider.tsx index 2c936f3221..1710435771 100644 --- a/src/providers/root/aggregation-data-provider.tsx +++ b/src/providers/root/aggregation-data-provider.tsx @@ -23,9 +23,14 @@ export const AggregationProvider: FC = ({ children }) => { useEffect(() => { if (callOnceRef.current) return if (!data?.user) return - login().then(() => { - callOnceRef.current = true - return fetchAppUrl() + login().then((logged) => { + if (logged) { + callOnceRef.current = true + // FIXME + setTimeout(() => { + fetchAppUrl() + }, 1000) + } }) }, [data?.user]) From 52d470dc7b645da4a08600a17443b10e6ef06acb Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 23 Jun 2023 00:28:17 +0800 Subject: [PATCH 2/7] refactor: use jotai to holder data Signed-off-by: Innei --- src/app/notes/[id]/layout.tsx | 5 +- src/app/notes/[id]/page.tsx | 120 +++++++++++------- .../widgets/note/NoteActionAside.tsx | 64 +++------- .../widgets/note/NoteFooterNavigation.tsx | 14 +- .../widgets/note/NoteHideIfSecret.tsx | 22 ++-- src/components/widgets/note/NoteMetaBar.tsx | 26 ++-- src/components/widgets/note/NoteTimeline.tsx | 5 +- src/components/widgets/note/NoteTopic.tsx | 7 +- .../widgets/note/NoteTopicDetail.tsx | 10 +- src/components/widgets/note/NoteTopicInfo.tsx | 19 ++- .../widgets/post/PostActionAside.tsx | 6 +- src/components/widgets/subscribe/hooks.tsx | 2 + src/components/widgets/xlog/XLogInfo.tsx | 10 +- src/components/widgets/xlog/XLogSummary.tsx | 6 +- src/hooks/data/use-note.ts | 37 +----- src/providers/note/CurrentNodeData.tsx | 29 ----- .../note/CurrentNodeDataProvider.tsx | 52 ++++++++ 17 files changed, 214 insertions(+), 220 deletions(-) delete mode 100644 src/providers/note/CurrentNodeData.tsx create mode 100644 src/providers/note/CurrentNodeDataProvider.tsx diff --git a/src/app/notes/[id]/layout.tsx b/src/app/notes/[id]/layout.tsx index 7637456c34..b0558bf88a 100644 --- a/src/app/notes/[id]/layout.tsx +++ b/src/app/notes/[id]/layout.tsx @@ -1,11 +1,11 @@ import { headers } from 'next/dist/client/components/headers' import type { Metadata } from 'next' -import { QueryHydration } from '~/components/common/QueryHydration' import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView' import { REQUEST_QUERY } from '~/constants/system' import { attachUA } from '~/lib/attach-ua' import { getSummaryFromMd } from '~/lib/markdown' +import { CurrentNoteDataProvider } from '~/providers/note/CurrentNodeDataProvider' import { CurrentNoteIdProvider } from '~/providers/note/CurrentNoteIdProvider' import { queries } from '~/queries/definition' import { getQueryClient } from '~/utils/query-client.server' @@ -71,9 +71,8 @@ export default async ( return ( <> - {/* */} + - {props.children} diff --git a/src/app/notes/[id]/page.tsx b/src/app/notes/[id]/page.tsx index 9bb38ca098..e2c324c325 100644 --- a/src/app/notes/[id]/page.tsx +++ b/src/app/notes/[id]/page.tsx @@ -7,20 +7,21 @@ import { Balancer } from 'react-wrap-balancer' import clsx from 'clsx' import dayjs from 'dayjs' import dynamic from 'next/dynamic' -import type { Image, NoteModel } from '@mx-space/api-client' +import type { Image } from '@mx-space/api-client' import type { MarkdownToJSX } from '~/components/ui/markdown' +import type { PropsWithChildren } from 'react' import { BanCopyWrapper } from '~/components/common/BanCopyWrapper' import { ClientOnly } from '~/components/common/ClientOnly' import { MdiClockOutline } from '~/components/icons/clock' import { useSetHeaderMetaInfo } from '~/components/layout/header/hooks' import { FloatPopover } from '~/components/ui/float-popover' -import { Loading } from '~/components/ui/loading' import { Markdown } from '~/components/ui/markdown' import { XLogInfoForNote, XLogSummaryForNote } from '~/components/widgets/xlog' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { noopArr } from '~/lib/noop' import { MarkdownImageRecordProvider } from '~/providers/article/MarkdownImageRecordProvider' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { LayoutRightSidePortal } from '~/providers/shared/LayoutRightSideProvider' import { WrappedElementProvider } from '~/providers/shared/WrappedElementProvider' import { parseDate } from '~/utils/datetime' @@ -59,33 +60,76 @@ const TocAutoScroll = dynamic(() => ) const PageImpl = () => { - const note = useCurrentNoteData() + return ( + <> + + + + ) +} +const NoteHeaderMetaInfoSetting = () => { const setHeaderMetaInfo = useSetHeaderMetaInfo() - useEffect(() => { - if (!note?.title) return - setHeaderMetaInfo({ + const meta = useCurrentNoteDataSelector((data) => { + if (!data) return null + const note = data.data + + return { title: note?.title, description: `手记${note.topic?.name ? ` / ${note.topic?.name}` : ''}`, slug: note?.nid.toString(), - }) - }, [note?.nid, note?.title, note?.topic?.name]) + } + }) - if (!note) { - return - } + useEffect(() => { + if (meta) setHeaderMetaInfo(meta) + }, [meta]) - // return null - return + return null } -const NotePage = memo(({ note }: { note: NoteModel }) => { - const tips = `创建于 ${parseDate(note.created, 'YYYY 年 M 月 D 日 dddd')}${ - note.modified - ? `,修改于 ${parseDate(note.modified, 'YYYY 年 M 月 D 日 dddd')}` +const NoteHeaderDate = () => { + const date = useCurrentNoteDataSelector((data) => ({ + created: data?.data.created, + modified: data?.data.modified, + })) + if (!date?.created) return null + + const tips = `创建于 ${parseDate(date.created, 'YYYY 年 M 月 D 日 dddd')}${ + date.modified + ? `,修改于 ${parseDate(date.modified, 'YYYY 年 M 月 D 日 dddd')}` : '' }` + return ( + + {tips} + + ) +} + +const NoteMarkdown = () => { + const text = useCurrentNoteDataSelector((data) => data?.data.text) + if (!text) return null + + return +} +const NoteMarkdownImageRecordProvider = (props: PropsWithChildren) => { + const images = useCurrentNoteDataSelector( + (data) => data?.data.images || (noopArr as Image[]), + ) + if (!images) return null + + return ( + + {props.children} + + ) +} +const NotePage = memo(() => { + const noteId = useCurrentNoteId() + if (!noteId) return null + return (
{
- - {tips} - + @@ -111,17 +153,11 @@ const NotePage = memo(({ note }: { note: NoteModel }) => { - + - + - + {
- + - +
) }) const NoteTitle = () => { - const note = useCurrentNoteData() - if (!note) return null - const title = note.title + const title = useCurrentNoteDataSelector((data) => data?.data.title) + if (!title) return null return (

{title} @@ -157,10 +192,9 @@ const NoteTitle = () => { } const NoteDateMeta = () => { - const note = useCurrentNoteData() - if (!note) return null - - const dateFormat = dayjs(note.created) + const created = useCurrentNoteDataSelector((data) => data?.data.created) + if (!created) return null + const dateFormat = dayjs(created) .locale('zh-cn') .format('YYYY 年 M 月 D 日 dddd') @@ -187,11 +221,3 @@ const MarkdownRenderers: { [name: string]: Partial } = { } export default PageImpl -// export default PageDataHolder(PageImpl, () => { -// const { id } = useParams() as { id: string } - -// useEffect(() => { -// springScrollToTop() -// }, [id]) -// return useNoteByNidQuery(id) -// }) diff --git a/src/components/widgets/note/NoteActionAside.tsx b/src/components/widgets/note/NoteActionAside.tsx index 9ba6ccbc42..18edc5fea1 100644 --- a/src/components/widgets/note/NoteActionAside.tsx +++ b/src/components/widgets/note/NoteActionAside.tsx @@ -1,18 +1,19 @@ 'use client' -import { useQueryClient } from '@tanstack/react-query' import { motion, useAnimationControls, useForceUpdate } from 'framer-motion' -import { produce } from 'immer' -import type { NoteWrappedPayload } from '@mx-space/api-client' import { MotionButtonBase } from '~/components/ui/button' import { useIsClient } from '~/hooks/common/use-is-client' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { routeBuilder, Routes } from '~/lib/route-builder' import { toast } from '~/lib/toast' import { urlBuilder } from '~/lib/url-builder' +import { + getCurrentNoteData, + setCurrentNoteData, + useCurrentNoteDataSelector, +} from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { useAggregationData } from '~/providers/root/aggregation-data-provider' -import { queries } from '~/queries/definition' import { isLikedBefore, setLikeId } from '~/utils/cookie' import { clsxm } from '~/utils/helper' import { apiClient } from '~/utils/request' @@ -35,50 +36,20 @@ export const NoteActionAside: Component = ({ className }) => { } const LikeButton = () => { - const note = useCurrentNoteData() - - const queryClient = useQueryClient() const control = useAnimationControls() const [update] = useForceUpdate() - if (!note) return null - const id = note.id - const handleLike = () => { - // queryClient.setQueriesData( - // queries.note.byNid(note.nid.toString()), - // (old: any) => { - // // return produce(old as NoteWrappedPayload, (draft) => { - // // draft.data.count.like += 1 - // // draft - // // }) - // // old.data.count.like += 1 - // // old.data.count = { ...old.data.count } - // // old.data = { ...old.data } - // return { - // ...old, - // data: { - // ...old.data, - // text: `1${Math.random()}`, - // // count: { - // // ...old.data.count, - // // like: old.data.count.like + 1, - // // }, - // }, - // } - // }, - // ) - // return + const id = useCurrentNoteDataSelector((data) => data?.data.id) + const nid = useCurrentNoteId() + if (!id) return null + const handleLike = () => { if (isLikedBefore(id)) return + if (!nid) return apiClient.note.likeIt(id).then(() => { setLikeId(id) - queryClient.setQueriesData( - queries.note.byNid(note.nid.toString()), - (old: any) => { - return produce(old as NoteWrappedPayload, (draft) => { - draft.data.count.like += 1 - }) - }, - ) + setCurrentNoteData((draft) => { + draft.data.count.like += 1 + }) update() }) } @@ -138,10 +109,9 @@ const LikeButton = () => { const ShareButton = () => { const hasShare = 'share' in navigator const isClient = useIsClient() - const note = useCurrentNoteData() + const aggregation = useAggregationData() if (!isClient) return null - if (!note) return null if (!hasShare) { return null @@ -152,6 +122,10 @@ const ShareButton = () => { aria-label="Share This Note Button" className="flex flex-col space-y-2" onClick={() => { + const note = getCurrentNoteData()?.data + + if (!note) return + navigator.share({ title: note.title, text: note.text, diff --git a/src/components/widgets/note/NoteFooterNavigation.tsx b/src/components/widgets/note/NoteFooterNavigation.tsx index 1860dd751b..18171bfff9 100644 --- a/src/components/widgets/note/NoteFooterNavigation.tsx +++ b/src/components/widgets/note/NoteFooterNavigation.tsx @@ -7,17 +7,21 @@ import type { FC } from 'react' import { MdiClockTimeThreeOutline } from '~/components/icons/clock' import { Divider } from '~/components/ui/divider' import { OnlyMobile } from '~/components/ui/viewport/OnlyMobile' -import { useNoteByNidQuery } from '~/hooks/data/use-note' import { routeBuilder, Routes } from '~/lib/route-builder' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' import { springScrollToTop } from '~/utils/scroller' export const NoteFooterNavigation: FC<{ noteId: string }> = ({ noteId: id, }) => { - const { data } = useNoteByNidQuery(id, (data) => ({ - nextNid: data.next?.nid, - prevNid: data.prev?.nid, - })) + const data = useCurrentNoteDataSelector((data) => + !data + ? null + : { + nextNid: data?.next?.nid, + prevNid: data?.prev?.nid, + }, + ) const router = useRouter() diff --git a/src/components/widgets/note/NoteHideIfSecret.tsx b/src/components/widgets/note/NoteHideIfSecret.tsx index a26da7aeff..abbedc2711 100644 --- a/src/components/widgets/note/NoteHideIfSecret.tsx +++ b/src/components/widgets/note/NoteHideIfSecret.tsx @@ -6,20 +6,20 @@ import { useEffect, useMemo } from 'react' import dayjs from 'dayjs' import { useIsLogged } from '~/atoms/owner' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { toast } from '~/lib/toast' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' export const NoteHideIfSecret: Component = ({ children }) => { - const note = useCurrentNoteData() - const secretDate = useMemo(() => new Date(note?.secret!), [note?.secret]) - const isSecret = note?.secret - ? dayjs(note?.secret).isAfter(new Date()) - : false + const noteSecret = useCurrentNoteDataSelector((data) => data?.data.secret) + const nodeId = useCurrentNoteId() + const secretDate = useMemo(() => new Date(noteSecret!), [noteSecret]) + const isSecret = noteSecret ? dayjs(noteSecret).isAfter(new Date()) : false const isLogged = useIsLogged() useEffect(() => { - if (!note?.id) return + if (!nodeId) return let timer: any const timeout = +secretDate - +new Date() // https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values @@ -33,12 +33,12 @@ export const NoteHideIfSecret: Component = ({ children }) => { return () => { clearTimeout(timer) } - }, [isSecret, secretDate, note?.id]) + }, [isSecret, secretDate, nodeId]) - if (!note) return null + if (!nodeId) return null if (isSecret) { - const dateFormat = note.secret + const dateFormat = noteSecret ? Intl.DateTimeFormat('zh-cn', { hour12: false, hour: 'numeric', @@ -46,7 +46,7 @@ export const NoteHideIfSecret: Component = ({ children }) => { year: 'numeric', day: 'numeric', month: 'long', - }).format(new Date(note.secret)) + }).format(new Date(noteSecret)) : '' if (isLogged) { diff --git a/src/components/widgets/note/NoteMetaBar.tsx b/src/components/widgets/note/NoteMetaBar.tsx index dd047b20d8..4446b4b4e4 100644 --- a/src/components/widgets/note/NoteMetaBar.tsx +++ b/src/components/widgets/note/NoteMetaBar.tsx @@ -2,28 +2,26 @@ import { CreativeCommonsIcon } from '~/components/icons/cc' import { DividerVertical } from '~/components/ui/divider' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { mood2icon, weather2icon } from '~/lib/meta-icon' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' const dividerVertical = const sectionBlockClassName = 'flex items-center space-x-1 flex-shrink-0' export const NoteMetaBar = () => { - const note = useCurrentNoteData() - if (!note) return null - const { weather, mood, count } = note return ( <> - - - - + + + + ) } -export const NoteMetaWeather = ({ weather }: { weather?: string }) => { +export const NoteMetaWeather = () => { + const weather = useCurrentNoteDataSelector((data) => data?.data.weather) if (!weather) return null return ( <> @@ -36,7 +34,9 @@ export const NoteMetaWeather = ({ weather }: { weather?: string }) => { ) } -export const NoteMetaMood = ({ mood }: { mood?: string }) => { +export const NoteMetaMood = () => { + const mood = useCurrentNoteDataSelector((data) => data?.data.mood) + if (!mood) return null return ( <> @@ -49,7 +49,8 @@ export const NoteMetaMood = ({ mood }: { mood?: string }) => { ) } -export const NoteMetaReadCount = ({ read }: { read?: number }) => { +export const NoteMetaReadCount = () => { + const read = useCurrentNoteDataSelector((data) => data?.data.count.read) if (!read) return null return ( <> @@ -62,7 +63,8 @@ export const NoteMetaReadCount = ({ read }: { read?: number }) => { ) } -export const NoteMetaLikeCount = ({ like }: { like?: number }) => { +export const NoteMetaLikeCount = () => { + const like = useCurrentNoteDataSelector((data) => data?.data.count.like) if (!like) return null return ( <> diff --git a/src/components/widgets/note/NoteTimeline.tsx b/src/components/widgets/note/NoteTimeline.tsx index b2abf23955..f2c326269d 100644 --- a/src/components/widgets/note/NoteTimeline.tsx +++ b/src/components/widgets/note/NoteTimeline.tsx @@ -7,8 +7,8 @@ import Link from 'next/link' import { tv } from 'tailwind-variants' import { LeftToRightTransitionView } from '~/components/ui/transition/LeftToRightTransitionView' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { routeBuilder, Routes } from '~/lib/route-builder' +import { getCurrentNoteData } from '~/providers/note/CurrentNodeDataProvider' import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { clsxm } from '~/utils/helper' import { apiClient } from '~/utils/request' @@ -20,7 +20,8 @@ export const NoteTimeline = () => { } const NoteTimelineImpl = () => { - const note = useCurrentNoteData() + void useCurrentNoteId() + const note = getCurrentNoteData()?.data const noteId = note?.id const { data: timelineData } = useQuery( diff --git a/src/components/widgets/note/NoteTopic.tsx b/src/components/widgets/note/NoteTopic.tsx index 0c89f23925..66b54b6a94 100644 --- a/src/components/widgets/note/NoteTopic.tsx +++ b/src/components/widgets/note/NoteTopic.tsx @@ -1,11 +1,11 @@ import Link from 'next/link' -import type { TopicModel } from '@mx-space/api-client' import type { FC } from 'react' import { Avatar } from '~/components/ui/avatar' import { Divider } from '~/components/ui/divider' import { FloatPopover } from '~/components/ui/float-popover' import { routeBuilder, Routes } from '~/lib/route-builder' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' import { NoteTopicDetail } from './NoteTopicDetail' import { NoteTopicMarkdownRender } from './NoteTopicMarkdownRender' @@ -19,8 +19,9 @@ const textToBigCharOrWord = (name: string | undefined) => { return bigChar } -export const NoteTopic: FC<{ topic?: TopicModel }> = (props) => { - const { topic } = props +export const NoteTopic: FC = () => { + const topic = useCurrentNoteDataSelector((state) => state?.data.topic) + if (!topic) return null const { icon, name, introduce } = topic diff --git a/src/components/widgets/note/NoteTopicDetail.tsx b/src/components/widgets/note/NoteTopicDetail.tsx index 99cf4246db..e5816ef080 100644 --- a/src/components/widgets/note/NoteTopicDetail.tsx +++ b/src/components/widgets/note/NoteTopicDetail.tsx @@ -11,8 +11,8 @@ import { Divider, DividerVertical } from '~/components/ui/divider' import { Loading } from '~/components/ui/loading' import { RelativeTime } from '~/components/ui/relative-time' import { useIsClient } from '~/hooks/common/use-is-client' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { routeBuilder, Routes } from '~/lib/route-builder' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' import { apiClient } from '~/utils/request' import { NoteTopicMarkdownRender } from './NoteTopicMarkdownRender' @@ -105,16 +105,16 @@ export const NoteTopicDetail: FC<{ topic: TopicModel }> = (props) => { } export const ToTopicLink: FC = () => { - const note = useCurrentNoteData() - if (!note?.topic) return null + const topic = useCurrentNoteDataSelector((data) => data?.data.topic) + if (!topic) return null return ( - {note.topic.name} + {topic.name} ) diff --git a/src/components/widgets/note/NoteTopicInfo.tsx b/src/components/widgets/note/NoteTopicInfo.tsx index 8cd8c5a97e..91f8f782a0 100644 --- a/src/components/widgets/note/NoteTopicInfo.tsx +++ b/src/components/widgets/note/NoteTopicInfo.tsx @@ -2,20 +2,19 @@ import { Divider } from '~/components/ui/divider' import { FloatPopover } from '~/components/ui/float-popover' -import { useCurrentNoteData } from '~/hooks/data/use-note' -import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' import { NoteTopicDetail, ToTopicLink } from './NoteTopicDetail' +// export const NoteTopicInfo = () => { +// const noteId = useCurrentNoteId() +// if (!noteId) return null +// return +// } export const NoteTopicInfo = () => { - const noteId = useCurrentNoteId() - if (!noteId) return null - return -} -const NoteTopicInfoImpl = () => { - const note = useCurrentNoteData() + const topic = useCurrentNoteDataSelector((data) => data?.data.topic) - if (!note?.topic) return null + if (!topic) return null return ( <> @@ -30,7 +29,7 @@ const NoteTopicInfoImpl = () => { wrapperClassNames="flex flex-grow flex-shrink min-w-0" TriggerComponent={ToTopicLink} > - + ) diff --git a/src/components/widgets/post/PostActionAside.tsx b/src/components/widgets/post/PostActionAside.tsx index d58d3c09d2..3a96b37d1e 100644 --- a/src/components/widgets/post/PostActionAside.tsx +++ b/src/components/widgets/post/PostActionAside.tsx @@ -8,11 +8,12 @@ import type { NoteWrappedPayload } from '@mx-space/api-client' import { IonThumbsup } from '~/components/icons/thumbs-up' import { MotionButtonBase } from '~/components/ui/button' import { useIsClient } from '~/hooks/common/use-is-client' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { useCurrentPostData } from '~/hooks/data/use-post' import { routeBuilder, Routes } from '~/lib/route-builder' import { toast } from '~/lib/toast' import { urlBuilder } from '~/lib/url-builder' +import { getCurrentNoteData } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { useAggregationData } from '~/providers/root/aggregation-data-provider' import { queries } from '~/queries/definition' import { isLikedBefore, setLikeId } from '~/utils/cookie' @@ -113,7 +114,8 @@ const LikeButton = () => { const ShareButton = () => { const hasShare = 'share' in navigator const isClient = useIsClient() - const note = useCurrentNoteData() + void useCurrentNoteId() + const note = getCurrentNoteData()?.data const aggregation = useAggregationData() if (!isClient) return null if (!note) return null diff --git a/src/components/widgets/subscribe/hooks.tsx b/src/components/widgets/subscribe/hooks.tsx index cb6452546e..05c407a068 100644 --- a/src/components/widgets/subscribe/hooks.tsx +++ b/src/components/widgets/subscribe/hooks.tsx @@ -19,6 +19,8 @@ export const useIsEnableSubscribe = () => queryKey: SWR_CHECK_SUBSCRIBE_KEY, queryFn: apiClient.subscribe.check, select: (data: { enable: boolean }) => data?.enable, + cacheTime: 60_000 * 10, + staleTime: 60_000 * 10, }) export const usePresentSubscribeModal = ( diff --git a/src/components/widgets/xlog/XLogInfo.tsx b/src/components/widgets/xlog/XLogInfo.tsx index d784b03c8e..5c717e8894 100644 --- a/src/components/widgets/xlog/XLogInfo.tsx +++ b/src/components/widgets/xlog/XLogInfo.tsx @@ -5,8 +5,8 @@ import type { XLogMeta } from './types' import { Collapse } from '~/components/ui/collapse' import { useIsClient } from '~/hooks/common/use-is-client' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { useCurrentPostData } from '~/hooks/data/use-post' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' export const XLogInfoForPost: FC = () => { const data = useCurrentPostData() @@ -18,16 +18,12 @@ export const XLogInfoForPost: FC = () => { } export const XLogInfoForNote: FC = () => { - const data = useCurrentNoteData() - - if (!data) return null - - const meta = data.meta?.xLog + const meta = useCurrentNoteDataSelector((data) => data?.data.meta?.xLog) return } const XLogInfoBase: FC<{ - meta?: XLogMeta + meta?: XLogMeta | null }> = ({ meta }) => { const [collapse, setCollapse] = useState(false) diff --git a/src/components/widgets/xlog/XLogSummary.tsx b/src/components/widgets/xlog/XLogSummary.tsx index 59249e9b50..9defd05ba9 100644 --- a/src/components/widgets/xlog/XLogSummary.tsx +++ b/src/components/widgets/xlog/XLogSummary.tsx @@ -3,8 +3,8 @@ import type { FC, SVGProps } from 'react' import { AutoResizeHeight } from '~/components/common/AutoResizeHeight' import { useIsClient } from '~/hooks/common/use-is-client' -import { useCurrentNoteData } from '~/hooks/data/use-note' import { useCurrentPostData } from '~/hooks/data/use-post' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' import { clsxm } from '~/utils/helper' import { apiClient } from '~/utils/request' @@ -81,8 +81,8 @@ export const XLogSummaryForPost: FC = () => { } export const XLogSummaryForNote: FC = () => { - const data = useCurrentNoteData() - const cid = data?.meta?.xLog?.cid + const cid = useCurrentNoteDataSelector((data) => data?.data.meta?.xLog?.cid) + if (!cid) return null return diff --git a/src/hooks/data/use-note.ts b/src/hooks/data/use-note.ts index eed64280f8..eadf3faca3 100644 --- a/src/hooks/data/use-note.ts +++ b/src/hooks/data/use-note.ts @@ -1,46 +1,11 @@ import { useQuery, useQueryClient } from '@tanstack/react-query' import { useMemo } from 'react' -import type { NoteModel, NoteWrappedPayload } from '@mx-space/api-client' +import type { NoteWrappedPayload } from '@mx-space/api-client' import type { UseQueryResult } from '@tanstack/react-query' -import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { queries } from '~/queries/definition' import { isClientSide } from '~/utils/env' -export function useCurrentNoteData(): NoteModel -export function useCurrentNoteData( - select?: (data: NoteWrappedPayload) => T, -): T -export function useCurrentNoteData(select?: any) { - const nid = useCurrentNoteId() - - if (!nid) { - throw 'nid not ready' - } - - const searchParams = useMemo( - () => (isClientSide ? new URLSearchParams(location.search) : null), - [nid], - ) - const password = searchParams?.get('password') - const queryClient = useQueryClient() - const query = queries.note.byNid(nid, password!) - const key = query.queryKey - - const { data: noteAggregation } = useQuery({ - ...query, - // enabled: nid, - select(data) { - if (typeof select === 'function') return select(data) - return data.data - }, - initialData: queryClient.getQueryData(key), - keepPreviousData: true, - }) - - return noteAggregation -} - export function useNoteByNidQuery( nid: string, ): UseQueryResult diff --git a/src/providers/note/CurrentNodeData.tsx b/src/providers/note/CurrentNodeData.tsx deleted file mode 100644 index d6176d450c..0000000000 --- a/src/providers/note/CurrentNodeData.tsx +++ /dev/null @@ -1,29 +0,0 @@ -'use client' - -import { memo, useEffect } from 'react' -import { atom, useAtomValue } from 'jotai' -import type { NoteWrappedPayload } from '@mx-space/api-client' -import type { FC, PropsWithChildren } from 'react' - -import { useBeforeMounted } from '~/hooks/common/use-before-mounted' -import { jotaiStore } from '~/lib/store' - -const currentNoteDataAtom = atom(null) -export const CurrentNoteDataProvider: FC< - { - data: NoteWrappedPayload - } & PropsWithChildren -> = memo(({ data, children }) => { - useBeforeMounted(() => { - jotaiStore.set(currentNoteDataAtom, data) - }) - - useEffect(() => { - jotaiStore.set(currentNoteDataAtom, data) - }, [data]) - - return children -}) -export const useCurrentNoteData = () => { - return useAtomValue(currentNoteDataAtom) -} diff --git a/src/providers/note/CurrentNodeDataProvider.tsx b/src/providers/note/CurrentNodeDataProvider.tsx new file mode 100644 index 0000000000..58741b46cc --- /dev/null +++ b/src/providers/note/CurrentNodeDataProvider.tsx @@ -0,0 +1,52 @@ +'use client' + +import { memo, useCallback, useEffect } from 'react' +import { produce } from 'immer' +import { atom, useAtomValue } from 'jotai' +import { selectAtom } from 'jotai/utils' +import type { NoteWrappedPayload } from '@mx-space/api-client' +import type { FC, PropsWithChildren } from 'react' + +import { useBeforeMounted } from '~/hooks/common/use-before-mounted' +import { noopArr } from '~/lib/noop' +import { jotaiStore } from '~/lib/store' + +const currentNoteDataAtom = atom(null) +export const CurrentNoteDataProvider: FC< + { + data: NoteWrappedPayload + } & PropsWithChildren +> = memo(({ data, children }) => { + useBeforeMounted(() => { + jotaiStore.set(currentNoteDataAtom, data) + }) + + useEffect(() => { + jotaiStore.set(currentNoteDataAtom, data) + }, [data]) + + return children +}) +export const useCurrentNoteDataSelector = ( + selector: (data: NoteWrappedPayload | null) => T, + deps?: any[], +) => { + const nextSelector = useCallback((data: NoteWrappedPayload | null) => { + return data ? selector(data) : null + }, deps || noopArr) + + return useAtomValue(selectAtom(currentNoteDataAtom, nextSelector)) +} + +export const setCurrentNoteData = ( + recipe: (draft: NoteWrappedPayload) => void, +) => { + jotaiStore.set( + currentNoteDataAtom, + produce(jotaiStore.get(currentNoteDataAtom), recipe), + ) +} + +export const getCurrentNoteData = () => { + return jotaiStore.get(currentNoteDataAtom) +} From 8fb2da411aa64f8da8d81c9abc77822c6ea4a691 Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 23 Jun 2023 00:45:17 +0800 Subject: [PATCH 3/7] refactor: use jotai to hold post data Signed-off-by: Innei --- src/app/notes/[id]/layout.tsx | 2 +- src/app/notes/[id]/page.tsx | 2 +- .../[category]/[slug]/layout.tsx | 9 ++- .../(post-detail)/[category]/[slug]/page.tsx | 75 +++++++++++-------- .../widgets/note/NoteActionAside.tsx | 2 +- .../widgets/note/NoteFooterNavigation.tsx | 2 +- .../widgets/note/NoteHideIfSecret.tsx | 2 +- src/components/widgets/note/NoteMetaBar.tsx | 2 +- src/components/widgets/note/NoteTimeline.tsx | 4 +- src/components/widgets/note/NoteTopic.tsx | 2 +- .../widgets/note/NoteTopicDetail.tsx | 2 +- src/components/widgets/note/NoteTopicInfo.tsx | 2 +- .../widgets/post/PostActionAside.tsx | 32 ++++---- src/components/widgets/post/PostMetaBar.tsx | 32 ++++---- src/components/widgets/xlog/XLogInfo.tsx | 10 +-- src/components/widgets/xlog/XLogSummary.tsx | 7 +- src/hooks/data/use-note.ts | 31 -------- src/hooks/data/use-post.ts | 17 ----- src/providers/internal/createDataProvider.tsx | 58 ++++++++++++++ .../note/CurrentNodeDataProvider.tsx | 52 ------------- .../note/CurrentNoteDataProvider.tsx | 19 +++++ .../post/CurrentPostDataProvider.tsx | 19 +++++ 22 files changed, 196 insertions(+), 187 deletions(-) delete mode 100644 src/hooks/data/use-note.ts delete mode 100644 src/hooks/data/use-post.ts create mode 100644 src/providers/internal/createDataProvider.tsx delete mode 100644 src/providers/note/CurrentNodeDataProvider.tsx create mode 100644 src/providers/note/CurrentNoteDataProvider.tsx create mode 100644 src/providers/post/CurrentPostDataProvider.tsx diff --git a/src/app/notes/[id]/layout.tsx b/src/app/notes/[id]/layout.tsx index b0558bf88a..d0282f72be 100644 --- a/src/app/notes/[id]/layout.tsx +++ b/src/app/notes/[id]/layout.tsx @@ -5,7 +5,7 @@ import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpT import { REQUEST_QUERY } from '~/constants/system' import { attachUA } from '~/lib/attach-ua' import { getSummaryFromMd } from '~/lib/markdown' -import { CurrentNoteDataProvider } from '~/providers/note/CurrentNodeDataProvider' +import { CurrentNoteDataProvider } from '~/providers/note/CurrentNoteDataProvider' import { CurrentNoteIdProvider } from '~/providers/note/CurrentNoteIdProvider' import { queries } from '~/queries/definition' import { getQueryClient } from '~/utils/query-client.server' diff --git a/src/app/notes/[id]/page.tsx b/src/app/notes/[id]/page.tsx index e2c324c325..721aede74d 100644 --- a/src/app/notes/[id]/page.tsx +++ b/src/app/notes/[id]/page.tsx @@ -20,7 +20,7 @@ import { Markdown } from '~/components/ui/markdown' import { XLogInfoForNote, XLogSummaryForNote } from '~/components/widgets/xlog' import { noopArr } from '~/lib/noop' import { MarkdownImageRecordProvider } from '~/providers/article/MarkdownImageRecordProvider' -import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNoteDataProvider' import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { LayoutRightSidePortal } from '~/providers/shared/LayoutRightSideProvider' import { WrappedElementProvider } from '~/providers/shared/WrappedElementProvider' diff --git a/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx b/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx index 6c31cce7c1..b5c23fcb45 100644 --- a/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx +++ b/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx @@ -1,10 +1,10 @@ import React from 'react' import type { Metadata } from 'next' -import { QueryHydration } from '~/components/common/QueryHydration' import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView' import { attachUA } from '~/lib/attach-ua' import { getSummaryFromMd } from '~/lib/markdown' +import { CurrentPostDataProvider } from '~/providers/post/CurrentPostDataProvider' import { LayoutRightSideProvider } from '~/providers/shared/LayoutRightSideProvider' import { queries } from '~/queries/definition' import { getQueryClient } from '~/utils/query-client.server' @@ -61,10 +61,11 @@ export default async (props: NextPageParams) => { params: { category, slug }, } = props const query = queries.post.bySlug(category, slug) - const queryKey = query.queryKey + // const queryKey = query.queryKey const data = await getQueryClient().fetchQuery(query) return ( - + <> +
{props.children} @@ -72,6 +73,6 @@ export default async (props: NextPageParams) => {
-
+ ) } diff --git a/src/app/posts/(post-detail)/[category]/[slug]/page.tsx b/src/app/posts/(post-detail)/[category]/[slug]/page.tsx index 45ad0df748..ecc9377273 100644 --- a/src/app/posts/(post-detail)/[category]/[slug]/page.tsx +++ b/src/app/posts/(post-detail)/[category]/[slug]/page.tsx @@ -2,7 +2,8 @@ import { useEffect } from 'react' import { Balancer } from 'react-wrap-balancer' -import type { Image, PostModel } from '@mx-space/api-client' +import type { Image } from '@mx-space/api-client' +import type { PropsWithChildren } from 'react' import { ReadIndicator } from '~/components/common/ReadIndicator' import { useSetHeaderMetaInfo } from '~/components/layout/header/hooks' @@ -12,63 +13,77 @@ import { PostMetaBar } from '~/components/widgets/post/PostMetaBar' import { SubscribeBell } from '~/components/widgets/subscribe/SubscribeBell' import { TocAside, TocAutoScroll } from '~/components/widgets/toc' import { XLogInfoForPost, XLogSummaryForPost } from '~/components/widgets/xlog' -import { useCurrentPostData } from '~/hooks/data/use-post' import { noopArr } from '~/lib/noop' import { MarkdownImageRecordProvider } from '~/providers/article/MarkdownImageRecordProvider' +import { useCurrentPostDataSelector } from '~/providers/post/CurrentPostDataProvider' import { LayoutRightSidePortal } from '~/providers/shared/LayoutRightSideProvider' import { WrappedElementProvider } from '~/providers/shared/WrappedElementProvider' import Loading from './loading' -const useHeaderMeta = (data?: PostModel | null) => { - const setHeader = useSetHeaderMetaInfo() - useEffect(() => { - if (!data) return - setHeader({ +const PostMarkdown = () => { + const text = useCurrentPostDataSelector((data) => data?.text) + if (!text) return null + + return +} +const PostMarkdownImageRecordProvider = (props: PropsWithChildren) => { + const images = useCurrentPostDataSelector( + (data) => data?.images || (noopArr as Image[]), + ) + if (!images) return null + + return ( + + {props.children} + + ) +} + +const HeaderMetaInfoSetting = () => { + const setHeaderMetaInfo = useSetHeaderMetaInfo() + const meta = useCurrentPostDataSelector((data) => { + if (!data) return null + + return { title: data.title, description: data.category.name + (data.tags.length > 0 ? ` / ${data.tags.join(', ')}` : ''), slug: `${data.category.slug}/${data.slug}`, - }) - }, [ - data?.title, - data?.category.name, - data?.tags.length, - data?.category.slug, - data?.slug, - ]) + } + }) + + useEffect(() => { + if (meta) setHeaderMetaInfo(meta) + }, [meta]) + + return null } const PostPage = () => { - const data = useCurrentPostData() - - useHeaderMeta(data) - if (!data) { + const id = useCurrentPostDataSelector((p) => p?.id) + const title = useCurrentPostDataSelector((p) => p?.title) + if (!id) { return } return (
+

- {data.title} + {title}

- +
- - - + + + = ({ diff --git a/src/components/widgets/note/NoteHideIfSecret.tsx b/src/components/widgets/note/NoteHideIfSecret.tsx index abbedc2711..2b17d21368 100644 --- a/src/components/widgets/note/NoteHideIfSecret.tsx +++ b/src/components/widgets/note/NoteHideIfSecret.tsx @@ -7,7 +7,7 @@ import dayjs from 'dayjs' import { useIsLogged } from '~/atoms/owner' import { toast } from '~/lib/toast' -import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNoteDataProvider' import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' export const NoteHideIfSecret: Component = ({ children }) => { diff --git a/src/components/widgets/note/NoteMetaBar.tsx b/src/components/widgets/note/NoteMetaBar.tsx index 4446b4b4e4..cc718b256c 100644 --- a/src/components/widgets/note/NoteMetaBar.tsx +++ b/src/components/widgets/note/NoteMetaBar.tsx @@ -3,7 +3,7 @@ import { CreativeCommonsIcon } from '~/components/icons/cc' import { DividerVertical } from '~/components/ui/divider' import { mood2icon, weather2icon } from '~/lib/meta-icon' -import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNoteDataProvider' const dividerVertical = diff --git a/src/components/widgets/note/NoteTimeline.tsx b/src/components/widgets/note/NoteTimeline.tsx index f2c326269d..ff7cdb684f 100644 --- a/src/components/widgets/note/NoteTimeline.tsx +++ b/src/components/widgets/note/NoteTimeline.tsx @@ -8,10 +8,11 @@ import { tv } from 'tailwind-variants' import { LeftToRightTransitionView } from '~/components/ui/transition/LeftToRightTransitionView' import { routeBuilder, Routes } from '~/lib/route-builder' -import { getCurrentNoteData } from '~/providers/note/CurrentNodeDataProvider' +import { getCurrentNoteData } from '~/providers/note/CurrentNoteDataProvider' import { useCurrentNoteId } from '~/providers/note/CurrentNoteIdProvider' import { clsxm } from '~/utils/helper' import { apiClient } from '~/utils/request' +import { springScrollToTop } from '~/utils/scroller' export const NoteTimeline = () => { const noteId = useCurrentNoteId() @@ -99,6 +100,7 @@ const MemoedItem = memo<{ { } const LikeButton = () => { - const post = useCurrentPostData() - - const queryClient = useQueryClient() const control = useAnimationControls() const [update] = useForceUpdate() - if (!post) return null - const id = post.id + + const id = useCurrentPostDataSelector((data) => data?.id) + const nid = useCurrentNoteId() + if (!id) return null const handleLike = () => { if (isLikedBefore(id)) return + if (!nid) return apiClient.note.likeIt(id).then(() => { setLikeId(id) - queryClient.setQueriesData( - queries.post.bySlug(post.category.slug, post.slug), - (old: any) => { - return produce(old as NoteWrappedPayload, (draft) => { - draft.data.count.like += 1 - }) - }, - ) + setCurrentPostData((draft) => { + draft.count.like += 1 + }) update() }) } diff --git a/src/components/widgets/post/PostMetaBar.tsx b/src/components/widgets/post/PostMetaBar.tsx index 27f5987259..9d3f67bf8a 100644 --- a/src/components/widgets/post/PostMetaBar.tsx +++ b/src/components/widgets/post/PostMetaBar.tsx @@ -1,14 +1,20 @@ -import type { PostModel } from '@mx-space/api-client' - import { MdiClockOutline } from '~/components/icons/clock' import { FeHash } from '~/components/icons/fa-hash' import { RelativeTime } from '~/components/ui/relative-time' +import { useCurrentPostDataSelector } from '~/providers/post/CurrentPostDataProvider' import { clsxm } from '~/utils/helper' -export const PostMetaBar: Component<{ data: PostModel }> = ({ - data, - className, -}) => { +export const PostMetaBar: Component = ({ className }) => { + const meta = useCurrentPostDataSelector((data) => { + if (!data) return + return { + created: data.created, + category: data.category, + tags: data.tags, + count: data.count, + } + }) + if (!meta) return null return (
= ({
- +
- {data.category.name} - {data.tags.length ? ` / ${data.tags.join(', ')}` : ''} + {meta.category.name} + {meta.tags.length ? ` / ${meta.tags.join(', ')}` : ''}
- {!!data.count?.read && ( + {!!meta.count?.read && (
- {data.count.read} + {meta.count.read}
)} - {!!data.count?.like && ( + {!!meta.count?.like && (
- {data.count.like} + {meta.count.like}
)}
diff --git a/src/components/widgets/xlog/XLogInfo.tsx b/src/components/widgets/xlog/XLogInfo.tsx index 5c717e8894..f058886d5c 100644 --- a/src/components/widgets/xlog/XLogInfo.tsx +++ b/src/components/widgets/xlog/XLogInfo.tsx @@ -5,15 +5,11 @@ import type { XLogMeta } from './types' import { Collapse } from '~/components/ui/collapse' import { useIsClient } from '~/hooks/common/use-is-client' -import { useCurrentPostData } from '~/hooks/data/use-post' -import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNoteDataProvider' +import { useCurrentPostDataSelector } from '~/providers/post/CurrentPostDataProvider' export const XLogInfoForPost: FC = () => { - const data = useCurrentPostData() - - if (!data) return null - - const meta = data.meta?.xLog + const meta = useCurrentPostDataSelector((data) => data?.meta?.xLog) return } diff --git a/src/components/widgets/xlog/XLogSummary.tsx b/src/components/widgets/xlog/XLogSummary.tsx index 9defd05ba9..d1c84cb98e 100644 --- a/src/components/widgets/xlog/XLogSummary.tsx +++ b/src/components/widgets/xlog/XLogSummary.tsx @@ -3,8 +3,8 @@ import type { FC, SVGProps } from 'react' import { AutoResizeHeight } from '~/components/common/AutoResizeHeight' import { useIsClient } from '~/hooks/common/use-is-client' -import { useCurrentPostData } from '~/hooks/data/use-post' -import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNodeDataProvider' +import { useCurrentNoteDataSelector } from '~/providers/note/CurrentNoteDataProvider' +import { useCurrentPostDataSelector } from '~/providers/post/CurrentPostDataProvider' import { clsxm } from '~/utils/helper' import { apiClient } from '~/utils/request' @@ -72,8 +72,7 @@ const XLogSummary: FC<{ } export const XLogSummaryForPost: FC = () => { - const data = useCurrentPostData() - const cid = data?.meta?.xLog?.cid + const cid = useCurrentPostDataSelector((data) => data?.meta?.xLog?.cid) if (!cid) return null diff --git a/src/hooks/data/use-note.ts b/src/hooks/data/use-note.ts deleted file mode 100644 index eadf3faca3..0000000000 --- a/src/hooks/data/use-note.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useQuery, useQueryClient } from '@tanstack/react-query' -import { useMemo } from 'react' -import type { NoteWrappedPayload } from '@mx-space/api-client' -import type { UseQueryResult } from '@tanstack/react-query' - -import { queries } from '~/queries/definition' -import { isClientSide } from '~/utils/env' - -export function useNoteByNidQuery( - nid: string, -): UseQueryResult -export function useNoteByNidQuery( - nid: string, - select?: (data: NoteWrappedPayload) => T, -): UseQueryResult -export function useNoteByNidQuery(nid: string, select?: any) { - const searchParams = useMemo( - () => (isClientSide ? new URLSearchParams(location.search) : null), - [nid], - ) - const password = searchParams?.get('password') - const key = queries.note.byNid(nid, password!).queryKey - const queryClient = useQueryClient() - - return useQuery({ - ...queries.note.byNid(nid, password!), - enabled: !!nid, - initialData: queryClient.getQueryData(key), - select, - }) -} diff --git a/src/hooks/data/use-post.ts b/src/hooks/data/use-post.ts deleted file mode 100644 index 5a1415352c..0000000000 --- a/src/hooks/data/use-post.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { useParams } from 'next/navigation' - -import { queries } from '~/queries/definition' - -export const useCurrentPostData = () => { - const { category, slug } = useParams() - - const { data } = useQuery({ - ...queries.post.bySlug(category, slug), - enabled: !(category && slug), - - keepPreviousData: true, - }) - - return data -} diff --git a/src/providers/internal/createDataProvider.tsx b/src/providers/internal/createDataProvider.tsx new file mode 100644 index 0000000000..f045c853d9 --- /dev/null +++ b/src/providers/internal/createDataProvider.tsx @@ -0,0 +1,58 @@ +'use client' + +import { memo, useCallback, useEffect } from 'react' +import { produce } from 'immer' +import { atom, useAtomValue } from 'jotai' +import { selectAtom } from 'jotai/utils' +import type { FC, PropsWithChildren } from 'react' + +import { useBeforeMounted } from '~/hooks/common/use-before-mounted' +import { noopArr } from '~/lib/noop' +import { jotaiStore } from '~/lib/store' + +export const createDataProvider = () => { + const currentDataAtom = atom(null) + const CurrentDataProvider: FC< + { + data: Model + } & PropsWithChildren + > = memo(({ data, children }) => { + useBeforeMounted(() => { + jotaiStore.set(currentDataAtom, data) + }) + + useEffect(() => { + jotaiStore.set(currentDataAtom, data) + }, [data]) + + return children + }) + const useCurrentDataSelector = ( + selector: (data: Model | null) => T, + deps?: any[], + ) => { + const nextSelector = useCallback((data: Model | null) => { + return data ? selector(data) : null + }, deps || noopArr) + + return useAtomValue(selectAtom(currentDataAtom, nextSelector)) + } + + const setCurrentData = (recipe: (draft: Model) => void) => { + jotaiStore.set( + currentDataAtom, + produce(jotaiStore.get(currentDataAtom), recipe), + ) + } + + const getCurrentData = () => { + return jotaiStore.get(currentDataAtom) + } + + return { + CurrentDataProvider, + useCurrentDataSelector, + setCurrentData, + getCurrentData, + } +} diff --git a/src/providers/note/CurrentNodeDataProvider.tsx b/src/providers/note/CurrentNodeDataProvider.tsx deleted file mode 100644 index 58741b46cc..0000000000 --- a/src/providers/note/CurrentNodeDataProvider.tsx +++ /dev/null @@ -1,52 +0,0 @@ -'use client' - -import { memo, useCallback, useEffect } from 'react' -import { produce } from 'immer' -import { atom, useAtomValue } from 'jotai' -import { selectAtom } from 'jotai/utils' -import type { NoteWrappedPayload } from '@mx-space/api-client' -import type { FC, PropsWithChildren } from 'react' - -import { useBeforeMounted } from '~/hooks/common/use-before-mounted' -import { noopArr } from '~/lib/noop' -import { jotaiStore } from '~/lib/store' - -const currentNoteDataAtom = atom(null) -export const CurrentNoteDataProvider: FC< - { - data: NoteWrappedPayload - } & PropsWithChildren -> = memo(({ data, children }) => { - useBeforeMounted(() => { - jotaiStore.set(currentNoteDataAtom, data) - }) - - useEffect(() => { - jotaiStore.set(currentNoteDataAtom, data) - }, [data]) - - return children -}) -export const useCurrentNoteDataSelector = ( - selector: (data: NoteWrappedPayload | null) => T, - deps?: any[], -) => { - const nextSelector = useCallback((data: NoteWrappedPayload | null) => { - return data ? selector(data) : null - }, deps || noopArr) - - return useAtomValue(selectAtom(currentNoteDataAtom, nextSelector)) -} - -export const setCurrentNoteData = ( - recipe: (draft: NoteWrappedPayload) => void, -) => { - jotaiStore.set( - currentNoteDataAtom, - produce(jotaiStore.get(currentNoteDataAtom), recipe), - ) -} - -export const getCurrentNoteData = () => { - return jotaiStore.get(currentNoteDataAtom) -} diff --git a/src/providers/note/CurrentNoteDataProvider.tsx b/src/providers/note/CurrentNoteDataProvider.tsx new file mode 100644 index 0000000000..9ac254dd12 --- /dev/null +++ b/src/providers/note/CurrentNoteDataProvider.tsx @@ -0,0 +1,19 @@ +'use client' + +import { type NoteWrappedPayload } from '@mx-space/api-client' + +import { createDataProvider } from '../internal/createDataProvider' + +const { + CurrentDataProvider, + getCurrentData, + setCurrentData, + useCurrentDataSelector, +} = createDataProvider() + +export { + CurrentDataProvider as CurrentNoteDataProvider, + getCurrentData as getCurrentNoteData, + setCurrentData as setCurrentNoteData, + useCurrentDataSelector as useCurrentNoteDataSelector, +} diff --git a/src/providers/post/CurrentPostDataProvider.tsx b/src/providers/post/CurrentPostDataProvider.tsx new file mode 100644 index 0000000000..f6a6b58a5c --- /dev/null +++ b/src/providers/post/CurrentPostDataProvider.tsx @@ -0,0 +1,19 @@ +'use client' + +import type { PostModel } from '@mx-space/api-client' + +import { createDataProvider } from '../internal/createDataProvider' + +const { + CurrentDataProvider, + getCurrentData, + setCurrentData, + useCurrentDataSelector, +} = createDataProvider() + +export { + CurrentDataProvider as CurrentPostDataProvider, + getCurrentData as getCurrentPostData, + setCurrentData as setCurrentPostData, + useCurrentDataSelector as useCurrentPostDataSelector, +} From e20827fa08611d2eead179f77a9423f68914f86f Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 23 Jun 2023 01:03:09 +0800 Subject: [PATCH 4/7] fix: cleanup Signed-off-by: Innei --- src/components/widgets/post/PostItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/widgets/post/PostItem.tsx b/src/components/widgets/post/PostItem.tsx index be59555c7a..97c3981cae 100644 --- a/src/components/widgets/post/PostItem.tsx +++ b/src/components/widgets/post/PostItem.tsx @@ -53,7 +53,7 @@ export const PostItem = memo<{ data: PostModel }>(({ data }) => {
- + 阅读全文 From 702b262813e36607d1dc624d469390347236cc01 Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 23 Jun 2023 16:58:48 +0800 Subject: [PATCH 5/7] refactor: post layout Signed-off-by: Innei --- src/app/error.tsx | 18 ++- src/app/notes/Paper.tsx | 7 +- src/app/notes/[id]/layout.tsx | 6 +- src/app/notes/[id]/page.tsx | 117 +++++++++--------- src/app/notes/__route.ts | 13 -- src/app/notes/error.tsx | 25 ++-- .../[category]/[slug]/layout.tsx | 13 +- .../(post-detail)/[category]/[slug]/page.tsx | 16 ++- src/app/posts/(post-detail)/error.tsx | 8 -- src/atoms/owner.ts | 1 + src/components/common/404.tsx | 26 ++++ src/components/common/CodeBlock.tsx | 12 +- src/components/common/QueryHydration.tsx | 4 + src/components/common/ReadIndicator.tsx | 4 +- src/components/widgets/post/PostItem.tsx | 2 +- src/components/widgets/post/PostMetaBar.tsx | 17 +-- src/lib/is-error.ts | 9 ++ .../note/CurrentNoteDataProvider.tsx | 25 ++++ .../root/aggregation-data-provider.tsx | 2 +- src/queries/definition/note.ts | 2 +- 20 files changed, 210 insertions(+), 117 deletions(-) delete mode 100644 src/app/notes/__route.ts delete mode 100644 src/app/posts/(post-detail)/error.tsx create mode 100644 src/components/common/404.tsx create mode 100644 src/lib/is-error.ts diff --git a/src/app/error.tsx b/src/app/error.tsx index 52d72615b7..b892a511fb 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -4,9 +4,25 @@ import { useEffect } from 'react' import { captureException } from '@sentry/nextjs' +import { NotFound404 } from '~/components/common/404' +import { isRequestError, pickStatusCode } from '~/lib/is-error' + export default ({ error, reset }: any) => { useEffect(() => { captureException(error) }, [error]) - return
Something went wrong
+ + if (isRequestError(error) && pickStatusCode(error) === 404) { + return ( +
+ +
+ ) + } + + return ( +
+

Something went wrong!

+
+ ) } diff --git a/src/app/notes/Paper.tsx b/src/app/notes/Paper.tsx index 4d8d0b7991..f113ca8cf2 100644 --- a/src/app/notes/Paper.tsx +++ b/src/app/notes/Paper.tsx @@ -1,13 +1,14 @@ -import clsx from 'clsx' +import { clsxm } from '~/utils/helper' -export const Paper: Component = ({ children }) => { +export const Paper: Component = ({ children, className }) => { return (
{children} diff --git a/src/app/notes/[id]/layout.tsx b/src/app/notes/[id]/layout.tsx index d0282f72be..a5398719ee 100644 --- a/src/app/notes/[id]/layout.tsx +++ b/src/app/notes/[id]/layout.tsx @@ -5,7 +5,10 @@ import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpT import { REQUEST_QUERY } from '~/constants/system' import { attachUA } from '~/lib/attach-ua' import { getSummaryFromMd } from '~/lib/markdown' -import { CurrentNoteDataProvider } from '~/providers/note/CurrentNoteDataProvider' +import { + CurrentNoteDataProvider, + SyncNoteDataAfterLoggedIn, +} from '~/providers/note/CurrentNoteDataProvider' import { CurrentNoteIdProvider } from '~/providers/note/CurrentNoteIdProvider' import { queries } from '~/queries/definition' import { getQueryClient } from '~/utils/query-client.server' @@ -72,6 +75,7 @@ export default async ( <> + {props.children} diff --git a/src/app/notes/[id]/page.tsx b/src/app/notes/[id]/page.tsx index 721aede74d..31bee79698 100644 --- a/src/app/notes/[id]/page.tsx +++ b/src/app/notes/[id]/page.tsx @@ -68,64 +68,6 @@ const PageImpl = () => { ) } -const NoteHeaderMetaInfoSetting = () => { - const setHeaderMetaInfo = useSetHeaderMetaInfo() - const meta = useCurrentNoteDataSelector((data) => { - if (!data) return null - const note = data.data - - return { - title: note?.title, - description: `手记${note.topic?.name ? ` / ${note.topic?.name}` : ''}`, - slug: note?.nid.toString(), - } - }) - - useEffect(() => { - if (meta) setHeaderMetaInfo(meta) - }, [meta]) - - return null -} - -const NoteHeaderDate = () => { - const date = useCurrentNoteDataSelector((data) => ({ - created: data?.data.created, - modified: data?.data.modified, - })) - if (!date?.created) return null - - const tips = `创建于 ${parseDate(date.created, 'YYYY 年 M 月 D 日 dddd')}${ - date.modified - ? `,修改于 ${parseDate(date.modified, 'YYYY 年 M 月 D 日 dddd')}` - : '' - }` - - return ( - - {tips} - - ) -} - -const NoteMarkdown = () => { - const text = useCurrentNoteDataSelector((data) => data?.data.text) - if (!text) return null - - return -} -const NoteMarkdownImageRecordProvider = (props: PropsWithChildren) => { - const images = useCurrentNoteDataSelector( - (data) => data?.data.images || (noopArr as Image[]), - ) - if (!images) return null - - return ( - - {props.children} - - ) -} const NotePage = memo(() => { const noteId = useCurrentNoteId() if (!noteId) return null @@ -208,6 +150,65 @@ const NoteDateMeta = () => { ) } +const NoteHeaderDate = () => { + const date = useCurrentNoteDataSelector((data) => ({ + created: data?.data.created, + modified: data?.data.modified, + })) + if (!date?.created) return null + + const tips = `创建于 ${parseDate(date.created, 'YYYY 年 M 月 D 日 dddd')}${ + date.modified + ? `,修改于 ${parseDate(date.modified, 'YYYY 年 M 月 D 日 dddd')}` + : '' + }` + + return ( + + {tips} + + ) +} + +const NoteMarkdown = () => { + const text = useCurrentNoteDataSelector((data) => data?.data.text) + if (!text) return null + + return +} +const NoteMarkdownImageRecordProvider = (props: PropsWithChildren) => { + const images = useCurrentNoteDataSelector( + (data) => data?.data.images || (noopArr as Image[]), + ) + if (!images) return null + + return ( + + {props.children} + + ) +} + +const NoteHeaderMetaInfoSetting = () => { + const setHeaderMetaInfo = useSetHeaderMetaInfo() + const meta = useCurrentNoteDataSelector((data) => { + if (!data) return null + const note = data.data + + return { + title: note?.title, + description: `手记${note.topic?.name ? ` / ${note.topic?.name}` : ''}`, + slug: note?.nid.toString(), + } + }) + + useEffect(() => { + if (meta) setHeaderMetaInfo(meta) + }, [meta]) + + return null +} + const MarkdownRenderers: { [name: string]: Partial } = { text: { react(node, _, state) { diff --git a/src/app/notes/__route.ts b/src/app/notes/__route.ts deleted file mode 100644 index dafcd7c1a4..0000000000 --- a/src/app/notes/__route.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NextResponse } from 'next/server' -import type { NextRequest } from 'next/server' - -import { apiClient } from '~/utils/request' - -export const GET = async (request: NextRequest) => { - const url = request.nextUrl.clone() - const { - data: { nid }, - } = await apiClient.note.getLatest() - url.pathname = `/notes/${nid}` - return NextResponse.redirect(url) -} diff --git a/src/app/notes/error.tsx b/src/app/notes/error.tsx index fbcbfa5459..96f2da9abf 100644 --- a/src/app/notes/error.tsx +++ b/src/app/notes/error.tsx @@ -4,17 +4,12 @@ import { useEffect } from 'react' import { captureException } from '@sentry/nextjs' +import { NotFound404 } from '~/components/common/404' import { NotePasswordForm } from '~/components/widgets/note/NotePasswordForm' +import { isRequestError, pickStatusCode } from '~/lib/is-error' import { Paper } from './Paper' -const isRequestError = (error: Error) => { - return error.message.startsWith(`Request failed with status code`) -} - -const pickStatusCode = (error: Error) => { - return error.message.split(' ').pop() -} // TODO Catch if 404 or 403 export default ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -27,17 +22,25 @@ export default ({ error, reset }: { error: Error; reset: () => void }) => { if (!code) { return null } - if (parseInt(code) === 403) { + if (code === 403) { return ( ) } + + if (code === 404) { + return ( + + + + ) + } return ( -
-

{code}

-
+ +

{code}

+
) } diff --git a/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx b/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx index b5c23fcb45..e8231c9cc2 100644 --- a/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx +++ b/src/app/posts/(post-detail)/[category]/[slug]/layout.tsx @@ -1,6 +1,9 @@ import React from 'react' +import { notFound } from 'next/navigation' import type { Metadata } from 'next' +import { RequestError } from '@mx-space/api-client' + import { BottomToUpTransitionView } from '~/components/ui/transition/BottomToUpTransitionView' import { attachUA } from '~/lib/attach-ua' import { getSummaryFromMd } from '~/lib/markdown' @@ -62,7 +65,15 @@ export default async (props: NextPageParams) => { } = props const query = queries.post.bySlug(category, slug) // const queryKey = query.queryKey - const data = await getQueryClient().fetchQuery(query) + const data = await getQueryClient() + .fetchQuery(query) + .catch((error) => { + if (error instanceof RequestError && error.status === 404) { + return notFound() + } + throw error + }) + return ( <> diff --git a/src/app/posts/(post-detail)/[category]/[slug]/page.tsx b/src/app/posts/(post-detail)/[category]/[slug]/page.tsx index ecc9377273..0d7d1800ae 100644 --- a/src/app/posts/(post-detail)/[category]/[slug]/page.tsx +++ b/src/app/posts/(post-detail)/[category]/[slug]/page.tsx @@ -60,6 +60,20 @@ const HeaderMetaInfoSetting = () => { return null } + +const PostMetaBarInternal: Component = ({ className }) => { + const meta = useCurrentPostDataSelector((data) => { + if (!data) return + return { + created: data.created, + category: data.category, + tags: data.tags, + count: data.count, + } + }) + if (!meta) return null + return +} const PostPage = () => { const id = useCurrentPostDataSelector((p) => p?.id) const title = useCurrentPostDataSelector((p) => p?.title) @@ -76,7 +90,7 @@ const PostPage = () => { {title}

- + diff --git a/src/app/posts/(post-detail)/error.tsx b/src/app/posts/(post-detail)/error.tsx deleted file mode 100644 index a4f8c1b980..0000000000 --- a/src/app/posts/(post-detail)/error.tsx +++ /dev/null @@ -1,8 +0,0 @@ -'use client' - -import { Container } from './Container' - -export default ({ error, reset }: NextErrorProps) => { - // TODO - return Post Fetch error -} diff --git a/src/atoms/owner.ts b/src/atoms/owner.ts index f9aa12f934..b0afec70ac 100644 --- a/src/atoms/owner.ts +++ b/src/atoms/owner.ts @@ -46,6 +46,7 @@ export const login = async (username?: string, password?: string) => { if (!validated) { outdateToast() + removeToken() return } diff --git a/src/components/common/404.tsx b/src/components/common/404.tsx new file mode 100644 index 0000000000..17ec00e065 --- /dev/null +++ b/src/components/common/404.tsx @@ -0,0 +1,26 @@ +/* eslint-disable react/no-unknown-property */ +export const NotFound404 = () => { + return ( + <> +
+ 404 +
+ +

你来到了没有知识的荒原

+ + + ) +} diff --git a/src/components/common/CodeBlock.tsx b/src/components/common/CodeBlock.tsx index 566c3049b5..b39046cd8c 100644 --- a/src/components/common/CodeBlock.tsx +++ b/src/components/common/CodeBlock.tsx @@ -1,7 +1,11 @@ -import { Mermaid } from '~/components/common/Mermaid' - -import { HighLighter } from '../ui/code-highlighter' +import dynamic from 'next/dynamic' +const Mermaid = dynamic(() => import('./Mermaid').then((mod) => mod.Mermaid)) +const HighLighter = dynamic(() => + import('~/components/ui/code-highlighter/CodeHighlighter').then( + (mod) => mod.HighLighter, + ), +) export const CodeBlock = (props: { lang: string | undefined content: string @@ -13,4 +17,4 @@ export const CodeBlock = (props: { } } -export default CodeBlock \ No newline at end of file +export default CodeBlock diff --git a/src/components/common/QueryHydration.tsx b/src/components/common/QueryHydration.tsx index 2576783400..febafbfecc 100644 --- a/src/components/common/QueryHydration.tsx +++ b/src/components/common/QueryHydration.tsx @@ -1,3 +1,7 @@ +/** + * @deprecated + */ + 'use client' import { useQueryClient } from '@tanstack/react-query' diff --git a/src/components/common/ReadIndicator.tsx b/src/components/common/ReadIndicator.tsx index 08fb74af92..07db271343 100644 --- a/src/components/common/ReadIndicator.tsx +++ b/src/components/common/ReadIndicator.tsx @@ -15,7 +15,9 @@ export const ReadIndicator: Component<{ const { y } = useWrappedElementPositsion() const { h } = useWrappedElementSize() const readPercent = usePageScrollLocationSelector((scrollTop) => { - return Math.floor(Math.min(Math.max(0, ((scrollTop - y) / h) * 100), 100)) + return ( + Math.floor(Math.min(Math.max(0, ((scrollTop - y) / h) * 100), 100)) || 0 + ) }) const As = as || 'span' return ( diff --git a/src/components/widgets/post/PostItem.tsx b/src/components/widgets/post/PostItem.tsx index 97c3981cae..63e787721c 100644 --- a/src/components/widgets/post/PostItem.tsx +++ b/src/components/widgets/post/PostItem.tsx @@ -53,7 +53,7 @@ export const PostItem = memo<{ data: PostModel }>(({ data }) => {
- + 阅读全文 diff --git a/src/components/widgets/post/PostMetaBar.tsx b/src/components/widgets/post/PostMetaBar.tsx index 9d3f67bf8a..f1a8f7e24d 100644 --- a/src/components/widgets/post/PostMetaBar.tsx +++ b/src/components/widgets/post/PostMetaBar.tsx @@ -1,20 +1,13 @@ +import type { PostModel } from '@mx-space/api-client' + import { MdiClockOutline } from '~/components/icons/clock' import { FeHash } from '~/components/icons/fa-hash' import { RelativeTime } from '~/components/ui/relative-time' -import { useCurrentPostDataSelector } from '~/providers/post/CurrentPostDataProvider' import { clsxm } from '~/utils/helper' -export const PostMetaBar: Component = ({ className }) => { - const meta = useCurrentPostDataSelector((data) => { - if (!data) return - return { - created: data.created, - category: data.category, - tags: data.tags, - count: data.count, - } - }) - if (!meta) return null +export const PostMetaBar: Component<{ + meta: Pick +}> = ({ className, meta }) => { return (
{ + return error.message.startsWith(`Request failed with status code`) +} + +export const pickStatusCode = (error: Error) => { + return parseInt(error.message.split(' ').pop() || '') +} diff --git a/src/providers/note/CurrentNoteDataProvider.tsx b/src/providers/note/CurrentNoteDataProvider.tsx index 9ac254dd12..1bbb912eda 100644 --- a/src/providers/note/CurrentNoteDataProvider.tsx +++ b/src/providers/note/CurrentNoteDataProvider.tsx @@ -1,7 +1,13 @@ 'use client' +import { useQuery } from '@tanstack/react-query' +import { useEffect } from 'react' +import { useSearchParams } from 'next/navigation' + import { type NoteWrappedPayload } from '@mx-space/api-client' +import { queries } from '~/queries/definition' + import { createDataProvider } from '../internal/createDataProvider' const { @@ -17,3 +23,22 @@ export { setCurrentData as setCurrentNoteData, useCurrentDataSelector as useCurrentNoteDataSelector, } + +export const SyncNoteDataAfterLoggedIn = () => { + const nid = useCurrentDataSelector((data) => data?.data.nid) + const password = useSearchParams().get('password') + const { data } = useQuery({ + ...queries.note.byNid(nid?.toString() || '', password), + enabled: !!nid, + }) + + useEffect(() => { + if (data) { + setCurrentData((draft) => { + draft.data = data.data + }) + } + }, [data]) + + return null +} diff --git a/src/providers/root/aggregation-data-provider.tsx b/src/providers/root/aggregation-data-provider.tsx index 1710435771..665639e65a 100644 --- a/src/providers/root/aggregation-data-provider.tsx +++ b/src/providers/root/aggregation-data-provider.tsx @@ -23,9 +23,9 @@ export const AggregationProvider: FC = ({ children }) => { useEffect(() => { if (callOnceRef.current) return if (!data?.user) return + callOnceRef.current = true login().then((logged) => { if (logged) { - callOnceRef.current = true // FIXME setTimeout(() => { fetchAppUrl() diff --git a/src/queries/definition/note.ts b/src/queries/definition/note.ts index 30859c0c22..cee42f3a3b 100644 --- a/src/queries/definition/note.ts +++ b/src/queries/definition/note.ts @@ -8,7 +8,7 @@ import { defineQuery } from './helper' const LATEST_KEY = 'latest' export const note = { - byNid: (nid: string, password?: string) => + byNid: (nid: string, password?: string | null) => defineQuery({ queryKey: ['note', nid], meta: { From c4071840402f39ab41736e310c49784dbe9f672c Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 23 Jun 2023 16:59:26 +0800 Subject: [PATCH 6/7] fix: remove rq hydrate Signed-off-by: Innei --- src/app/layout.tsx | 69 ++++++++++--------- .../root/aggregation-data-provider.tsx | 21 +++--- src/providers/root/index.tsx | 3 +- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index fe995dfb54..74701e3e38 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,21 +1,19 @@ import '../styles/index.css' -import { dehydrate } from '@tanstack/react-query' import { Analytics } from '@vercel/analytics/react' import { ToastContainer } from 'react-toastify' -import { headers } from 'next/dist/client/components/headers' import { ClerkProvider } from '@clerk/nextjs' import { appConfig } from '~/app.config' import { Root } from '~/components/layout/root/Root' -import { REQUEST_GEO, REQUEST_PATHNAME } from '~/constants/system' import { defineMetadata } from '~/lib/define-metadata' import { sansFont, serifFont } from '~/lib/fonts' +import { AggregationProvider } from '~/providers/root/aggregation-data-provider' +import { queries } from '~/queries/definition' import { getQueryClient } from '~/utils/query-client.server' import { Providers } from '../providers/root' -import { Hydrate } from './hydrate' import { init } from './init' init() @@ -82,33 +80,36 @@ export default async function RootLayout(props: Props) { const queryClient = getQueryClient() - const dehydratedState = dehydrate(queryClient, { - shouldDehydrateQuery: (query) => { - if (query.state.error) return false - if (!query.meta) return true - const { - shouldHydration, - hydrationRoutePath, - skipHydration, - forceHydration, - } = query.meta - - if (forceHydration) return true - if (hydrationRoutePath) { - const pathname = headers().get(REQUEST_PATHNAME) - - if (pathname === query.meta?.hydrationRoutePath) { - if (!shouldHydration) return true - return (shouldHydration as Function)(query.state.data as any) - } - } - - if (skipHydration) return false - - return (shouldHydration as Function)?.(query.state.data as any) ?? false - }, + // const dehydratedState = dehydrate(queryClient, { + // shouldDehydrateQuery: (query) => { + // if (query.state.error) return false + // if (!query.meta) return true + // const { + // shouldHydration, + // hydrationRoutePath, + // skipHydration, + // forceHydration, + // } = query.meta + + // if (forceHydration) return true + // if (hydrationRoutePath) { + // const pathname = headers().get(REQUEST_PATHNAME) + + // if (pathname === query.meta?.hydrationRoutePath) { + // if (!shouldHydration) return true + // return (shouldHydration as Function)(query.state.data as any) + // } + // } + + // if (skipHydration) return false + + // return (shouldHydration as Function)?.(query.state.data as any) ?? false + // }, + // }) + + const data = await queryClient.fetchQuery({ + ...queries.aggregation.root(), }) - const geo = headers().get(REQUEST_GEO) return ( // @@ -118,12 +119,12 @@ export default async function RootLayout(props: Props) { className={`${sansFont.variable} ${serifFont.variable} m-0 h-full p-0 font-sans`} > - - {children} - + + {/* */} + {children} + {/* */} - {!!geo &&
{geo}
} diff --git a/src/providers/root/aggregation-data-provider.tsx b/src/providers/root/aggregation-data-provider.tsx index 665639e65a..57c11ba6e1 100644 --- a/src/providers/root/aggregation-data-provider.tsx +++ b/src/providers/root/aggregation-data-provider.tsx @@ -1,3 +1,5 @@ +'use client' + import { useCallback, useEffect, useRef } from 'react' import { atom, useAtomValue } from 'jotai' import { selectAtom } from 'jotai/utils' @@ -6,23 +8,24 @@ import type { FC, PropsWithChildren } from 'react' import { fetchAppUrl } from '~/atoms' import { login } from '~/atoms/owner' -import { useAggregationQuery } from '~/hooks/data/use-aggregation' import { jotaiStore } from '~/lib/store' export const aggregationDataAtom = atom(null) -export const AggregationProvider: FC = ({ children }) => { - const { data } = useAggregationQuery() - +export const AggregationProvider: FC< + PropsWithChildren<{ + aggregationData: AggregateRoot + }> +> = ({ children, aggregationData }) => { useEffect(() => { - if (!data) return - jotaiStore.set(aggregationDataAtom, data) - }, [data]) + if (!aggregationData) return + jotaiStore.set(aggregationDataAtom, aggregationData) + }, [aggregationData]) const callOnceRef = useRef(false) useEffect(() => { if (callOnceRef.current) return - if (!data?.user) return + if (!aggregationData?.user) return callOnceRef.current = true login().then((logged) => { if (logged) { @@ -32,7 +35,7 @@ export const AggregationProvider: FC = ({ children }) => { }, 1000) } }) - }, [data?.user]) + }, [aggregationData?.user]) return children } diff --git a/src/providers/root/index.tsx b/src/providers/root/index.tsx index 6e10f0d691..3051b273a0 100644 --- a/src/providers/root/index.tsx +++ b/src/providers/root/index.tsx @@ -6,7 +6,6 @@ import { ThemeProvider } from 'next-themes' import type { PropsWithChildren } from 'react' import { ProviderComposer } from '../../components/common/ProviderComposer' -import { AggregationProvider } from './aggregation-data-provider' import { DebugProvider } from './debug-provider' import { EventProvider } from './event-provider' import { JotaiStoreProvider } from './jotai-provider' @@ -20,7 +19,7 @@ const contexts: JSX.Element[] = [ , , , - , + , , From 45cc13208e1f8dc51c292f9e3b7b39c5f1db984d Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 23 Jun 2023 17:03:44 +0800 Subject: [PATCH 7/7] refactor: remove rq data store Signed-off-by: Innei --- .../internal/HeaderDataConfigureProvider.tsx | 21 ++++++++++++------- .../widgets/note/NoteActionAside.tsx | 4 +--- .../widgets/post/PostActionAside.tsx | 5 ++--- .../widgets/subscribe/SubscribeModal.tsx | 9 +++----- src/hooks/data/use-aggregation.ts | 14 ------------- .../root/aggregation-data-provider.tsx | 11 +++++----- 6 files changed, 25 insertions(+), 39 deletions(-) delete mode 100644 src/hooks/data/use-aggregation.ts diff --git a/src/components/layout/header/internal/HeaderDataConfigureProvider.tsx b/src/components/layout/header/internal/HeaderDataConfigureProvider.tsx index 073ae3fd8b..ee5d3e3d18 100644 --- a/src/components/layout/header/internal/HeaderDataConfigureProvider.tsx +++ b/src/components/layout/header/internal/HeaderDataConfigureProvider.tsx @@ -3,8 +3,8 @@ import { createContext, useContext, useEffect, useMemo, useState } from 'react' -import { useAggregationQuery } from '~/hooks/data/use-aggregation' import { cloneDeep } from '~/lib/_' +import { useAggregationSelector } from '~/providers/root/aggregation-data-provider' import { headerMenuConfig as baseHeaderMenuConfig } from '../config' @@ -14,17 +14,22 @@ const HeaderMenuConfigContext = createContext({ export const useHeaderConfig = () => useContext(HeaderMenuConfigContext) export const HeaderDataConfigureProvider: Component = ({ children }) => { - const { data } = useAggregationQuery() + const pageMeta = useAggregationSelector( + (aggregationData) => aggregationData.pageMeta, + ) + const categories = useAggregationSelector( + (aggregationData) => aggregationData.categories, + ) const [headerMenuConfig, setHeaderMenuConfig] = useState(baseHeaderMenuConfig) useEffect(() => { - if (!data) return + if (!pageMeta) return const nextMenuConfig = cloneDeep(baseHeaderMenuConfig) - if (data.pageMeta) { + if (pageMeta) { const homeIndex = nextMenuConfig.findIndex((item) => item.type === 'Home') if (homeIndex !== -1) { nextMenuConfig[homeIndex].subMenu = [] - for (const page of data.pageMeta) { + for (const page of pageMeta) { nextMenuConfig[homeIndex].subMenu!.push({ path: page.slug, title: page.title, @@ -33,11 +38,11 @@ export const HeaderDataConfigureProvider: Component = ({ children }) => { } } - if (data.categories?.length) { + if (categories?.length) { const postIndex = nextMenuConfig.findIndex((item) => item.type === 'Post') if (postIndex !== -1) { nextMenuConfig[postIndex].subMenu = [] - for (const category of data.categories) { + for (const category of categories) { nextMenuConfig[postIndex].subMenu!.push({ path: `/categories/${category.slug}`, title: category.name, @@ -47,7 +52,7 @@ export const HeaderDataConfigureProvider: Component = ({ children }) => { } setHeaderMenuConfig(nextMenuConfig) - }, [data]) + }, [categories, pageMeta]) return ( { const hasShare = 'share' in navigator const isClient = useIsClient() - const aggregation = useAggregationData() if (!isClient) return null if (!hasShare) { return null } - if (!aggregation) return null + return ( { const isClient = useIsClient() void useCurrentNoteId() const note = getCurrentNoteData()?.data - const aggregation = useAggregationData() + if (!isClient) return null if (!note) return null if (!hasShare) { return null } - if (!aggregation) return null + return ( = ({ dispatch({ type: 'reset' }) onConfirm() } - const aggregation = useAggregationData() - if (!aggregation) return null - const { - seo: { title }, - } = aggregation + const title = useAggregationSelector((data) => data.seo.title) + return (

diff --git a/src/hooks/data/use-aggregation.ts b/src/hooks/data/use-aggregation.ts deleted file mode 100644 index 47304dec96..0000000000 --- a/src/hooks/data/use-aggregation.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import type { AggregateRoot } from '@mx-space/api-client' -import type { UseQueryOptions } from '@tanstack/react-query' - -import { aggregation } from '../../queries/definition/aggregation' - -export const useAggregationQuery = ( - options?: UseQueryOptions, -) => { - return useQuery({ - ...aggregation.root(), - ...options, - }) -} diff --git a/src/providers/root/aggregation-data-provider.tsx b/src/providers/root/aggregation-data-provider.tsx index 57c11ba6e1..f3e3c22641 100644 --- a/src/providers/root/aggregation-data-provider.tsx +++ b/src/providers/root/aggregation-data-provider.tsx @@ -8,6 +8,7 @@ import type { FC, PropsWithChildren } from 'react' import { fetchAppUrl } from '~/atoms' import { login } from '~/atoms/owner' +import { useBeforeMounted } from '~/hooks/common/use-before-mounted' import { jotaiStore } from '~/lib/store' export const aggregationDataAtom = atom(null) @@ -17,6 +18,11 @@ export const AggregationProvider: FC< aggregationData: AggregateRoot }> > = ({ children, aggregationData }) => { + useBeforeMounted(() => { + if (!aggregationData) return + jotaiStore.set(aggregationDataAtom, aggregationData) + }) + useEffect(() => { if (!aggregationData) return jotaiStore.set(aggregationDataAtom, aggregationData) @@ -40,11 +46,6 @@ export const AggregationProvider: FC< return children } -/** - * Not recommended to use - */ -export const useAggregationData = () => useAtomValue(aggregationDataAtom) - export const useAggregationSelector = ( selector: (atomValue: AggregateRoot) => T, deps: any[] = [],