From 2457f6a3146c8f069611d50f4acd6f3d12883ce2 Mon Sep 17 00:00:00 2001 From: plebeius Date: Tue, 27 Jan 2026 14:33:28 +0800 Subject: [PATCH 01/20] Update AGENTS.md --- AGENTS.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8edd2ed4..660a1d05 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,7 +32,7 @@ yarn electron # Run Electron app - TypeScript strict mode - Prettier for formatting (runs on pre-commit) -- **DRY principle**: Never repeat UI elements across views—extract them into reusable components in `src/components/`. Same applies to logic—extract into custom hooks in `src/hooks/` +- **DRY principle**: Always follow the DRY principle when possible. Never repeat UI elements across views—extract them into reusable components in `src/components/`. Same applies to logic—extract into custom hooks in `src/hooks/`. ## React Patterns (Critical) @@ -169,14 +169,19 @@ node scripts/update-translations.js --audit --write ## Workflow -### GitHub Issues +### GitHub Commits -When proposing or implementing code changes, always suggest a GitHub issue title and description. Format: +When proposing or implementing code changes, always suggest a short GitHub commit title, and if the commit title isn't exhaustive enough, then provide also a commit description. Format: -- **Title**: Use [Conventional Commits](https://www.conventionalcommits.org/) style (e.g., `fix: ...`, `feat: ...`, `perf: ...`, `refactor: ...`, `docs: ...`, `chore: ...`) -- **Description**: 2-3 informal sentences describing the problem (not the solution). Write as if the issue hasn't been fixed yet. Use markdown. +- **Title**: Use [Conventional Commits](https://www.conventionalcommits.org/) style (e.g., `fix: ...`, `feat: ...`, `perf: ...`, `refactor: ...`, `docs: ...`, `chore: ...`). Use the `perf` type for performance optimizations (not `fix`). Keep it short. Use markdown. +- **Description**: If the title is missing important information, also provide a description, consisting of 2-3 informal sentences describing the solution (not the problem) that is being committed. Concise, technical, no bullet points. Use markdown. -Use the `perf` type for performance optimizations (not `fix`). +### Github Issues + +When proposing or implementing code changes, always suggest a GitHub issue title and description to keep track of the problem that was fixed. Format: + +- **Title**: As short as possible, may use commas to list related commits that resolve the same Github issue. Use markdown. +- **Description**: 2-3 informal sentences describing the problem (not the solution). Write as if the issue hasn't been fixed yet. Bullet points are encouranged but may not always be necessary. Use markdown. ### Troubleshooting From 6163ee930bd802e294b48462e406beea1e917ba3 Mon Sep 17 00:00:00 2001 From: plebeius Date: Tue, 27 Jan 2026 15:08:56 +0800 Subject: [PATCH 02/20] typo --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 660a1d05..e9df7c93 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Project Overview -5chan is a serverless, adminless, decentralized 4chan alternative built on the Plebbit protocol. +5chan is a serverless, adminless, decentralized 4chan alternative built on the Bitsocial protocol. ## Stack @@ -181,7 +181,7 @@ When proposing or implementing code changes, always suggest a short GitHub commi When proposing or implementing code changes, always suggest a GitHub issue title and description to keep track of the problem that was fixed. Format: - **Title**: As short as possible, may use commas to list related commits that resolve the same Github issue. Use markdown. -- **Description**: 2-3 informal sentences describing the problem (not the solution). Write as if the issue hasn't been fixed yet. Bullet points are encouranged but may not always be necessary. Use markdown. +- **Description**: 2-3 informal sentences describing the problem (not the solution). Write as if the issue hasn't been fixed yet. Bullet points are encouraged but may not always be necessary. Use markdown. ### Troubleshooting From 629b12a4a4002b28e2969157a6bea081f5222d9c Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 28 Jan 2026 12:41:08 +0800 Subject: [PATCH 03/20] chore(deps): upgrade plebbit-react-hooks --- package.json | 2 +- yarn.lock | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e2a82963..62838331 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@capacitor/status-bar": "7.0.1", "@capawesome/capacitor-android-edge-to-edge-support": "7.2.2", "@floating-ui/react": "0.26.1", - "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#60b9d18ebc647f76cdda8f36e2143192d7378658", + "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#df3cb6901f63054870dc127cd7565c98617c2a4f", "@react-spring/web": "9.7.5", "@types/node": "20.8.2", "@types/react": "18.2.25", diff --git a/yarn.lock b/yarn.lock index dd342a58..e3359f50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3622,6 +3622,18 @@ semver "^7.3.5" tar "^6.1.11" +"@mintpass/challenge@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@mintpass/challenge/-/challenge-1.1.10.tgz#b32547c55462df4582861e1ae005888056178d74" + integrity sha512-ICi668eSf6nqdwERqXXHWevrO/5CIx4mMFxsS7Ob/IS1lmRr5h7jeAfp4HVofdCkyMw6K61nzjfnhd9rd9AMjQ== + dependencies: + "@keyv/sqlite" "^4.0.5" + env-paths "^3.0.0" + keyv "5.5.0" + multiformats "^13.1.0" + uint8arrays "^4.0.3" + viem "2.45.0" + "@mintpass/challenge@1.1.6": version "1.1.6" resolved "https://registry.yarnpkg.com/@mintpass/challenge/-/challenge-1.1.6.tgz#91cb412585ee8febfd921135e4388c6d5e0783c9" @@ -4115,6 +4127,71 @@ viem "2.43.1" zod "4.1.12" +"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#8e7d818688d0b4aab2ce6271b271167a4eb75225": + version "0.0.8" + resolved "https://github.com/plebbit/plebbit-js.git#8e7d818688d0b4aab2ce6271b271167a4eb75225" + dependencies: + "@bonfida/spl-name-service" "3.0.10" + "@enhances/with-resolvers" "0.0.5" + "@helia/block-brokers" "5.0.13" + "@helia/delegated-routing-v1-http-api-client" "5.1.4" + "@helia/ipns" "9.1.5" + "@helia/unixfs" "6.0.4" + "@libp2p/crypto" "5.1.13" + "@libp2p/fetch" "4.0.9" + "@libp2p/gossipsub" "15.0.11" + "@libp2p/identify" "4.0.9" + "@libp2p/interfaces" "3.3.2" + "@libp2p/peer-id" "6.0.4" + "@libp2p/peer-id-factory" "4.2.4" + "@libp2p/websockets" "10.1.2" + "@mintpass/challenge" "1.1.10" + "@plebbit/plebbit-logger" "github:plebbit/plebbit-logger#355a96d7659ed820047980049dfa627d30d83a69" + "@plebbit/proper-lockfile" "github:plebbit/node-proper-lockfile#7fd6332117340c1d3d98dd0afee2d31cc06f72b8" + "@types/proper-lockfile" "4.1.2" + assert "2.1.0" + better-sqlite3 "12.4.1" + blockstore-core "6.1.1" + blockstore-fs "3.0.2" + blockstore-idb "3.0.1" + buffer "6.0.3" + captcha-canvas "3.3.4" + cbor "9.0.1" + debounce "1.2.1" + err-code "3.0.1" + ethers "6.16.0" + ext-name "5.0.0" + helia "6.0.14" + hpagent "1.2.0" + it-all "3.0.6" + jose "4.11.0" + js-sha256 "0.11.1" + js-sha512 "0.9.0" + kubo-rpc-client "6.0.2" + limiter-es6-compat "2.1.2" + localforage "1.10.0" + lodash.merge "4.6.2" + lru-cache "10.1.0" + open-graph-scraper "6.10.0" + p-limit "4.0.0" + p-timeout "6.1.2" + peer-id "0.16.0" + probe-image-size "7.2.3" + remeda "1.57.0" + retry "0.13.1" + rpc-websockets "9.3.1" + safe-stable-stringify "2.4.3" + sha1-uint8array "0.10.7" + skia-canvas "3.0.8" + tcp-port-used "1.0.2" + tiny-typed-emitter "2.1.0" + tinycache "1.1.2" + ts-custom-error "3.3.1" + typestub-ipfs-only-hash "4.0.0" + uuid "11.1.0" + viem "2.43.1" + zod "4.1.12" + "@plebbit/plebbit-logger@github:plebbit/plebbit-logger#355a96d7659ed820047980049dfa627d30d83a69": version "0.0.1" uid "355a96d7659ed820047980049dfa627d30d83a69" @@ -4144,6 +4221,22 @@ uuid "11.1.0" zustand "4.0.0" +"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#df3cb6901f63054870dc127cd7565c98617c2a4f": + version "0.0.1" + resolved "https://github.com/plebbit/plebbit-react-hooks.git#df3cb6901f63054870dc127cd7565c98617c2a4f" + dependencies: + "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#8e7d818688d0b4aab2ce6271b271167a4eb75225" + "@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git" + assert "2.0.0" + ethers "5.6.9" + localforage "1.10.0" + lodash.isequal "4.5.0" + memoizee "0.4.15" + quick-lru "5.1.1" + uint8arrays "3.1.1" + uuid "11.1.0" + zustand "4.0.0" + "@plebbit/proper-lockfile@github:plebbit/node-proper-lockfile#7fd6332117340c1d3d98dd0afee2d31cc06f72b8": version "4.1.2" resolved "https://codeload.github.com/plebbit/node-proper-lockfile/tar.gz/7fd6332117340c1d3d98dd0afee2d31cc06f72b8" @@ -11814,6 +11907,20 @@ ox@0.10.5: abitype "^1.2.3" eventemitter3 "5.0.1" +ox@0.11.3: + version "0.11.3" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.11.3.tgz#f50be1cae88822f5111b1e3f4a324c4c07eed422" + integrity sha512-1bWYGk/xZel3xro3l8WGg6eq4YEKlaqvyMtVhfMFpbJzK2F6rj4EDRtqDCWVEJMkzcmEi9uW2QxsqELokOlarw== + dependencies: + "@adraffy/ens-normalize" "^1.11.0" + "@noble/ciphers" "^1.3.0" + "@noble/curves" "1.9.1" + "@noble/hashes" "^1.8.0" + "@scure/bip32" "^1.7.0" + "@scure/bip39" "^1.6.0" + abitype "^1.2.3" + eventemitter3 "5.0.1" + oxfmt@0.20.0: version "0.20.0" resolved "https://registry.yarnpkg.com/oxfmt/-/oxfmt-0.20.0.tgz#0b8d467b4cc1972fda5cd33e15540cc98943759e" @@ -14850,6 +14957,20 @@ viem@2.43.1: ox "0.10.5" ws "8.18.3" +viem@2.45.0: + version "2.45.0" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.45.0.tgz#5f7f28b639193b783b7fbb6bef7575e9cc8d6f86" + integrity sha512-iVA9qrAgRdtpWa80lCZ6Jri6XzmLOwwA1wagX2HnKejKeliFLpON0KOdyfqvcy+gUpBVP59LBxP2aKiL3aj8fg== + dependencies: + "@noble/curves" "1.9.1" + "@noble/hashes" "1.8.0" + "@scure/bip32" "1.7.0" + "@scure/bip39" "1.6.0" + abitype "1.2.3" + isows "1.0.7" + ox "0.11.3" + ws "8.18.3" + vite-plugin-node-polyfills@0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.24.0.tgz#4a2e984bba134017fc88cace0149cf8afdb50b54" From 149d1a411f6e244e17637132d0b2b8d03937f8d5 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 28 Jan 2026 13:23:32 +0800 Subject: [PATCH 04/20] chore(deps): patch dependabot security alerts via resolutions --- package.json | 7 +- yarn.lock | 213 ++++----------------------------------------------- 2 files changed, 20 insertions(+), 200 deletions(-) diff --git a/package.json b/package.json index 62838331..26451d9a 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "i18next-browser-languagedetector": "7.1.0", "i18next-http-backend": "2.2.2", "json-stringify-pretty-compact": "4.0.0", - "lodash": "4.17.21", + "lodash": "4.17.23", "memoizee": "0.4.15", "react": "19.1.2", "react-dom": "19.1.2", @@ -143,7 +143,10 @@ "preact": "10.27.3", "@remix-run/router": "1.23.2", "undici": "^7.18.2", - "diff": "^8.0.3" + "diff": "^8.0.3", + "lodash": "4.17.23", + "seroval": "1.4.1", + "tar": "7.5.4" }, "main": "electron/main.js", "build": { diff --git a/yarn.lock b/yarn.lock index e3359f50..a2e302bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44,11 +44,6 @@ merge-options "^3.0.4" xml2js "^0.6.2" -"@adraffy/ens-normalize@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" - integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== - "@adraffy/ens-normalize@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" @@ -3634,18 +3629,6 @@ uint8arrays "^4.0.3" viem "2.45.0" -"@mintpass/challenge@1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@mintpass/challenge/-/challenge-1.1.6.tgz#91cb412585ee8febfd921135e4388c6d5e0783c9" - integrity sha512-SdTXxDK/Nb1tAuT0VbkEcZHmIwnmtuQojgtbKJr24nN1d8DQeMzIcjCavqeaW6MbifrRdZn1ujvDyKAjy16ENg== - dependencies: - "@keyv/sqlite" "^4.0.5" - env-paths "^3.0.0" - keyv "5.5.0" - multiformats "^13.1.0" - uint8arrays "^4.0.3" - viem "2.0.0" - "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -3735,7 +3718,7 @@ resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-2.0.1.tgz#fb07d6479d11fd10d7f601b6fb8fb9925b7accf7" integrity sha512-xHK3XHPUW8DTAobU+G0XT+/w+JLM7/8k1UFdB5xg/zTFPnFCobhftzw8wl4Lw2aq/Rvir5pxfZV5fEazmeCJ2g== -"@noble/curves@1.2.0", "@noble/curves@~1.2.0": +"@noble/curves@1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== @@ -3783,11 +3766,6 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e" integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== -"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== - "@noble/secp256k1@^1.3.0": version "1.7.2" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.2.tgz#c2c3343e2dce80e15a914d7442147507f8a98e7f" @@ -4062,71 +4040,6 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#3a70ec2b97a4fe2ed53f7746d69e9ec6a00dc324": - version "0.0.8" - resolved "https://github.com/plebbit/plebbit-js.git#3a70ec2b97a4fe2ed53f7746d69e9ec6a00dc324" - dependencies: - "@bonfida/spl-name-service" "3.0.10" - "@enhances/with-resolvers" "0.0.5" - "@helia/block-brokers" "5.0.13" - "@helia/delegated-routing-v1-http-api-client" "5.1.4" - "@helia/ipns" "9.1.5" - "@helia/unixfs" "6.0.4" - "@libp2p/crypto" "5.1.13" - "@libp2p/fetch" "4.0.9" - "@libp2p/gossipsub" "15.0.11" - "@libp2p/identify" "4.0.9" - "@libp2p/interfaces" "3.3.2" - "@libp2p/peer-id" "6.0.4" - "@libp2p/peer-id-factory" "4.2.4" - "@libp2p/websockets" "10.1.2" - "@mintpass/challenge" "1.1.6" - "@plebbit/plebbit-logger" "github:plebbit/plebbit-logger#355a96d7659ed820047980049dfa627d30d83a69" - "@plebbit/proper-lockfile" "github:plebbit/node-proper-lockfile#7fd6332117340c1d3d98dd0afee2d31cc06f72b8" - "@types/proper-lockfile" "4.1.2" - assert "2.1.0" - better-sqlite3 "12.4.1" - blockstore-core "6.1.1" - blockstore-fs "3.0.2" - blockstore-idb "3.0.1" - buffer "6.0.3" - captcha-canvas "3.3.4" - cbor "9.0.1" - debounce "1.2.1" - err-code "3.0.1" - ethers "6.16.0" - ext-name "5.0.0" - helia "6.0.14" - hpagent "1.2.0" - it-all "3.0.6" - jose "4.11.0" - js-sha256 "0.11.1" - js-sha512 "0.9.0" - kubo-rpc-client "6.0.2" - limiter-es6-compat "2.1.2" - localforage "1.10.0" - lodash.merge "4.6.2" - lru-cache "10.1.0" - open-graph-scraper "6.10.0" - p-limit "4.0.0" - p-timeout "6.1.2" - peer-id "0.16.0" - probe-image-size "7.2.3" - remeda "1.57.0" - retry "0.13.1" - rpc-websockets "9.3.1" - safe-stable-stringify "2.4.3" - sha1-uint8array "0.10.7" - skia-canvas "3.0.8" - tcp-port-used "1.0.2" - tiny-typed-emitter "2.1.0" - tinycache "1.1.2" - ts-custom-error "3.3.1" - typestub-ipfs-only-hash "4.0.0" - uuid "11.1.0" - viem "2.43.1" - zod "4.1.12" - "@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#8e7d818688d0b4aab2ce6271b271167a4eb75225": version "0.0.8" resolved "https://github.com/plebbit/plebbit-js.git#8e7d818688d0b4aab2ce6271b271167a4eb75225" @@ -4205,22 +4118,6 @@ dependencies: debug "4.3.4" -"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#60b9d18ebc647f76cdda8f36e2143192d7378658": - version "0.0.1" - resolved "https://github.com/plebbit/plebbit-react-hooks.git#60b9d18ebc647f76cdda8f36e2143192d7378658" - dependencies: - "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#3a70ec2b97a4fe2ed53f7746d69e9ec6a00dc324" - "@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git" - assert "2.0.0" - ethers "5.6.9" - localforage "1.10.0" - lodash.isequal "4.5.0" - memoizee "0.4.15" - quick-lru "5.1.1" - uint8arrays "3.1.1" - uuid "11.1.0" - zustand "4.0.0" - "@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#df3cb6901f63054870dc127cd7565c98617c2a4f": version "0.0.1" resolved "https://github.com/plebbit/plebbit-react-hooks.git#df3cb6901f63054870dc127cd7565c98617c2a4f" @@ -4511,20 +4408,6 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== -"@scure/base@~1.1.0", "@scure/base@~1.1.2": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" - integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== - -"@scure/bip32@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" - integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== - dependencies: - "@noble/curves" "~1.2.0" - "@noble/hashes" "~1.3.2" - "@scure/base" "~1.1.2" - "@scure/bip32@1.7.0", "@scure/bip32@^1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.7.0.tgz#b8683bab172369f988f1589640e53c4606984219" @@ -4534,14 +4417,6 @@ "@noble/hashes" "~1.8.0" "@scure/base" "~1.2.5" -"@scure/bip39@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" - integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== - dependencies: - "@noble/hashes" "~1.3.0" - "@scure/base" "~1.1.0" - "@scure/bip39@1.6.0", "@scure/bip39@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.6.0.tgz#475970ace440d7be87a6086cbee77cb8f1a684f9" @@ -5330,11 +5205,6 @@ abbrev@1, abbrev@^1.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -abitype@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.10.0.tgz#d3504747cc81df2acaa6c460250ef7bc9219a77c" - integrity sha512-QvMHEUzgI9nPj9TWtUGnS2scas80/qaL5PBxGdwWhhvzqXfOph+IEiiiWrzuisu3U3JgDQVruW9oLbJoQ3oZ3A== - abitype@1.2.3, abitype@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.2.3.tgz#bec3e09dea97d99ef6c719140bee663a329ad1f4" @@ -6356,11 +6226,6 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - chownr@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4" @@ -8277,13 +8142,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs-minipass@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" @@ -9722,11 +9580,6 @@ isomorphic-ws@^4.0.1: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== -isows@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" - integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== - isows@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915" @@ -10518,10 +10371,10 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lodash@4.17.21, lodash@4.17.23, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== log-symbols@^4.1.0: version "4.1.0" @@ -11376,17 +11229,12 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3. dependencies: yallist "^4.0.0" -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3, minipass@^7.0.4, minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: +minizlib@^2.0.0, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -11406,11 +11254,6 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - modify-filename@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" @@ -13311,10 +13154,10 @@ seroval-plugins@~1.3.0: resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.3.3.tgz#51bcacf09e5384080d7ea4002b08fd9f6166daf5" integrity sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w== -seroval@~1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.3.2.tgz#7e5be0dc1a3de020800ef013ceae3a313f20eca7" - integrity sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ== +seroval@1.4.1, seroval@~1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.4.1.tgz#c3cf4ba6db244c5fc693d873c4136a7724fb4a29" + integrity sha512-9GOc+8T6LN4aByLN75uRvMbrwY5RDBW6lSlknsY4LEa9ZmWcxKcRe1G/Q3HZXjltxMHTrStnvrwAICxZrhldtg== set-blocking@^2.0.0: version "2.0.0" @@ -14087,22 +13930,10 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.1.2, tar@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -tar@^7.4.3: - version "7.5.3" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.3.tgz#e1a41236e32446f75e63b720222112c4ffe5b3a1" - integrity sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ== +tar@7.5.4, tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.1.2, tar@^6.2.1, tar@^7.4.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.4.tgz#18b53b44f939a7e03ed874f1fafe17d29e306c81" + integrity sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" @@ -14929,20 +14760,6 @@ vfile@^6.0.0: "@types/unist" "^3.0.0" vfile-message "^4.0.0" -viem@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.0.0.tgz#1871512fbb4baa48164dc96765cc548dbfa964a8" - integrity sha512-WYPwVAs/wrQE1Li9mgHMZaZTnRZuyPJMauIhnpZKeZgtx7f+bWU3uvVSR5kxTf1aBFdaTuRc/umzX2ucQUurnA== - dependencies: - "@adraffy/ens-normalize" "1.10.0" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@scure/bip32" "1.3.2" - "@scure/bip39" "1.2.1" - abitype "0.10.0" - isows "1.0.3" - ws "8.13.0" - viem@2.43.1: version "2.43.1" resolved "https://registry.yarnpkg.com/viem/-/viem-2.43.1.tgz#ec6eb4841ed8eb4a11d54cb0b39afa8204fcd287" @@ -15463,7 +15280,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@7.4.6, ws@8.13.0, ws@8.17.1, ws@8.18.3, ws@^7.5.10, ws@^8.18.3, ws@^8.5.0: +ws@7.4.6, ws@8.17.1, ws@8.18.3, ws@^7.5.10, ws@^8.18.3, ws@^8.5.0: version "8.18.3" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== From ad8e3262db9750282b163e78181502cda08b2873 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 28 Jan 2026 15:49:36 +0800 Subject: [PATCH 05/20] Update AGENTS.md --- AGENTS.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e9df7c93..a7bd9f92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,7 +31,8 @@ yarn electron # Run Electron app ## Code Style - TypeScript strict mode -- Prettier for formatting (runs on pre-commit) +- **oxfmt** for formatting (runs on pre-commit via husky; recommend setting up AI hooks too) +- **oxlint** for linting, **tsgo** for type-checking - **DRY principle**: Always follow the DRY principle when possible. Never repeat UI elements across views—extract them into reusable components in `src/components/`. Same applies to logic—extract into custom hooks in `src/hooks/`. ## React Patterns (Critical) @@ -106,6 +107,55 @@ Discover and install skills from the open agent skills ecosystem. npx skills add https://github.com/vercel-labs/skills --skill find-skills ``` +## AI Agent Hooks (Recommended) + +If you're using an AI coding assistant (Cursor, Claude Code, Codex, etc.), set up hooks to automatically enforce code quality. Most modern AI agents support lifecycle hooks. + +### Recommended Hooks + +Set up these hooks for this project: + +| Hook | Command | Purpose | +|------|---------|---------| +| `afterFileEdit` | `npx oxfmt ` | Auto-format files after AI edits | +| `stop` | `yarn lint && yarn type-check` | Verify code when agent finishes | + +### Why Use Hooks + +- **Consistent formatting** — Every file follows the same style +- **Catch issues early** — Lint and type errors are caught before commit/CI +- **Less manual work** — No need to run `yarn prettier`, `yarn lint`, `yarn type-check` manually + +### Example Hook Scripts + +**Format hook** (runs after each file edit): +```bash +#!/bin/bash +# Auto-format JS/TS files after AI edits +# Hook receives JSON via stdin with file_path + +input=$(cat) +file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/') + +case "$file_path" in + *.js|*.ts|*.tsx|*.mjs) npx oxfmt "$file_path" 2>/dev/null ;; +esac +exit 0 +``` + +**Verify hook** (runs when agent finishes): +```bash +#!/bin/bash +# Run lint and type-check when agent finishes + +cat > /dev/null # consume stdin +echo "=== yarn lint ===" && yarn lint +echo "=== yarn type-check ===" && yarn type-check +exit 0 +``` + +Consult your AI tool's documentation for how to configure hooks (e.g., `hooks.json` for Cursor/Claude Code). + ## Recommended MCP Servers ### GitHub MCP @@ -171,17 +221,29 @@ node scripts/update-translations.js --audit --write ### GitHub Commits -When proposing or implementing code changes, always suggest a short GitHub commit title, and if the commit title isn't exhaustive enough, then provide also a commit description. Format: +When proposing or implementing code changes, always suggest a commit message. Format: -- **Title**: Use [Conventional Commits](https://www.conventionalcommits.org/) style (e.g., `fix: ...`, `feat: ...`, `perf: ...`, `refactor: ...`, `docs: ...`, `chore: ...`). Use the `perf` type for performance optimizations (not `fix`). Keep it short. Use markdown. -- **Description**: If the title is missing important information, also provide a description, consisting of 2-3 informal sentences describing the solution (not the problem) that is being committed. Concise, technical, no bullet points. Use markdown. +- **Title**: Use [Conventional Commits](https://www.conventionalcommits.org/) style. Use `perf` for performance optimizations (not `fix`). Keep it short. **MUST be wrapped in backticks.** +- **Description**: Optional. 2-3 informal sentences describing the solution (not the problem). Concise, technical, no bullet points. Use backticks for code references. -### Github Issues +Example output: -When proposing or implementing code changes, always suggest a GitHub issue title and description to keep track of the problem that was fixed. Format: +> **Commit title:** `fix: correct date formatting in timezone conversion` +> +> Updated `formatDate()` in `date-utils.ts` to properly handle timezone offsets. -- **Title**: As short as possible, may use commas to list related commits that resolve the same Github issue. Use markdown. -- **Description**: 2-3 informal sentences describing the problem (not the solution). Write as if the issue hasn't been fixed yet. Bullet points are encouraged but may not always be necessary. Use markdown. +### GitHub Issues + +When proposing or implementing code changes, always suggest a GitHub issue to track the problem. Format: + +- **Title**: As short as possible. **MUST be wrapped in backticks.** +- **Description**: 2-3 informal sentences describing the problem (not the solution). Write as if the issue hasn't been fixed yet. Use backticks for code references. + +Example output: + +> **GitHub issue:** +> - **Title:** `Date formatting displays incorrect timezone` +> - **Description:** Comment timestamps show incorrect timezones when users view posts from different regions. The `formatDate()` function doesn't account for user's local timezone settings. ### Troubleshooting @@ -194,4 +256,3 @@ When stuck on a bug or issue, search the web for solutions. Developer communitie - Keep components focused—split large components - Add comments for complex/unclear code (especially custom functions in this FOSS project with many contributors). Skip comments for obvious code - Test on mobile viewport (this is a responsive app) - From 74946b875d6a0e28c50840fabccf9738bdbabf27 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 28 Jan 2026 16:18:20 +0800 Subject: [PATCH 06/20] Update AGENTS.md --- AGENTS.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a7bd9f92..b80f0055 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,13 +118,14 @@ Set up these hooks for this project: | Hook | Command | Purpose | |------|---------|---------| | `afterFileEdit` | `npx oxfmt ` | Auto-format files after AI edits | -| `stop` | `yarn lint && yarn type-check` | Verify code when agent finishes | +| `stop` | `yarn lint && yarn type-check && yarn audit` | Verify code and security when agent finishes | ### Why Use Hooks - **Consistent formatting** — Every file follows the same style - **Catch issues early** — Lint and type errors are caught before commit/CI -- **Less manual work** — No need to run `yarn prettier`, `yarn lint`, `yarn type-check` manually +- **Security awareness** — `yarn audit` flags known vulnerabilities in dependencies +- **Less manual work** — No need to run `yarn prettier`, `yarn lint`, `yarn type-check`, `yarn audit` manually ### Example Hook Scripts @@ -146,11 +147,12 @@ exit 0 **Verify hook** (runs when agent finishes): ```bash #!/bin/bash -# Run lint and type-check when agent finishes +# Run lint, type-check, and security audit when agent finishes cat > /dev/null # consume stdin echo "=== yarn lint ===" && yarn lint echo "=== yarn type-check ===" && yarn type-check +echo "=== yarn audit ===" && yarn audit exit 0 ``` @@ -249,6 +251,34 @@ Example output: When stuck on a bug or issue, search the web for solutions. Developer communities often have recent fixes or workarounds that aren't in training data. +## Dependency Management + +### Pin All Package Versions (No Carets) + +When adding or updating npm packages, **always use exact version numbers**—never use carets (`^`) or tildes (`~`). + +```bash +# ✅ Correct +yarn add lodash@4.17.21 + +# ❌ Wrong (will add caret by default) +yarn add lodash +``` + +**Why pin versions:** + +- **Supply chain security**: A compromised package could push a malicious minor/patch update. With carets, running `yarn upgrade` or regenerating `yarn.lock` would auto-install it. +- **Reproducibility**: Guarantees identical dependencies across all environments. +- **Defense in depth**: While `yarn.lock` pins versions in practice, explicit pinning in `package.json` protects against lockfile regeneration and makes the intended version auditable. + +**When upgrading packages:** + +1. Specify the exact version: `yarn add package@1.2.3` +2. Review the changelog for breaking changes or security notes +3. Test the upgrade before committing + +**Note:** This applies to both `dependencies` and `devDependencies`. There are no exceptions—the convenience of auto-updates doesn't justify the security risk. + ## Boundaries - Never commit secrets or API keys From 7c8a7b8df8ba684c263a2d4216b6a148b4876035 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 28 Jan 2026 16:40:30 +0800 Subject: [PATCH 07/20] Update AGENTS.md --- AGENTS.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b80f0055..c52073af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,14 +118,15 @@ Set up these hooks for this project: | Hook | Command | Purpose | |------|---------|---------| | `afterFileEdit` | `npx oxfmt ` | Auto-format files after AI edits | -| `stop` | `yarn lint && yarn type-check && yarn audit` | Verify code and security when agent finishes | +| `stop` | `yarn build && yarn lint && yarn type-check && yarn audit` | Build, verify code, and check security when agent finishes | ### Why Use Hooks - **Consistent formatting** — Every file follows the same style +- **Catch build errors** — `yarn build` catches compilation errors that would break production - **Catch issues early** — Lint and type errors are caught before commit/CI - **Security awareness** — `yarn audit` flags known vulnerabilities in dependencies -- **Less manual work** — No need to run `yarn prettier`, `yarn lint`, `yarn type-check`, `yarn audit` manually +- **Less manual work** — No need to run `yarn build`, `yarn lint`, `yarn type-check`, `yarn audit` manually ### Example Hook Scripts @@ -147,9 +148,10 @@ exit 0 **Verify hook** (runs when agent finishes): ```bash #!/bin/bash -# Run lint, type-check, and security audit when agent finishes +# Run build, lint, type-check, and security audit when agent finishes cat > /dev/null # consume stdin +echo "=== yarn build ===" && yarn build echo "=== yarn lint ===" && yarn lint echo "=== yarn type-check ===" && yarn type-check echo "=== yarn audit ===" && yarn audit From 63ee8aa1681454fbd39e808ff40bde6a27235d11 Mon Sep 17 00:00:00 2001 From: plebeius Date: Wed, 28 Jan 2026 16:51:27 +0800 Subject: [PATCH 08/20] refactor: rebrand default subplebbits to 5chan directories --- README.md | 4 +- src/app.tsx | 6 +- .../board-buttons/board-buttons.tsx | 34 ++--- src/components/board-header/board-header.tsx | 10 +- src/components/catalog-row/catalog-row.tsx | 6 +- .../create-board-modal/create-board-modal.tsx | 4 +- .../directory-modal/directory-modal.tsx | 4 +- src/components/post-desktop/post-desktop.tsx | 18 +-- .../post-menu-desktop/post-menu-desktop.tsx | 6 +- src/components/post-form/post-form.tsx | 6 +- .../post-menu-mobile/post-menu-mobile.tsx | 6 +- src/components/post-mobile/post-mobile.tsx | 14 +- .../reply-quote-preview.tsx | 18 +-- src/components/topbar/topbar.tsx | 16 +- ...ubplebbits.json => 5chan-directories.json} | 2 +- .../use-account-subplebbits-with-metadata.ts | 4 +- ...ault-subplebbits.ts => use-directories.ts} | 142 +++++++++--------- ...e-filtered-default-subplebbit-addresses.ts | 19 --- src/hooks/use-filtered-directory-addresses.ts | 19 +++ src/hooks/use-initial-theme.ts | 20 +-- src/hooks/use-resolved-subplebbit-address.ts | 22 +-- src/hooks/use-theme.ts | 12 +- src/lib/utils/route-utils.ts | 64 ++++---- src/views/board/board.tsx | 43 ++---- src/views/catalog/catalog.tsx | 20 +-- src/views/home/boards-list/boards-list.tsx | 14 +- src/views/home/home.tsx | 28 ++-- .../popular-threads-box.tsx | 41 ++--- src/views/mod-queue/mod-queue.tsx | 35 +++-- src/views/not-found/not-found.tsx | 6 +- src/views/pending-post/pending-post.tsx | 8 +- src/views/post/post.tsx | 8 +- src/views/rules/rules.tsx | 18 +-- 33 files changed, 329 insertions(+), 348 deletions(-) rename src/data/{default-subplebbits.json => 5chan-directories.json} (96%) rename src/hooks/{use-default-subplebbits.ts => use-directories.ts} (62%) delete mode 100644 src/hooks/use-filtered-default-subplebbit-addresses.ts create mode 100644 src/hooks/use-filtered-directory-addresses.ts diff --git a/README.md b/README.md index 61843a1c..8a5c2134 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Currently, directory assignments are temporarily handpicked by developers throug ### How It Works -- **Current System**: Developers manually curate directory assignments by reviewing pull requests to the [5chan-multisub.json](https://github.com/bitsocialhq/lists/blob/master/5chan-multisub.json) file. +- **Current System**: Developers manually curate directory assignments by reviewing pull requests to the [5chan-directories.json](https://github.com/bitsocialhq/lists/blob/master/5chan-directories.json) file. - **Future System**: Directory board assignments will be determined through gasless voting using pubsub. Community members will vote on which board should be assigned to each directory, and the highest-voted board will automatically become the directory board. This creates a competitive marketplace where board quality and community engagement determine directory placement. @@ -72,7 +72,7 @@ To have your board appear in a directory on the 5chan homepage: - Relevant to the directory category - **99% uptime** (since a board acts like its own server—it's a P2P node) -2. Open a pull request on GitHub by editing the [5chan-multisub.json](https://github.com/bitsocialhq/lists/blob/master/5chan-multisub.json) file +2. Open a pull request on GitHub by editing the [5chan-directories.json](https://github.com/bitsocialhq/lists/blob/master/5chan-directories.json) file 3. Add your board's entry with: - Title: in the format `/directoryCode/ - Title`, e.g. "/biz/ - Business & Finance"; diff --git a/src/app.tsx b/src/app.tsx index 81f5eba1..5a6d690a 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -9,7 +9,7 @@ import useCreateBoardModalStore from './stores/use-create-board-modal-store'; import useSpecialThemeStore from './stores/use-special-theme-store'; import useIsMobile from './hooks/use-is-mobile'; import useTheme from './hooks/use-theme'; -import { useDefaultSubplebbits } from './hooks/use-default-subplebbits'; +import { useDirectories } from './hooks/use-directories'; import { getSubplebbitAddress, isPostRoute, isPendingPostRoute, isModQueueRoute } from './lib/utils/route-utils'; import styles from './app.module.css'; import FAQ from './views/faq'; @@ -44,8 +44,8 @@ const BoardLayout = () => { const isInAllView = isAllView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); const isInModView = isModView(location.pathname); - const defaultSubplebbits = useDefaultSubplebbits(); - const subplebbitAddress = boardIdentifier ? getSubplebbitAddress(boardIdentifier, defaultSubplebbits) : undefined; + const directories = useDirectories(); + const subplebbitAddress = boardIdentifier ? getSubplebbitAddress(boardIdentifier, directories) : undefined; const pendingPost = useAccountComment({ commentIndex: accountCommentIndex ? parseInt(accountCommentIndex) : undefined }); const { closeCreateBoardModal } = useCreateBoardModalStore(); diff --git a/src/components/board-buttons/board-buttons.tsx b/src/components/board-buttons/board-buttons.tsx index 381c965f..6ee39ad0 100644 --- a/src/components/board-buttons/board-buttons.tsx +++ b/src/components/board-buttons/board-buttons.tsx @@ -3,7 +3,7 @@ import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { useAccountComment, useSubscribe } from '@plebbit/plebbit-react-hooks'; import useSubplebbitsPagesStore from '@plebbit/plebbit-react-hooks/dist/stores/subplebbits-pages'; import { isAllView, isCatalogView, isModView, isModQueueView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; -import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../hooks/use-directories'; import { getBoardPath, isDirectoryBoard } from '../../lib/utils/route-utils'; import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address'; import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; @@ -35,7 +35,7 @@ interface BoardButtonsProps { const CatalogButton = ({ address, isInAllView, isInSubscriptionsView, isInModView }: BoardButtonsProps) => { const { t } = useTranslation(); const params = useParams(); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const createCatalogLink = () => { if (isInAllView) { @@ -50,9 +50,9 @@ const CatalogButton = ({ address, isInAllView, isInSubscriptionsView, isInModVie } let boardPath = ''; if (address) { - boardPath = getBoardPath(address, defaultSubplebbits); - } else if (Array.isArray(defaultSubplebbits) && defaultSubplebbits.length > 0 && defaultSubplebbits[0]?.address) { - boardPath = getBoardPath(defaultSubplebbits[0].address, defaultSubplebbits); + boardPath = getBoardPath(address, directories); + } else if (Array.isArray(directories) && directories.length > 0 && directories[0]?.address) { + boardPath = getBoardPath(directories[0].address, directories); } return `/${boardPath}/catalog`; }; @@ -78,7 +78,7 @@ const SubscribeButton = ({ address }: BoardButtonsProps) => { const ReturnButton = ({ address, isInAllView, isInSubscriptionsView, isInModView, isInModQueueView }: BoardButtonsProps) => { const { t } = useTranslation(); const params = useParams(); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const createReturnLink = () => { if (isInAllView) { @@ -99,9 +99,9 @@ const ReturnButton = ({ address, isInAllView, isInSubscriptionsView, isInModView } let boardPath = ''; if (address) { - boardPath = getBoardPath(address, defaultSubplebbits); - } else if (Array.isArray(defaultSubplebbits) && defaultSubplebbits.length > 0 && defaultSubplebbits[0]?.address) { - boardPath = getBoardPath(defaultSubplebbits[0].address, defaultSubplebbits); + boardPath = getBoardPath(address, directories); + } else if (Array.isArray(directories) && directories.length > 0 && directories[0]?.address) { + boardPath = getBoardPath(directories[0].address, directories); } return `/${boardPath}`; }; @@ -116,13 +116,13 @@ const ReturnButton = ({ address, isInAllView, isInSubscriptionsView, isInModView const VoteButton = () => { const { t } = useTranslation(); const params = useParams(); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); // Get the boardIdentifier from params (try boardIdentifier first, then subplebbitAddress for backward compatibility) const boardIdentifier = params.boardIdentifier || params.subplebbitAddress; // Only render the vote button if we're on a directory board route - if (!boardIdentifier || !isDirectoryBoard(boardIdentifier, defaultSubplebbits)) { + if (!boardIdentifier || !isDirectoryBoard(boardIdentifier, directories)) { return null; } @@ -418,9 +418,9 @@ export const MobileBoardButtons = () => { const { filteredCount, searchText } = useCatalogFiltersStore(); // Check if we should show the vote button (only for directory boards) - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const boardIdentifier = params.boardIdentifier || params.subplebbitAddress; - const showVoteButton = boardIdentifier && isDirectoryBoard(boardIdentifier, defaultSubplebbits); + const showVoteButton = boardIdentifier && isDirectoryBoard(boardIdentifier, directories); return (
@@ -539,9 +539,9 @@ export const DesktopBoardButtons = () => { const { filteredCount, searchText } = useCatalogFiltersStore(); // Check if we should show the vote button (only for directory boards) - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const boardIdentifier = params.boardIdentifier || params.subplebbitAddress; - const showVoteButton = boardIdentifier && isDirectoryBoard(boardIdentifier, defaultSubplebbits); + const showVoteButton = boardIdentifier && isDirectoryBoard(boardIdentifier, directories); return ( <> @@ -655,9 +655,9 @@ const SearchOPsBar = () => { const isInAllView = isAllView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); const isInModView = isModView(location.pathname); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const resolvedAddress = useResolvedSubplebbitAddress(); - const boardPath = resolvedAddress ? getBoardPath(resolvedAddress, defaultSubplebbits) : params?.boardIdentifier || params?.subplebbitAddress; + const boardPath = resolvedAddress ? getBoardPath(resolvedAddress, directories) : params?.boardIdentifier || params?.subplebbitAddress; const handleSearch = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { diff --git a/src/components/board-header/board-header.tsx b/src/components/board-header/board-header.tsx index a2123a50..920acf00 100644 --- a/src/components/board-header/board-header.tsx +++ b/src/components/board-header/board-header.tsx @@ -8,7 +8,7 @@ import Plebbit from '@plebbit/plebbit-js'; import { useStableSubplebbit } from '../../hooks/use-stable-subplebbit'; import { isAllView, isSubscriptionsView, isModView } from '../../lib/utils/view-utils'; import styles from './board-header.module.css'; -import { useMultisubMetadata, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectoriesMetadata, useDirectories } from '../../hooks/use-directories'; import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address'; import useIsMobile from '../../hooks/use-is-mobile'; import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; @@ -59,11 +59,11 @@ const BoardHeader = () => { const stableSubplebbit = useStableSubplebbit(subplebbitAddress); const { address, shortAddress } = stableSubplebbit || {}; - const multisubMetadata = useMultisubMetadata(); - const defaultSubplebbits = useDefaultSubplebbits(); + const directoriesMetadata = useDirectoriesMetadata(); + const directories = useDirectories(); // Find matching subplebbit from default list to get its title - const defaultSubplebbit = subplebbitAddress ? defaultSubplebbits.find((s) => s.address === subplebbitAddress) : null; + const defaultSubplebbit = subplebbitAddress ? directories.find((s) => s.address === subplebbitAddress) : null; // Use accounts store with selector to only subscribe to subscriptions count const subscriptionsCount = useAccountsStore((state) => { @@ -74,7 +74,7 @@ const BoardHeader = () => { const subscriptionsSubtitle = t('subscriptions_subtitle', { count: subscriptionsCount }); const title = isInAllView - ? multisubMetadata?.title || '/all/ - 5chan Directories' + ? directoriesMetadata?.title || '/all/ - 5chan Directories' : isInSubscriptionsView ? '/subs/ - Subscriptions' : isInModView diff --git a/src/components/catalog-row/catalog-row.tsx b/src/components/catalog-row/catalog-row.tsx index e3cdb44c..19aca254 100644 --- a/src/components/catalog-row/catalog-row.tsx +++ b/src/components/catalog-row/catalog-row.tsx @@ -9,7 +9,7 @@ import { shouldShowSnow } from '../../lib/snow'; import { getHasThumbnail } from '../../lib/utils/media-utils'; import { getFormattedTimeAgo } from '../../lib/utils/time-utils'; import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils'; -import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../hooks/use-directories'; import { getBoardPath } from '../../lib/utils/route-utils'; import useCatalogFiltersStore from '../../stores/use-catalog-filters-store'; import useCatalogStyleStore from '../../stores/use-catalog-style-store'; @@ -125,8 +125,8 @@ const CatalogPost = memo( const params = useParams(); const isInAllView = isAllView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); - const defaultSubplebbits = useDefaultSubplebbits(); - const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : ''; + const directories = useDirectories(); + const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, directories) : ''; const postMenuProps = useMemo(() => selectPostMenuProps(post), [post]); const postLink = boardPath ? `/${boardPath}/thread/${cid}` : `/thread/${cid}`; diff --git a/src/components/create-board-modal/create-board-modal.tsx b/src/components/create-board-modal/create-board-modal.tsx index c6d78e52..e8583bb8 100644 --- a/src/components/create-board-modal/create-board-modal.tsx +++ b/src/components/create-board-modal/create-board-modal.tsx @@ -42,8 +42,8 @@ const CreateBoardModal = () => {

Submitting to a Directory

Open a PR editing{' '} - - 5chan-multisub.json + + 5chan-directories.json {' '} with your board's title, address, and NSFW status. Devs will review and merge if approved. 99% uptime is required.

diff --git a/src/components/directory-modal/directory-modal.tsx b/src/components/directory-modal/directory-modal.tsx index 3edada33..3b9a6c7c 100644 --- a/src/components/directory-modal/directory-modal.tsx +++ b/src/components/directory-modal/directory-modal.tsx @@ -50,8 +50,8 @@ const DirectoryModal = () => {

Directory Submission

To submit your board, open a PR editing{' '} - - 5chan-multisub.json + + 5chan-directories.json {' '} with your board's title, address, and NSFW status. Requirements: 99% uptime (they're P2P nodes), active, well-moderated, and relevant to the category. Devs will review and approve/reject based on these criteria. diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx index 166d60ac..9d3adf0c 100644 --- a/src/components/post-desktop/post-desktop.tsx +++ b/src/components/post-desktop/post-desktop.tsx @@ -11,7 +11,7 @@ import { getFormattedDate, getFormattedTimeAgo } from '../../lib/utils/time-util import { isValidURL } from '../../lib/utils/url-utils'; import { isAllView, isModQueueView, isPendingPostView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; import useModQueueStore from '../../stores/use-mod-queue-store'; -import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../hooks/use-directories'; import { getBoardPath } from '../../lib/utils/route-utils'; import useAvatarVisibilityStore from '../../stores/use-avatar-visibility-store'; import useAuthorAddressClick from '../../hooks/use-author-address-click'; @@ -85,8 +85,8 @@ const PostInfo = ({ const { showOmittedReplies } = useShowOmittedReplies(); const { imageUrl: avatarImageUrl } = useAuthorAvatar({ author }); const { hideAvatars } = useAvatarVisibilityStore(); - const defaultSubplebbits = useDefaultSubplebbits(); - const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : undefined; + const directories = useDirectories(); + const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, directories) : undefined; const postMenuProps = useMemo(() => selectPostMenuProps(post), [post]); const params = useParams(); @@ -453,7 +453,7 @@ const PostMedia = ({ const { url } = commentMediaInfo || {}; let type = commentMediaInfo?.type; const gifFrameUrl = useFetchGifFirstFrame(url); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); if (type === 'gif' && gifFrameUrl !== null) { type = 'animated gif'; @@ -465,7 +465,7 @@ const PostMedia = ({ const [showThumbnail, setShowThumbnail] = useState(true); const mediaDimensions = getMediaDimensions(commentMediaInfo); - const boardPath = getBoardPath(subplebbitAddress, defaultSubplebbits); + const boardPath = getBoardPath(subplebbitAddress, directories); return (

@@ -528,8 +528,8 @@ const Reply = ({ postReplyCount, reply, roles, threadNumber }: PostProps) => { } const { author, cid, deleted, link, linkHeight, linkWidth, postCid, reason, removed, spoiler, subplebbitAddress, thumbnailUrl, parentCid } = post || {}; - const defaultSubplebbits = useDefaultSubplebbits(); - const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : undefined; + const directories = useDirectories(); + const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, directories) : undefined; const location = useLocation(); const route = boardPath ? `/${boardPath}/thread/${cid}` : `/thread/${cid}`; @@ -591,8 +591,8 @@ const PostDesktop = ({ const isInPostPageView = isPostPageView(location.pathname, params); const isInAllView = isAllView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, params); - const defaultSubplebbits = useDefaultSubplebbits(); - const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : undefined; + const directories = useDirectories(); + const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, directories) : undefined; const { hidden, unhide, hide } = useHide({ cid }); const isHidden = hidden && !isInPostPageView; diff --git a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx index 5dbbfe4c..33bf8269 100644 --- a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx +++ b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx @@ -9,7 +9,7 @@ import { getCommentMediaInfo } from '../../../lib/utils/media-utils'; import { copyShareLinkToClipboard, isValidURL, type ShareLinkType } from '../../../lib/utils/url-utils'; import { copyToClipboard } from '../../../lib/utils/clipboard-utils'; import { getBoardPath } from '../../../lib/utils/route-utils'; -import { useDefaultSubplebbits } from '../../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../../hooks/use-directories'; import { isAllView, isCatalogView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils'; import useHide from '../../../hooks/use-hide'; import _ from 'lodash'; @@ -21,8 +21,8 @@ type CopyLinkButtonProps = const CopyLinkButton = ({ cid, subplebbitAddress, linkType, onClose }: CopyLinkButtonProps) => { const { t } = useTranslation(); - const defaultSubplebbits = useDefaultSubplebbits(); - const boardIdentifier = getBoardPath(subplebbitAddress, defaultSubplebbits); + const directories = useDirectories(); + const boardIdentifier = getBoardPath(subplebbitAddress, directories); return (
{ diff --git a/src/components/post-form/post-form.tsx b/src/components/post-form/post-form.tsx index d920e83b..2cea55fc 100644 --- a/src/components/post-form/post-form.tsx +++ b/src/components/post-form/post-form.tsx @@ -9,7 +9,7 @@ import { getHasThumbnail, getLinkMediaInfo } from '../../lib/utils/media-utils'; import { formatMarkdown } from '../../lib/utils/post-utils'; import { isValidURL } from '../../lib/utils/url-utils'; import { isAllView, isCatalogView, isModQueueView, isModView, isPostPageView, isSubscriptionsView } from '../../lib/utils/view-utils'; -import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../hooks/use-directories'; import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address'; import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame'; import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline'; @@ -71,7 +71,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: const isInModView = isModView(location.pathname); const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams()); const subscriptions = account?.subscriptions || []; - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const { accountSubplebbits } = useAccountSubplebbits(); const accountSubplebbitAddresses = Object.keys(accountSubplebbits); @@ -338,7 +338,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid: - {subplebbits.map((sub) => { + {communities.map((sub) => { const address = sub.address; if (!address) return null; return ( ); })} @@ -594,7 +593,7 @@ export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProp (state) => { const activeAccountId = state.activeAccountId; const activeAccount = activeAccountId ? state.accounts[activeAccountId] : undefined; - const accountSubplebbits = activeAccount?.subplebbits || {}; + const accountSubplebbits = activeAccount?.communities || {}; return Object.keys(accountSubplebbits); }, (prev, next) => { @@ -603,14 +602,14 @@ export const ModQueueButton = ({ boardIdentifier, isMobile }: ModQueueButtonProp }, ); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const resolvedAddress = useMemo(() => { if (boardIdentifier) { - return getSubplebbitAddress(boardIdentifier, defaultSubplebbits); + return getSubplebbitAddress(boardIdentifier, directories); } return undefined; - }, [boardIdentifier, defaultSubplebbits]); + }, [boardIdentifier, directories]); const subplebbitAddresses = useMemo(() => { if (resolvedAddress) { @@ -652,7 +651,7 @@ export const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueV (state) => { const activeAccountId = state.activeAccountId; const activeAccount = activeAccountId ? state.accounts[activeAccountId] : undefined; - const accountSubplebbits = activeAccount?.subplebbits || {}; + const accountSubplebbits = activeAccount?.communities || {}; return Object.keys(accountSubplebbits); }, (prev, next) => { @@ -661,18 +660,18 @@ export const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueV }, ); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); const boardIdentifier = propBoardIdentifier || params.boardIdentifier; const resolvedAddress = useMemo(() => { if (boardIdentifier) { - return getSubplebbitAddress(boardIdentifier, defaultSubplebbits); + return getSubplebbitAddress(boardIdentifier, directories); } return undefined; - }, [boardIdentifier, defaultSubplebbits]); + }, [boardIdentifier, directories]); - const subplebbitsWithMetadata = useAccountSubplebbitsWithMetadata(); + const communitiesWithMetadata = useAccountSubplebbitsWithMetadata(); const subplebbitAddresses = useMemo(() => { if (resolvedAddress) { @@ -740,7 +739,7 @@ export const ModQueueView = ({ boardIdentifier: propBoardIdentifier }: ModQueueV {!resolvedAddress && (
- +
)} diff --git a/src/views/not-found/not-found.tsx b/src/views/not-found/not-found.tsx index 82c98fe3..b2a471eb 100644 --- a/src/views/not-found/not-found.tsx +++ b/src/views/not-found/not-found.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; import { useSubplebbitField } from '../../hooks/use-stable-subplebbit'; -import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../hooks/use-directories'; import { getSubplebbitAddress } from '../../lib/utils/route-utils'; import { HomeLogo } from '../home'; import styles from './not-found.module.css'; @@ -18,8 +18,8 @@ const NotFound = () => { // Extract boardIdentifier from pathname (could be directory code or address) const pathParts = location.pathname.split('/').filter(Boolean); const boardIdentifier = pathParts[0] && pathParts[0] !== 'not-found' && pathParts[0] !== 'faq' ? pathParts[0] : ''; - const defaultSubplebbits = useDefaultSubplebbits(); - const subplebbitAddress = boardIdentifier ? getSubplebbitAddress(boardIdentifier, defaultSubplebbits) : ''; + const directories = useDirectories(); + const subplebbitAddress = boardIdentifier ? getSubplebbitAddress(boardIdentifier, directories) : ''; // Only subscribe to address and shortAddress to avoid rerenders from updatingState changes const address = useSubplebbitField(subplebbitAddress, (subplebbit) => subplebbit?.address); const shortAddress = useSubplebbitField(subplebbitAddress, (subplebbit) => subplebbit?.shortAddress); diff --git a/src/views/pending-post/pending-post.tsx b/src/views/pending-post/pending-post.tsx index a381d1c6..dd7d74f0 100644 --- a/src/views/pending-post/pending-post.tsx +++ b/src/views/pending-post/pending-post.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useAccountComment, useAccountComments } from '@plebbit/plebbit-react-hooks'; -import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../hooks/use-directories'; import { getBoardPath } from '../../lib/utils/route-utils'; import { Post } from '../post'; @@ -11,7 +11,7 @@ const PendingPost = () => { const commentIndex = accountCommentIndex ? parseInt(accountCommentIndex) : undefined; const post = useAccountComment({ commentIndex }); const navigate = useNavigate(); - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); useEffect(() => window.scrollTo(0, 0), []); @@ -31,10 +31,10 @@ const PendingPost = () => { useEffect(() => { if (post?.cid && post?.subplebbitAddress) { - const boardPath = getBoardPath(post.subplebbitAddress, defaultSubplebbits); + const boardPath = getBoardPath(post.subplebbitAddress, directories); navigate(`/${boardPath}/thread/${post.cid}`, { replace: true }); } - }, [post, navigate, defaultSubplebbits]); + }, [post, navigate, directories]); return ; }; diff --git a/src/views/post/post.tsx b/src/views/post/post.tsx index 8f58a58a..e1416051 100644 --- a/src/views/post/post.tsx +++ b/src/views/post/post.tsx @@ -5,7 +5,7 @@ import { useSubplebbitField } from '../../hooks/use-stable-subplebbit'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { isAllView } from '../../lib/utils/view-utils'; import { useResolvedSubplebbitAddress } from '../../hooks/use-resolved-subplebbit-address'; -import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits'; +import { useDirectories } from '../../hooks/use-directories'; import { isDirectoryBoard } from '../../lib/utils/route-utils'; import useIsMobile from '../../hooks/use-is-mobile'; import ErrorDisplay from '../../components/error-display/error-display'; @@ -113,7 +113,7 @@ const PostPage = () => { const subplebbit = useSubplebbit({ subplebbitAddress }); const { error: subplebbitError, shortAddress, title } = subplebbit || {}; - const defaultSubplebbits = useDefaultSubplebbits(); + const directories = useDirectories(); // if the comment is a reply, return the post comment instead, then the reply will be highlighted in the thread const postComment = useComment({ commentCid: comment?.postCid }); @@ -133,7 +133,7 @@ const PostPage = () => { useEffect(() => { const boardIdentifier = params.boardIdentifier; - const isDirectory = boardIdentifier ? isDirectoryBoard(boardIdentifier, defaultSubplebbits) : false; + const isDirectory = boardIdentifier ? isDirectoryBoard(boardIdentifier, directories) : false; let boardTitle: string; if (isInAllView) { @@ -147,7 +147,7 @@ const PostPage = () => { const postTitle = post?.title?.slice(0, 30) || post?.content?.slice(0, 30); const postTitlePart = postTitle ? ` - ${postTitle.trim()}...` : ''; document.title = `${boardTitle}${postTitlePart} - 5chan`; - }, [title, shortAddress, subplebbitAddress, post?.title, post?.content, isInAllView, t, params.boardIdentifier, defaultSubplebbits]); + }, [title, shortAddress, subplebbitAddress, post?.title, post?.content, isInAllView, t, params.boardIdentifier, directories]); const shouldShowCommentError = comment?.error?.message && !comment?.cid; const shouldShowPostError = post?.error && post?.replyCount > 0 && post?.replies?.length === 0; diff --git a/src/views/rules/rules.tsx b/src/views/rules/rules.tsx index f6c69f5c..1736b5bf 100644 --- a/src/views/rules/rules.tsx +++ b/src/views/rules/rules.tsx @@ -1,7 +1,7 @@ import { useEffect, useState, FormEvent } from 'react'; import { useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { Footer, HomeLogo } from '../home'; -import { useDefaultSubplebbits, MultisubSubplebbit } from '../../hooks/use-default-subplebbits'; +import { useDirectories, DirectoryCommunity } from '../../hooks/use-directories'; import styles from './rules.module.css'; const getBoardShortCode = (title?: string): string => { @@ -16,7 +16,7 @@ const getBoardName = (title?: string): string => { return match ? match[1] : title; }; -const BoardRulesDisplay = ({ subplebbitAddress, defaultSubplebbits }: { subplebbitAddress: string; defaultSubplebbits: MultisubSubplebbit[] }) => { +const BoardRulesDisplay = ({ subplebbitAddress, directories }: { subplebbitAddress: string; directories: DirectoryCommunity[] }) => { const subplebbit = useSubplebbit({ subplebbitAddress }); const { rules, state, title, shortAddress } = subplebbit || {}; @@ -42,7 +42,7 @@ const BoardRulesDisplay = ({ subplebbitAddress, defaultSubplebbits }: { subplebb const isLoaded = state === 'succeeded'; - const defaultSub = defaultSubplebbits.find((sub) => sub.address === subplebbitAddress); + const defaultSub = directories.find((sub) => sub.address === subplebbitAddress); let displayTitle: string; if (defaultSub?.title) { const shortCode = getBoardShortCode(defaultSub.title); @@ -87,11 +87,11 @@ const BoardRulesDisplay = ({ subplebbitAddress, defaultSubplebbits }: { subplebb }; const BoardSelector = ({ - defaultSubplebbits, + directories, selectedAddress, onSelect, }: { - defaultSubplebbits: MultisubSubplebbit[]; + directories: DirectoryCommunity[]; selectedAddress: string; onSelect: (address: string) => void; }) => { @@ -121,7 +121,7 @@ const BoardSelector = ({