@bitsocial/bitsocial-react-hooks 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +1365 -0
  3. package/dist/hooks/accounts/accounts.d.ts +64 -0
  4. package/dist/hooks/accounts/accounts.d.ts.map +1 -0
  5. package/dist/hooks/accounts/accounts.js +706 -0
  6. package/dist/hooks/accounts/accounts.js.map +1 -0
  7. package/dist/hooks/accounts/index.d.ts +2 -0
  8. package/dist/hooks/accounts/index.d.ts.map +1 -0
  9. package/dist/hooks/accounts/index.js +2 -0
  10. package/dist/hooks/accounts/index.js.map +1 -0
  11. package/dist/hooks/accounts/utils.d.ts +6 -0
  12. package/dist/hooks/accounts/utils.d.ts.map +1 -0
  13. package/dist/hooks/accounts/utils.js +226 -0
  14. package/dist/hooks/accounts/utils.js.map +1 -0
  15. package/dist/hooks/actions/actions.d.ts +19 -0
  16. package/dist/hooks/actions/actions.d.ts.map +1 -0
  17. package/dist/hooks/actions/actions.js +552 -0
  18. package/dist/hooks/actions/actions.js.map +1 -0
  19. package/dist/hooks/actions/index.d.ts +2 -0
  20. package/dist/hooks/actions/index.d.ts.map +1 -0
  21. package/dist/hooks/actions/index.js +2 -0
  22. package/dist/hooks/actions/index.js.map +1 -0
  23. package/dist/hooks/authors/author-avatars.d.ts +28 -0
  24. package/dist/hooks/authors/author-avatars.d.ts.map +1 -0
  25. package/dist/hooks/authors/author-avatars.js +191 -0
  26. package/dist/hooks/authors/author-avatars.js.map +1 -0
  27. package/dist/hooks/authors/authors.d.ts +37 -0
  28. package/dist/hooks/authors/authors.d.ts.map +1 -0
  29. package/dist/hooks/authors/authors.js +509 -0
  30. package/dist/hooks/authors/authors.js.map +1 -0
  31. package/dist/hooks/authors/index.d.ts +2 -0
  32. package/dist/hooks/authors/index.d.ts.map +1 -0
  33. package/dist/hooks/authors/index.js +2 -0
  34. package/dist/hooks/authors/index.js.map +1 -0
  35. package/dist/hooks/authors/utils.d.ts +4 -0
  36. package/dist/hooks/authors/utils.d.ts.map +1 -0
  37. package/dist/hooks/authors/utils.js +21 -0
  38. package/dist/hooks/authors/utils.js.map +1 -0
  39. package/dist/hooks/comments.d.ts +17 -0
  40. package/dist/hooks/comments.d.ts.map +1 -0
  41. package/dist/hooks/comments.js +351 -0
  42. package/dist/hooks/comments.js.map +1 -0
  43. package/dist/hooks/communities.d.ts +31 -0
  44. package/dist/hooks/communities.d.ts.map +1 -0
  45. package/dist/hooks/communities.js +389 -0
  46. package/dist/hooks/communities.js.map +1 -0
  47. package/dist/hooks/feeds/feeds.d.ts +18 -0
  48. package/dist/hooks/feeds/feeds.d.ts.map +1 -0
  49. package/dist/hooks/feeds/feeds.js +315 -0
  50. package/dist/hooks/feeds/feeds.js.map +1 -0
  51. package/dist/hooks/feeds/index.d.ts +2 -0
  52. package/dist/hooks/feeds/index.d.ts.map +1 -0
  53. package/dist/hooks/feeds/index.js +2 -0
  54. package/dist/hooks/feeds/index.js.map +1 -0
  55. package/dist/hooks/pkc-rpc.d.ts +7 -0
  56. package/dist/hooks/pkc-rpc.d.ts.map +1 -0
  57. package/dist/hooks/pkc-rpc.js +88 -0
  58. package/dist/hooks/pkc-rpc.js.map +1 -0
  59. package/dist/hooks/replies.d.ts +5 -0
  60. package/dist/hooks/replies.d.ts.map +1 -0
  61. package/dist/hooks/replies.js +155 -0
  62. package/dist/hooks/replies.js.map +1 -0
  63. package/dist/hooks/states.d.ts +15 -0
  64. package/dist/hooks/states.d.ts.map +1 -0
  65. package/dist/hooks/states.js +213 -0
  66. package/dist/hooks/states.js.map +1 -0
  67. package/dist/hooks/utils/use-interval.d.ts +3 -0
  68. package/dist/hooks/utils/use-interval.d.ts.map +1 -0
  69. package/dist/hooks/utils/use-interval.js +36 -0
  70. package/dist/hooks/utils/use-interval.js.map +1 -0
  71. package/dist/hooks/utils/use-previous.d.ts +1 -0
  72. package/dist/hooks/utils/use-previous.js +10 -0
  73. package/dist/index.d.ts +82 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +128 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/lib/chain/chain.d.ts +36 -0
  78. package/dist/lib/chain/chain.d.ts.map +1 -0
  79. package/dist/lib/chain/chain.js +195 -0
  80. package/dist/lib/chain/chain.js.map +1 -0
  81. package/dist/lib/chain/index.d.ts +4 -0
  82. package/dist/lib/chain/index.d.ts.map +1 -0
  83. package/dist/lib/chain/index.js +4 -0
  84. package/dist/lib/chain/index.js.map +1 -0
  85. package/dist/lib/community-address.d.ts +6 -0
  86. package/dist/lib/community-address.d.ts.map +1 -0
  87. package/dist/lib/community-address.js +26 -0
  88. package/dist/lib/community-address.js.map +1 -0
  89. package/dist/lib/community-ref.d.ts +23 -0
  90. package/dist/lib/community-ref.d.ts.map +1 -0
  91. package/dist/lib/community-ref.js +113 -0
  92. package/dist/lib/community-ref.js.map +1 -0
  93. package/dist/lib/debug-utils.d.ts +9 -0
  94. package/dist/lib/debug-utils.d.ts.map +1 -0
  95. package/dist/lib/debug-utils.js +21 -0
  96. package/dist/lib/debug-utils.js.map +1 -0
  97. package/dist/lib/feed-sort-type.d.ts +2 -0
  98. package/dist/lib/feed-sort-type.d.ts.map +1 -0
  99. package/dist/lib/feed-sort-type.js +22 -0
  100. package/dist/lib/feed-sort-type.js.map +1 -0
  101. package/dist/lib/localforage-lru/index.d.ts +3 -0
  102. package/dist/lib/localforage-lru/index.d.ts.map +1 -0
  103. package/dist/lib/localforage-lru/index.js +46 -0
  104. package/dist/lib/localforage-lru/index.js.map +1 -0
  105. package/dist/lib/localforage-lru/localforage-lru.d.ts +6 -0
  106. package/dist/lib/localforage-lru/localforage-lru.d.ts.map +1 -0
  107. package/dist/lib/localforage-lru/localforage-lru.js +182 -0
  108. package/dist/lib/localforage-lru/localforage-lru.js.map +1 -0
  109. package/dist/lib/pkc-compat.d.ts +25 -0
  110. package/dist/lib/pkc-compat.d.ts.map +1 -0
  111. package/dist/lib/pkc-compat.js +131 -0
  112. package/dist/lib/pkc-compat.js.map +1 -0
  113. package/dist/lib/pkc-js/fixtures/markdown-example.d.ts +3 -0
  114. package/dist/lib/pkc-js/fixtures/markdown-example.d.ts.map +1 -0
  115. package/dist/lib/pkc-js/fixtures/markdown-example.js +280 -0
  116. package/dist/lib/pkc-js/fixtures/markdown-example.js.map +1 -0
  117. package/dist/lib/pkc-js/index.d.ts +11 -0
  118. package/dist/lib/pkc-js/index.d.ts.map +1 -0
  119. package/dist/lib/pkc-js/index.js +85 -0
  120. package/dist/lib/pkc-js/index.js.map +1 -0
  121. package/dist/lib/pkc-js/pkc-js-mock-content.d.ts +3 -0
  122. package/dist/lib/pkc-js/pkc-js-mock-content.d.ts.map +1 -0
  123. package/dist/lib/pkc-js/pkc-js-mock-content.js +1235 -0
  124. package/dist/lib/pkc-js/pkc-js-mock-content.js.map +1 -0
  125. package/dist/lib/pkc-js/pkc-js-mock.d.ts +137 -0
  126. package/dist/lib/pkc-js/pkc-js-mock.d.ts.map +1 -0
  127. package/dist/lib/pkc-js/pkc-js-mock.js +644 -0
  128. package/dist/lib/pkc-js/pkc-js-mock.js.map +1 -0
  129. package/dist/lib/polyfill.d.ts +3 -0
  130. package/dist/lib/polyfill.d.ts.map +1 -0
  131. package/dist/lib/polyfill.js +14 -0
  132. package/dist/lib/polyfill.js.map +1 -0
  133. package/dist/lib/protocol-compat.d.ts +14 -0
  134. package/dist/lib/protocol-compat.d.ts.map +1 -0
  135. package/dist/lib/protocol-compat.js +67 -0
  136. package/dist/lib/protocol-compat.js.map +1 -0
  137. package/dist/lib/test-utils.d.ts +29 -0
  138. package/dist/lib/test-utils.d.ts.map +1 -0
  139. package/dist/lib/test-utils.js +184 -0
  140. package/dist/lib/test-utils.js.map +1 -0
  141. package/dist/lib/utils/comment-moderation.d.ts +4 -0
  142. package/dist/lib/utils/comment-moderation.d.ts.map +1 -0
  143. package/dist/lib/utils/comment-moderation.js +56 -0
  144. package/dist/lib/utils/comment-moderation.js.map +1 -0
  145. package/dist/lib/utils/index.d.ts +4 -0
  146. package/dist/lib/utils/index.d.ts.map +1 -0
  147. package/dist/lib/utils/index.js +4 -0
  148. package/dist/lib/utils/index.js.map +1 -0
  149. package/dist/lib/utils/utils.d.ts +23 -0
  150. package/dist/lib/utils/utils.d.ts.map +1 -0
  151. package/dist/lib/utils/utils.js +375 -0
  152. package/dist/lib/utils/utils.js.map +1 -0
  153. package/dist/lib/validator.d.ts +30 -0
  154. package/dist/lib/validator.d.ts.map +1 -0
  155. package/dist/lib/validator.js +307 -0
  156. package/dist/lib/validator.js.map +1 -0
  157. package/dist/stores/accounts/account-generator.d.ts +51 -0
  158. package/dist/stores/accounts/account-generator.d.ts.map +1 -0
  159. package/dist/stores/accounts/account-generator.js +160 -0
  160. package/dist/stores/accounts/account-generator.js.map +1 -0
  161. package/dist/stores/accounts/accounts-actions-internal.d.ts +8 -0
  162. package/dist/stores/accounts/accounts-actions-internal.d.ts.map +1 -0
  163. package/dist/stores/accounts/accounts-actions-internal.js +403 -0
  164. package/dist/stores/accounts/accounts-actions-internal.js.map +1 -0
  165. package/dist/stores/accounts/accounts-actions.d.ts +46 -0
  166. package/dist/stores/accounts/accounts-actions.d.ts.map +1 -0
  167. package/dist/stores/accounts/accounts-actions.js +1341 -0
  168. package/dist/stores/accounts/accounts-actions.js.map +1 -0
  169. package/dist/stores/accounts/accounts-database.d.ts +34 -0
  170. package/dist/stores/accounts/accounts-database.d.ts.map +1 -0
  171. package/dist/stores/accounts/accounts-database.js +685 -0
  172. package/dist/stores/accounts/accounts-database.js.map +1 -0
  173. package/dist/stores/accounts/accounts-store.d.ts +32 -0
  174. package/dist/stores/accounts/accounts-store.d.ts.map +1 -0
  175. package/dist/stores/accounts/accounts-store.js +169 -0
  176. package/dist/stores/accounts/accounts-store.js.map +1 -0
  177. package/dist/stores/accounts/index.d.ts +4 -0
  178. package/dist/stores/accounts/index.d.ts.map +1 -0
  179. package/dist/stores/accounts/index.js +4 -0
  180. package/dist/stores/accounts/index.js.map +1 -0
  181. package/dist/stores/accounts/utils.d.ts +49 -0
  182. package/dist/stores/accounts/utils.d.ts.map +1 -0
  183. package/dist/stores/accounts/utils.js +419 -0
  184. package/dist/stores/accounts/utils.js.map +1 -0
  185. package/dist/stores/authors-comments/authors-comments-store.d.ts +37 -0
  186. package/dist/stores/authors-comments/authors-comments-store.d.ts.map +1 -0
  187. package/dist/stores/authors-comments/authors-comments-store.js +338 -0
  188. package/dist/stores/authors-comments/authors-comments-store.js.map +1 -0
  189. package/dist/stores/authors-comments/index.d.ts +4 -0
  190. package/dist/stores/authors-comments/index.d.ts.map +1 -0
  191. package/dist/stores/authors-comments/index.js +4 -0
  192. package/dist/stores/authors-comments/index.js.map +1 -0
  193. package/dist/stores/authors-comments/utils.d.ts +14 -0
  194. package/dist/stores/authors-comments/utils.d.ts.map +1 -0
  195. package/dist/stores/authors-comments/utils.js +81 -0
  196. package/dist/stores/authors-comments/utils.js.map +1 -0
  197. package/dist/stores/comments/comments-store.d.ts +19 -0
  198. package/dist/stores/comments/comments-store.d.ts.map +1 -0
  199. package/dist/stores/comments/comments-store.js +385 -0
  200. package/dist/stores/comments/comments-store.js.map +1 -0
  201. package/dist/stores/comments/index.d.ts +4 -0
  202. package/dist/stores/comments/index.d.ts.map +1 -0
  203. package/dist/stores/comments/index.js +4 -0
  204. package/dist/stores/comments/index.js.map +1 -0
  205. package/dist/stores/communities/communities-store.d.ts +17 -0
  206. package/dist/stores/communities/communities-store.d.ts.map +1 -0
  207. package/dist/stores/communities/communities-store.js +304 -0
  208. package/dist/stores/communities/communities-store.js.map +1 -0
  209. package/dist/stores/communities/index.d.ts +4 -0
  210. package/dist/stores/communities/index.d.ts.map +1 -0
  211. package/dist/stores/communities/index.js +4 -0
  212. package/dist/stores/communities/index.js.map +1 -0
  213. package/dist/stores/communities-pages/communities-pages-store.d.ts +23 -0
  214. package/dist/stores/communities-pages/communities-pages-store.d.ts.map +1 -0
  215. package/dist/stores/communities-pages/communities-pages-store.js +316 -0
  216. package/dist/stores/communities-pages/communities-pages-store.js.map +1 -0
  217. package/dist/stores/communities-pages/index.d.ts +4 -0
  218. package/dist/stores/communities-pages/index.d.ts.map +1 -0
  219. package/dist/stores/communities-pages/index.js +4 -0
  220. package/dist/stores/communities-pages/index.js.map +1 -0
  221. package/dist/stores/feeds/feed-sorter.d.ts +5 -0
  222. package/dist/stores/feeds/feed-sorter.d.ts.map +1 -0
  223. package/dist/stores/feeds/feed-sorter.js +135 -0
  224. package/dist/stores/feeds/feed-sorter.js.map +1 -0
  225. package/dist/stores/feeds/feeds-store.d.ts +25 -0
  226. package/dist/stores/feeds/feeds-store.d.ts.map +1 -0
  227. package/dist/stores/feeds/feeds-store.js +459 -0
  228. package/dist/stores/feeds/feeds-store.js.map +1 -0
  229. package/dist/stores/feeds/index.d.ts +4 -0
  230. package/dist/stores/feeds/index.d.ts.map +1 -0
  231. package/dist/stores/feeds/index.js +4 -0
  232. package/dist/stores/feeds/index.js.map +1 -0
  233. package/dist/stores/feeds/utils.d.ts +43 -0
  234. package/dist/stores/feeds/utils.d.ts.map +1 -0
  235. package/dist/stores/feeds/utils.js +736 -0
  236. package/dist/stores/feeds/utils.js.map +1 -0
  237. package/dist/stores/replies/index.d.ts +4 -0
  238. package/dist/stores/replies/index.d.ts.map +1 -0
  239. package/dist/stores/replies/index.js +4 -0
  240. package/dist/stores/replies/index.js.map +1 -0
  241. package/dist/stores/replies/replies-comments-store.d.ts +8 -0
  242. package/dist/stores/replies/replies-comments-store.d.ts.map +1 -0
  243. package/dist/stores/replies/replies-comments-store.js +23 -0
  244. package/dist/stores/replies/replies-comments-store.js.map +1 -0
  245. package/dist/stores/replies/replies-store.d.ts +29 -0
  246. package/dist/stores/replies/replies-store.d.ts.map +1 -0
  247. package/dist/stores/replies/replies-store.js +413 -0
  248. package/dist/stores/replies/replies-store.js.map +1 -0
  249. package/dist/stores/replies/utils.d.ts +25 -0
  250. package/dist/stores/replies/utils.d.ts.map +1 -0
  251. package/dist/stores/replies/utils.js +549 -0
  252. package/dist/stores/replies/utils.js.map +1 -0
  253. package/dist/stores/replies-pages/index.d.ts +4 -0
  254. package/dist/stores/replies-pages/index.d.ts.map +1 -0
  255. package/dist/stores/replies-pages/index.js +4 -0
  256. package/dist/stores/replies-pages/index.js.map +1 -0
  257. package/dist/stores/replies-pages/replies-pages-store.d.ts +20 -0
  258. package/dist/stores/replies-pages/replies-pages-store.d.ts.map +1 -0
  259. package/dist/stores/replies-pages/replies-pages-store.js +270 -0
  260. package/dist/stores/replies-pages/replies-pages-store.js.map +1 -0
  261. package/dist/stores/replies-pages/utils.d.ts +3 -0
  262. package/dist/stores/replies-pages/utils.d.ts.map +1 -0
  263. package/dist/stores/replies-pages/utils.js +43 -0
  264. package/dist/stores/replies-pages/utils.js.map +1 -0
  265. package/dist/types.d.ts +638 -0
  266. package/dist/types.d.ts.map +1 -0
  267. package/dist/types.js +3 -0
  268. package/dist/types.js.map +1 -0
  269. package/package.json +160 -0
package/README.md ADDED
@@ -0,0 +1,1365 @@
1
+ [![CI](https://github.com/bitsocialnet/bitsocial-react-hooks/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/bitsocialnet/bitsocial-react-hooks/actions/workflows/CI.yml)
2
+ [![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/bitsocialnet/bitsocial-react-hooks/master/badges/coverage.json)](https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/scripts/write-coverage-badge.mjs)
3
+ [![License](https://img.shields.io/badge/license-GPL--3.0--or--later-red.svg)](https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/LICENSE)
4
+ [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
5
+
6
+ <p align="left">
7
+ <img src="./docs/assets/readme/react-hooks-banner.jpg" alt="React Hooks banner" width="340" />
8
+ </p>
9
+
10
+ # Bitsocial React Hooks
11
+
12
+ React hooks for the Bitsocial protocol. Build decentralized, serverless social apps with React using a familiar hooks API — fetch feeds, comments, author profiles, manage accounts, publish content, and more, all without a central server.
13
+
14
+ This package is published as [`@bitsocial/bitsocial-react-hooks`](https://www.npmjs.com/package/@bitsocial/bitsocial-react-hooks) and is used by [5chan](https://github.com/bitsocialnet/5chan) and other Bitsocial clients.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ yarn add @bitsocial/bitsocial-react-hooks
20
+ ```
21
+
22
+ The published build is self-contained ESM, so consumers should not need postinstall import-rewrite patches.
23
+
24
+ ## Development Setup
25
+
26
+ ```bash
27
+ nvm install
28
+ nvm use
29
+ corepack enable
30
+ yarn install
31
+ ```
32
+
33
+ Run `corepack enable` once per machine so plain `yarn` resolves to the pinned Yarn 4 release.
34
+
35
+ ---
36
+
37
+ ## Table of Contents
38
+
39
+ - [Installation](#installation)
40
+ - [Documentation Links](#documentation-links)
41
+ - [API Reference](#api-reference)
42
+ - [Hooks](#hooks)
43
+ - [Accounts Hooks](#accounts-hooks)
44
+ - [Comments Hooks](#comments-hooks)
45
+ - [Communities Hooks](#communities-hooks)
46
+ - [Authors Hooks](#authors-hooks)
47
+ - [Feeds Hooks](#feeds-hooks)
48
+ - [Actions Hooks](#actions-hooks)
49
+ - [States Hooks](#states-hooks)
50
+ - [RPC Hooks](#rpc-hooks)
51
+ - [Actions with no hooks implementations yet](#actions-with-no-hooks-implementations-yet)
52
+ - [Utility functions](#utility-functions)
53
+ - [Recipes](#recipes)
54
+ - [Getting started](#getting-started)
55
+ - [Get the active account, if none exist in browser database, a default account is generated](#get-the-active-account-if-none-exist-in-browser-database-a-default-account-is-generated)
56
+ - [Create accounts and change active account](#create-accounts-and-change-active-account)
57
+ - [Get a post](#get-a-post)
58
+ - [Get a comment](#get-a-comment)
59
+ - [Get author avatar](#get-author-avatar)
60
+ - [Get author profile page](#get-author-profile-page)
61
+ - [Get a community](#get-a-community)
62
+ - [Create a post or comment using callbacks](#create-a-post-or-comment-using-callbacks)
63
+ - [Create a post or comment using hooks](#create-a-post-or-comment-using-hooks)
64
+ - [Create a post or comment anonymously (without account.signer or account.author)](#create-a-post-or-comment-anonymously-without-accountsigner-or-accountauthor)
65
+ - [Create a vote](#create-a-vote)
66
+ - [Create a comment edit](#create-a-comment-edit)
67
+ - [Create a comment moderation](#create-a-comment-moderation)
68
+ - [Delete a comment](#delete-a-comment)
69
+ - [Subscribe to a community](#subscribe-to-a-community)
70
+ - [Get feed](#get-feed)
71
+ - [Get mod queue (pending approval)](#get-mod-queue-pending-approval)
72
+ - [Approve a pending approval comment](#approve-a-pending-approval-comment)
73
+ - [Edit an account](#edit-an-account)
74
+ - [Delete account](#delete-account)
75
+ - [Get your own comments and votes](#get-your-own-comments-and-votes)
76
+ - [Determine if a comment is your own](#determine-if-a-comment-is-your-own)
77
+ - [Get account notifications](#get-account-notifications)
78
+ - [Block an address (author, community or multisub)](#block-an-address-author-community-or-multisub)
79
+ - [Block a cid (hide a comment)](#block-a-cid-hide-a-comment)
80
+ - [(Desktop only) Create a community](#desktop-only-create-a-community)
81
+ - [(Desktop only) List the communities you created](#desktop-only-list-the-communities-you-created)
82
+ - [(Desktop only) Edit your community settings](#desktop-only-edit-your-community-settings)
83
+ - [Export and import account](#export-and-import-account)
84
+ - [View the status of a comment edit](#view-the-status-of-a-comment-edit)
85
+ - [View the status of a specific comment edit property](#view-the-status-of-a-specific-comment-edit-property)
86
+ - [List all comment and community edits the account has performed](#list-all-comment-and-community-edits-the-account-has-performed)
87
+ - [Get replies to a post (nested or flat)](#get-replies-to-a-post-nested-or-flat)
88
+ - [Format short CIDs and addresses](#format-short-cids-and-addresses)
89
+ - [useBufferedFeeds with concurrency](#usebufferedfeeds-with-concurrency)
90
+
91
+ ## Documentation Links
92
+
93
+ - [Hooks API](#hooks)
94
+ - [Getting started](#getting-started)
95
+ - Install, testing and building: https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/docs/testing.md
96
+ - Mock content (for UI development): https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/docs/mock-content.md
97
+ - Algorithms: https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/docs/algorithms.md
98
+ - Schema (Types, IndexedDb and state management): https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/docs/schema.md
99
+ - Types: https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/src/types.ts
100
+
101
+ ## API Reference
102
+
103
+ ### Hooks
104
+
105
+ #### Accounts Hooks
106
+
107
+ ```
108
+ useAccount(): Account | undefined
109
+ useAccountComment({commentIndex?: number, commentCid?: string}): Comment // get one own comment by index or cid
110
+ useAccountComments({filter?: AccountPublicationsFilter, commentCid?: string, commentIndices?: number[], communityAddress?: string, parentCid?: string, newerThan?: number, page?: number, pageSize?: number, sortType?: "new" | "old"}): {accountComments: Comment[]} // export or display list of own comments
111
+ useAccountVotes({filter?: AccountPublicationsFilter, vote?: number, commentCid?: string, communityAddress?: string, newerThan?: number, page?: number, pageSize?: number, sortType?: "new" | "old"}): {accountVotes: Vote[]} // export or display list of own votes
112
+ useAccountVote({commentCid: string}): Vote // know if you already voted on some comment
113
+ useAccountEdits({filer: AccountPublicationsFilter}): {accountEdits: AccountEdit[]}
114
+ useAccountCommunities(): {accountCommunities: {[communityAddress: string]: AccountCommunity}, onlyIfCached?: boolean}
115
+ useAccounts(): Account[]
116
+ useNotifications(): {notifications: Notification[], markAsRead: Function}
117
+ ```
118
+
119
+ #### Comments Hooks
120
+
121
+ ```
122
+ useComment({commentCid: string, onlyIfCached?: boolean, autoUpdate?: boolean}): Comment & {refresh: Function}
123
+ useReplies({comment: Comment, onlyIfCached?: boolean, sortType?: string, flat?: boolean, repliesPerPage?: number, filter?: CommentsFilter, accountComments?: {newerThan: number, append?: boolean}}): {replies: Comment[], hasMore: boolean, loadMore: function, reset: function, updatedReplies: Comment[], bufferedReplies: Comment[]}
124
+ useComments({commentCids: string[], onlyIfCached?: boolean, autoUpdate?: boolean}): {comments: Comment[], refresh: Function}
125
+ useEditedComment({comment: Comment}): {editedComment: Comment | undefined}
126
+ useValidateComment({comment: Comment, validateReplies?: boolean}): {valid: boolean}
127
+ ```
128
+
129
+ #### Communities Hooks
130
+
131
+ ```
132
+ useCommunity({community: {name?: string, publicKey?: string}, onlyIfCached?: boolean}): Community
133
+ useCommunities({communities?: CommunityIdentifier[], onlyIfCached?: boolean}): {communities: Communities[]}
134
+ useCommunityStats({community: {name?: string, publicKey?: string}, onlyIfCached?: boolean}): CommunityStats
135
+ useResolvedCommunityAddress({communityAddress: string, cache: boolean}): {resolvedAddress: string | undefined} // use {cache: false} when checking the user's own community address
136
+ ```
137
+
138
+ Pass `{ publicKey, name }` when you have both so `pkc-js` can fetch through the public key and resolve the name in the background. `communityAddress`, `communityAddresses`, and `communityRefs` are no longer accepted by these hooks.
139
+
140
+ #### Authors Hooks
141
+
142
+ ```
143
+ useAuthor({authorAddress: string, commentCid: string}): {author: Author | undefined}
144
+ useAuthorAddress({comment: Comment}): {authorAddress: string | undefined, shortAuthorAddress: string | undefined, authorAddressChanged: boolean}
145
+ useAuthorComments({authorAddress: string, commentCid: string, filter?: CommentsFilter}): {authorComments: Comment[], hasMore: boolean, loadMore: Promise<void>}
146
+ useResolvedAuthorAddress({author?: Author, cache?: boolean}): {resolvedAddress: string | undefined, nameResolver: NameResolverInfo | undefined} // supports .eth/.bso aliases; use {cache: false} when checking the user's own author address
147
+ useAuthorAvatar({author?: Author}): {imageUrl: string | undefined}
148
+ setAuthorAvatarsWhitelistedTokenAddresses(tokenAddresses: string[])
149
+ ```
150
+
151
+ #### Feeds Hooks
152
+
153
+ ```
154
+ useFeed({communities?: CommunityIdentifier[], sortType?: string, postsPerPage?: number, filter?: CommentsFilter, newerThan?: number, accountComments?: {newerThan: number, append?: boolean}, modQueue: ['pendingApproval']}): {feed: Comment[], loadMore: function, expandTimeWindow: function, hasMore: boolean, reset: function, updatedFeed: Comment[], bufferedFeed: Comment[], communityKeysWithNewerPosts: string[]}
155
+ useBufferedFeeds({feedsOptions: UseFeedOptions[]}) // preload or buffer feeds in the background, so they load faster when you call `useFeed`
156
+ ```
157
+
158
+ `useFeed().reset()` clears the current feed and refreshes the latest community snapshots before rebuilding it.
159
+ `useFeed().expandTimeWindow(newerThan)` broadens `newerThan` in place for feeds whose derived sort type stays the same, so older posts can be appended without replacing the feed instance.
160
+
161
+ #### Actions Hooks
162
+
163
+ ```
164
+ useSubscribe({communityAddress: string}): {subscribed: boolean | undefined, subscribe: Function, unsubscribe: Function}
165
+ useBlock({address?: string, cid?: string}): {blocked: boolean | undefined, block: Function, unblock: Function}
166
+ usePublishComment(options: UsePublishCommentOptions): {index: number, abandonPublish: () => Promise<void>, ...UsePublishCommentResult}
167
+ usePublishVote(options: UsePublishVoteOptions): UsePublishVoteResult
168
+ usePublishCommentEdit(options: UsePublishCommentEditOptions): UsePublishCommentEditResult
169
+ usePublishCommentModeration(options: UsePublishCommentModerationOptions): UsePublishCommentModerationResult
170
+ usePublishCommunityEdit(options: UsePublishCommunityEditOptions): UsePublishCommunityEditResult
171
+ useCreateCommunity(options: CreateCommunityOptions): {createdCommunity: Community | undefined, createCommunity: Function}
172
+ ```
173
+
174
+ #### States Hooks
175
+
176
+ ```
177
+ useClientsStates({comment?: Comment, community?: Community}): {states, peers}
178
+ useCommunitiesStates({communities?: CommunityIdentifier[]}): {states, peers}
179
+ ```
180
+
181
+ #### RPC Hooks
182
+
183
+ ```
184
+ usePkcRpcSettings(): {pkcRpcSettings: {pkcOptions, challenges}, setPkcRpcSettings: Function}
185
+ ```
186
+
187
+ #### Actions with no hooks implementations yet
188
+
189
+ ```
190
+ createAccount(account: Account)
191
+ deleteAccount(accountName: string)
192
+ setAccount(account: Account)
193
+ setActiveAccount(accountName: string)
194
+ setAccountsOrder(accountNames: string[])
195
+ importAccount(serializedAccount: string)
196
+ exportAccount(accountName: string): string // don't allow undefined to prevent catastrophic bugs
197
+ deleteCommunity(communityAddress: string, accountName?: string)
198
+ deleteComment(commentCidOrAccountCommentIndex: string | number, accountName?: string): Promise<void>
199
+ ```
200
+
201
+ #### Utility functions
202
+
203
+ ```
204
+ setPkcJs(PKC) // swap the underlying protocol client implementation, e.g. for mocks or Electron
205
+ deleteDatabases() // delete all databases, including all caches and accounts data
206
+ deleteCaches() // delete the cached comments, cached communities and cached pages only, no accounts data
207
+ ```
208
+
209
+ ## Recipes
210
+
211
+ #### Getting started
212
+
213
+ ```jsx
214
+ import { useComment, useAccount } from "@bitsocial/bitsocial-react-hooks";
215
+
216
+ const account = useAccount();
217
+ const comment = useComment({ commentCid });
218
+ ```
219
+
220
+ #### Get the active account, if none exist in browser database, a default account is generated
221
+
222
+ ```jsx
223
+ const account = useAccount();
224
+ ```
225
+
226
+ #### Create accounts and change active account
227
+
228
+ ```jsx
229
+ import {
230
+ useAccount,
231
+ useAccounts,
232
+ createAccount,
233
+ setActiveAccount,
234
+ } from "@bitsocial/bitsocial-react-hooks";
235
+
236
+ const account = useAccount();
237
+ const { accounts } = useAccounts();
238
+
239
+ // on first render
240
+ console.log(accounts.length); // 1
241
+ console.log(account.name); // 'Account 1'
242
+
243
+ await createAccount(); // create 'Account 2'
244
+ await createAccount(); // create 'Account 3'
245
+ await setActiveAccount("Account 3");
246
+
247
+ // on render after updates
248
+ console.log(accounts.length); // 3
249
+ console.log(account.name); // 'Account 3'
250
+
251
+ // you are now publishing from 'Account 3' because it is the active one
252
+ const { publishComment } = usePublishComment(publishCommentOptions);
253
+ await publishComment();
254
+ ```
255
+
256
+ #### Get a post
257
+
258
+ ```jsx
259
+ const post = useComment({ commentCid });
260
+
261
+ // manual refresh is always available
262
+ await post.refresh();
263
+
264
+ // post.author.address should not be used directly, it needs to be verified asynchronously using useAuthorAddress
265
+ const { authorAddress, shortAuthorAddress } = useAuthorAddress({ comment: post });
266
+ // exception: when linking to an author profile page, /u/${comment.author.address}/c/${comment.cid} should be used, not useAuthorAddress({comment}).authorAddress
267
+
268
+ // use many times in a page without affecting performance
269
+ const post = useComment({ commentCid, onlyIfCached: true });
270
+
271
+ // disable background polling and refresh on demand
272
+ const post = useComment({ commentCid, autoUpdate: false });
273
+ await post.refresh();
274
+
275
+ // post.replies are not validated, to show replies
276
+ const { replies, hasMore, loadMore } = useReplies({ comment: post });
277
+
278
+ // only use the comment's preloaded replies plus any reply pages already cached in memory
279
+ // won't fetch missing reply pages; hasMore only reflects cached replies still available to load
280
+ const cachedReplies = useReplies({ comment: post, onlyIfCached: true });
281
+
282
+ // to show a preloaded reply without rerenders, validate manually
283
+ const { valid } = useValidateComment({ comment: post.replies.pages.best.comments[0] });
284
+ if (valid === false) {
285
+ // don't show this reply, it's malicious
286
+ }
287
+ // won't cause any rerenders if true
288
+ ```
289
+
290
+ #### Get a comment
291
+
292
+ ```jsx
293
+ const comment = useComment({ commentCid });
294
+ const { comments, refresh } = useComments({ commentCids: [commentCid1, commentCid2, commentCid3] });
295
+ await refresh();
296
+
297
+ // content
298
+ console.log(comment.content || comment.link || comment.title);
299
+
300
+ // comment.author.address should not be used directly, it needs to be verified asynchronously using useAuthorAddress
301
+ const { authorAddress, shortAuthorAddress } = useAuthorAddress({ comment });
302
+ // exception: when linking to an author profile page, /u/${comment.author.address}/c/${comment.cid} should be used, not useAuthorAddress({comment}).authorAddress
303
+
304
+ // use without affecting performance
305
+ const { comments } = useComments({ commentCids, onlyIfCached: true });
306
+
307
+ // disable background polling and refresh this list on demand
308
+ const frozenComments = useComments({ commentCids, autoUpdate: false });
309
+ await frozenComments.refresh();
310
+ ```
311
+
312
+ #### Get author avatar
313
+
314
+ ```jsx
315
+ const comment = useComment({ commentCid });
316
+
317
+ // get the nft avatar image url of the comment author
318
+ const { imageUrl, state, error, chainProvider, metadataUrl } = useAuthorAvatar({
319
+ author: comment.author,
320
+ });
321
+
322
+ // result
323
+ if (state === "succeeded") {
324
+ console.log("Succeeded getting avatar image URL", imageUrl);
325
+ }
326
+ if (state === "failed") {
327
+ console.log("Failed getting avatar image URL", error.message);
328
+ }
329
+
330
+ // pending
331
+ if (state === "fetching-owner") {
332
+ console.log("Fetching NFT owner address from chain provider", chainProvider.urls);
333
+ }
334
+ if (state === "fetching-uri") {
335
+ console.log("Fetching NFT URI from chain provider URL", chainProvider.urls);
336
+ }
337
+ if (state === "fetching-metadata") {
338
+ console.log("Fetching NFT URI from", metadataUrl);
339
+ }
340
+ ```
341
+
342
+ #### Get author profile page
343
+
344
+ ```jsx
345
+ // NOTE: you must have a comment cid from the author to load his profile page
346
+ // e.g. the page url would be /#/u/<authorAddress>/c/<commentCid>
347
+ const authorResult = useAuthor({ commentCid, authorAddress });
348
+ const { imageUrl } = useAuthorAvatar({ author: authorResult.author });
349
+ const { authorComments, lastCommentCid, hasMore, loadMore } = useAuthorComments({
350
+ commentCid,
351
+ authorAddress,
352
+ });
353
+
354
+ // result
355
+ if (authorResult.state === "succeeded") {
356
+ console.log("Succeeded getting author", authorResult.author);
357
+ }
358
+ if (state === "failed") {
359
+ console.log("Failed getting author", authorResult.error.message);
360
+ }
361
+
362
+ // listing the author comments with infinite scroll
363
+ import { Virtuoso } from "react-virtuoso";
364
+
365
+ <Virtuoso
366
+ data={authorComments}
367
+ itemContent={(index, comment) => <Comment index={index} comment={comment} />}
368
+ useWindowScroll={true}
369
+ components={{ Footer: hasMore ? () => <Loading /> : undefined }}
370
+ endReached={loadMore}
371
+ increaseViewportBy={{ bottom: 600, top: 600 }}
372
+ />;
373
+
374
+ // it is recommended to always redirect the user to the last known comment cid
375
+ // in case they want to share the url with someone, the author's comments
376
+ // will load faster when using the last comment cid
377
+ import { useParams } from "react-router-dom";
378
+ const params = useParams();
379
+
380
+ useEffect(() => {
381
+ if (lastCommentCid && params.comentCid !== lastCommentCid) {
382
+ history.push(`/u/${params.authorAddress}/c/${lastCommentCid}`);
383
+ }
384
+ }, [lastCommentCid]);
385
+
386
+ // search an author's comments
387
+ const createSearchFilter = (searchTerm) => ({
388
+ filter: (comment) => comment.title?.includes(searchTerm) || comment.content?.includes(searchTerm),
389
+ key: `includes-${searchTerm}`, // required key to cache the filter
390
+ });
391
+ const filter = createSearchFilter("bitcoin");
392
+ const { authorComments, lastCommentCid, hasMore, loadMore } = useAuthorComments({
393
+ commentCid,
394
+ authorAddress,
395
+ filter,
396
+ });
397
+ ```
398
+
399
+ #### Get a community
400
+
401
+ ```jsx
402
+ const community = useCommunity({ community: { name: communityAddress, publicKey: communityPublicKey } });
403
+ const communityStats = useCommunityStats({
404
+ community: { name: communityAddress, publicKey: communityPublicKey },
405
+ });
406
+ const { communities } = useCommunities({
407
+ communities: [
408
+ { name: communityAddress, publicKey: communityPublicKey },
409
+ { name: communityAddress2, publicKey: communityPublicKey2 },
410
+ { name: communityAddress3, publicKey: communityPublicKey3 },
411
+ ],
412
+ });
413
+
414
+ // use without affecting performance
415
+ const { communities: cachedCommunities } = useCommunities({
416
+ communities: [
417
+ { name: communityAddress, publicKey: communityPublicKey },
418
+ { name: communityAddress2, publicKey: communityPublicKey2 },
419
+ { name: communityAddress3, publicKey: communityPublicKey3 },
420
+ ],
421
+ onlyIfCached: true,
422
+ });
423
+
424
+ // community.posts are not validated, to show posts
425
+ const { feed, hasMore, loadMore } = useFeed({
426
+ communities: [{ name: communityAddress, publicKey: communityPublicKey }],
427
+ });
428
+
429
+ // to show a preloaded post without rerenders, validate manually
430
+ const { valid } = useValidateComment({ comment: community.posts.pages.topAll.comments[0] });
431
+ if (valid === false) {
432
+ // don't show this post, it's malicious
433
+ }
434
+ // won't cause any rerenders if true
435
+ ```
436
+
437
+ #### Create a post or comment using callbacks
438
+
439
+ ```jsx
440
+ const onChallenge = async (challenges: Challenge[], comment: Comment) => {
441
+ let challengeAnswers: string[]
442
+ try {
443
+ // ask the user to complete the challenges in a modal window
444
+ challengeAnswers = await getChallengeAnswersFromUser(challenges)
445
+ }
446
+ catch (e) {
447
+ // if he declines, throw error and don't get a challenge answer
448
+ }
449
+ if (challengeAnswers) {
450
+ // if user declines, publishChallengeAnswers is not called, retry loop stops
451
+ await comment.publishChallengeAnswers(challengeAnswers)
452
+ }
453
+ }
454
+
455
+ const onChallengeVerification = (challengeVerification, comment) => {
456
+ // if the challengeVerification fails, a new challenge request will be sent automatically
457
+ // to break the loop, the user must decline to send a challenge answer
458
+ // if the community owner sends more than 1 challenge for the same challenge request, subsequents will be ignored
459
+ if (challengeVerification.challengeSuccess === true) {
460
+ console.log('challenge success', {publishedCid: challengeVerification.publication.cid})
461
+ }
462
+ else if (challengeVerification.challengeSuccess === false) {
463
+ console.error('challenge failed', {reason: challengeVerification.reason, errors: challengeVerification.errors})
464
+ }
465
+ }
466
+
467
+ const onError = (error, comment) => console.error(error)
468
+
469
+ const publishCommentOptions = {
470
+ content: 'hello',
471
+ title: 'hello',
472
+ communityAddress: '12D3KooW...',
473
+ onChallenge,
474
+ onChallengeVerification,
475
+ onError
476
+ }
477
+
478
+ const {index, state, publishComment, abandonPublish} = usePublishComment(publishCommentOptions)
479
+
480
+ // create post
481
+ await publishComment()
482
+ // pending comment index
483
+ console.log(index)
484
+ // pending comment state
485
+ console.log(state)
486
+
487
+ // after publishComment is called, the account comment index gets defined
488
+ // it is recommended to immediately redirect the user to a page displaying
489
+ // the user's comment with a "pending" label
490
+ if (index !== undefined) {
491
+ history.push(`/profile/c/${index}`)
492
+ // on the "pending" comment page, you can get the pending comment by doing
493
+ // const accountComment = useAccountComment({commentIndex: index})
494
+ // after accountComment.cid gets defined, it means the comment was published successfully
495
+ // it is recommended to immediately redirect to `/p/${accountComment.communityAddress}/c/${useAccountComment.cid}`
496
+ }
497
+
498
+ // if the user closes the challenge modal and wants to cancel publishing:
499
+ await abandonPublish()
500
+ // the pending local account comment is removed from accountComments
501
+ // this works even if called immediately from onChallenge before publishComment() resolves
502
+
503
+ // reply to a post or comment
504
+ const publishReplyOptions = {
505
+ content: 'hello',
506
+ parentCid: 'Qm...', // the cid of the comment to reply to
507
+ communityAddress: '12D3KooW...',
508
+ onChallenge,
509
+ onChallengeVerification,
510
+ onError
511
+ }
512
+ const {publishComment} = usePublishComment(publishReplyOptions)
513
+ await publishComment()
514
+
515
+ // when displaying replies, it is recommended to include the user's pending replies
516
+ // https://github.com/bitsocialnet/bitsocial-react-hooks/#get-replies-to-a-post-nested (nested)
517
+ // https://github.com/bitsocialnet/bitsocial-react-hooks/#get-replies-to-a-post-flattened-not-nested (not nested)
518
+ ```
519
+
520
+ #### Create a post or comment using hooks
521
+
522
+ ```jsx
523
+ const publishCommentOptions = {
524
+ content: "hello",
525
+ title: "hello",
526
+ communityAddress: "12D3KooW...",
527
+ };
528
+
529
+ const {
530
+ index,
531
+ state,
532
+ publishComment,
533
+ challenge,
534
+ challengeVerification,
535
+ publishChallengeAnswers,
536
+ abandonPublish,
537
+ error,
538
+ } = usePublishComment(publishCommentOptions);
539
+
540
+ if (challenge) {
541
+ // display challenges to user and call publishChallengeAnswers(challengeAnswers)
542
+ }
543
+
544
+ if (challengeVerification) {
545
+ // display challengeVerification.challengeSuccess to user
546
+ // redirect to challengeVerification.publication.cid
547
+ }
548
+
549
+ if (error) {
550
+ // display error to user
551
+ }
552
+
553
+ // if the user closes your challenge modal:
554
+ if (challenge && challengeModalClosedByUser) {
555
+ await abandonPublish();
556
+ }
557
+
558
+ // after publishComment is called, the account comment index gets defined
559
+ // it is recommended to immediately redirect the user to a page displaying
560
+ // the user's comment with a "pending" label
561
+ if (index !== undefined) {
562
+ history.push(`/profile/c/${index}`);
563
+ // on the "pending" comment page, you can get the pending comment by doing
564
+ // const accountComment = useAccountComment({commentIndex: index})
565
+ // after accountComment.cid gets defined, it means the comment was published successfully
566
+ // it is recommended to immediately redirect to `/p/${accountComment.communityAddress}/c/${useAccountComment.cid}`
567
+ }
568
+
569
+ // create post
570
+ await publishComment();
571
+ ```
572
+
573
+ #### Create a post or comment anonymously (without account.signer or account.author)
574
+
575
+ ```jsx
576
+ const account = useAccount();
577
+ const signer = await account.pkc.createSigner();
578
+
579
+ const publishCommentOptions = {
580
+ content: "hello",
581
+ title: "hello",
582
+ communityAddress: "12D3KooW...",
583
+ // use a newly generated author address (optional)
584
+ signer,
585
+ // use a different display name (optional)
586
+ author: {
587
+ displayName: "Esteban",
588
+ address: signer.address,
589
+ },
590
+ };
591
+
592
+ const { publishComment } = usePublishComment(publishCommentOptions);
593
+ await publishComment();
594
+ ```
595
+
596
+ #### Create a vote
597
+
598
+ ```jsx
599
+ const commentCid = "QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui";
600
+ const publishVoteOptions = {
601
+ commentCid,
602
+ vote: 1,
603
+ communityAddress: "news.eth",
604
+ onChallenge,
605
+ onChallengeVerification,
606
+ onError,
607
+ };
608
+ const { state, error, publishVote } = usePublishVote(publishVoteOptions);
609
+
610
+ await publishVote();
611
+ console.log(state);
612
+ console.log(error);
613
+
614
+ // display the user's vote
615
+ const { vote } = useAccountVote({ commentCid });
616
+
617
+ if (vote === 1) console.log("user voted 1");
618
+ if (vote === -1) console.log("user voted -1");
619
+ if (vote === 0) console.log("user voted 0");
620
+ if (vote === undefined) console.log(`user didn't vote yet`);
621
+ ```
622
+
623
+ #### Create a comment edit
624
+
625
+ ```jsx
626
+ const publishCommentEditOptions = {
627
+ commentCid: "QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui",
628
+ content: "edited content",
629
+ communityAddress: "news.eth",
630
+ onChallenge,
631
+ onChallengeVerification,
632
+ onError,
633
+ };
634
+ const { state, error, publishCommentEdit } = usePublishCommentEdit(publishCommentEditOptions);
635
+
636
+ await publishCommentEdit();
637
+ console.log(state);
638
+ console.log(error);
639
+
640
+ // view the status of a comment edit instantly
641
+ let comment = useComment({ commentCid: publishCommentEditOptions.commentCid });
642
+ const { state: editedCommentState, editedComment } = useEditedComment({ comment });
643
+
644
+ // if the comment has a succeeded, failed or pending edit, use the edited comment
645
+ if (editedComment) {
646
+ comment = editedComment;
647
+ }
648
+
649
+ let editLabel;
650
+ if (editedCommentState === "succeeded") {
651
+ editLabel = { text: "EDITED", color: "green" };
652
+ }
653
+ if (editedCommentState === "pending") {
654
+ editLabel = { text: "PENDING EDIT", color: "orange" };
655
+ }
656
+ if (editedCommentState === "failed") {
657
+ editLabel = { text: "FAILED EDIT", color: "red" };
658
+ }
659
+ ```
660
+
661
+ #### Create a comment moderation
662
+
663
+ ```jsx
664
+ const publishCommentModerationOptions = {
665
+ commentCid: "QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui",
666
+ communityAddress: "news.eth",
667
+ commentModeration: { locked: true },
668
+ onChallenge,
669
+ onChallengeVerification,
670
+ onError,
671
+ };
672
+ const { state, error, publishCommentModeration } = usePublishCommentModeration(
673
+ publishCommentModerationOptions,
674
+ );
675
+
676
+ await publishCommentModeration();
677
+ console.log(state);
678
+ console.log(error);
679
+
680
+ // view the status of a comment moderation instantly
681
+ let comment = useComment({ commentCid: publishCommentModerationOptions.commentCid });
682
+ const { state: editedCommentState, editedComment } = useEditedComment({ comment });
683
+
684
+ // if the comment has a succeeded, failed or pending edit, use the edited comment
685
+ if (editedComment) {
686
+ comment = editedComment;
687
+ }
688
+
689
+ let editLabel;
690
+ if (editedCommentState === "succeeded") {
691
+ editLabel = { text: "EDITED", color: "green" };
692
+ }
693
+ if (editedCommentState === "pending") {
694
+ editLabel = { text: "PENDING EDIT", color: "orange" };
695
+ }
696
+ if (editedCommentState === "failed") {
697
+ editLabel = { text: "FAILED EDIT", color: "red" };
698
+ }
699
+ ```
700
+
701
+ #### Delete a comment
702
+
703
+ You can remove comments from your local account database (local JSON export / IndexedDB state) in two ways.
704
+ This only removes local account history entries; it does not delete already-published network comments.
705
+
706
+ **1. Abandon a pending publish** — if you just published and want to cancel before it propagates:
707
+
708
+ ```jsx
709
+ const { publishComment, abandonPublish } = usePublishComment(publishCommentOptions);
710
+
711
+ await publishComment();
712
+ // User changes mind — abandon the pending comment
713
+ await abandonPublish();
714
+ // Hook state returns to ready; the comment is removed from accountComments
715
+ ```
716
+
717
+ **2. Delete by index or CID** — remove any of your comments (pending or published):
718
+
719
+ ```jsx
720
+ import { deleteComment, useAccountComments } from "@bitsocial/bitsocial-react-hooks";
721
+
722
+ // By account comment index (from usePublishComment or useAccountComment)
723
+ const { index, publishComment } = usePublishComment(publishCommentOptions);
724
+ await publishComment();
725
+ await deleteComment(index);
726
+
727
+ // By comment CID (from useAccountComments or useAccountComment)
728
+ const { accountComments } = useAccountComments();
729
+ const accountComment = accountComments[0];
730
+ await deleteComment(accountComment.cid);
731
+ ```
732
+
733
+ > **Note:** `accountComment.index` can change after deletions. If you delete a comment, indices of comments after it may shift. Prefer using `commentCid` when you need a stable identifier, or re-fetch `accountComments` after deletions.
734
+
735
+ **Common cleanup pattern (remove failed UI clutter):**
736
+
737
+ ```jsx
738
+ import { deleteComment, useAccountComments } from "@bitsocial/bitsocial-react-hooks";
739
+
740
+ const { accountComments } = useAccountComments();
741
+ const failedComments = accountComments.filter((comment) => comment.state === "failed");
742
+
743
+ for (const failedComment of failedComments) {
744
+ // failed pending comments may not have a cid yet, so fallback to index
745
+ await deleteComment(failedComment.cid || failedComment.index);
746
+ }
747
+ ```
748
+
749
+ #### Subscribe to a community
750
+
751
+ ```jsx
752
+ let communityAddress = "news.eth";
753
+ communityAddress = "12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z";
754
+ communityAddress = "tech.eth";
755
+ const { subscribed, subscribe, unsubscribe } = useSubscribe({ communityAddress });
756
+ await subscribe();
757
+ console.log(subscribed); // true
758
+
759
+ // view subscriptions
760
+ const account = useAccount();
761
+ console.log(account.subscriptions); // ['news.eth', '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z', 'tech.eth']
762
+
763
+ // unsubscribe
764
+ await unsubscribe();
765
+
766
+ // get a feed of subscriptions
767
+ const communities = account.subscriptions.map((communityAddress) => ({ name: communityAddress }));
768
+ const { feed, hasMore, loadMore } = useFeed({
769
+ communities,
770
+ sortType: "topAll",
771
+ });
772
+ console.log(feed);
773
+ ```
774
+
775
+ #### Get feed
776
+
777
+ ```jsx
778
+ import {Virtuoso} from 'react-virtuoso'
779
+ const topAllCommunities = [
780
+ {name: 'memes.eth', publicKey: '12D3KooWMemes...'},
781
+ {publicKey: '12D3KooWNews...'},
782
+ {publicKey: '12D3KooWTech...'},
783
+ ]
784
+ const {feed, hasMore, loadMore} = useFeed({communities: topAllCommunities, sortType: 'topAll'})
785
+
786
+ <Virtuoso
787
+ data={feed}
788
+ itemContent={(index, post) => <Post index={index} post={post}/>}
789
+ useWindowScroll={true}
790
+ components={{Footer: hasMore ? () => <Loading/> : undefined}}
791
+ endReached={loadMore}
792
+ increaseViewportBy={{bottom: 600, top: 600}}
793
+ />
794
+
795
+ // you probably will want to buffer some feeds in the background so they are already loaded
796
+ // when you need them
797
+ useBufferedFeeds({
798
+ feedsOptions: [
799
+ {communities: [{name: 'news.eth'}, {name: 'crypto.eth'}], sortType: 'new'},
800
+ {communities: [{name: 'memes.eth', publicKey: '12D3KooWMemes...'}], sortType: 'topWeek'},
801
+ {communities: [{publicKey: '12D3KooW...'}, {publicKey: '12D3KooW...'}, {publicKey: '12D3KooW...'}, {publicKey: '12D3KooW...'}], sortType: 'hot'}
802
+ ]
803
+ })
804
+
805
+ // search a feed
806
+ const createSearchFilter = (searchTerm) => ({
807
+ filter: (comment) => comment.title?.includes(searchTerm) || comment.content?.includes(searchTerm),
808
+ key: `includes-${searchTerm}` // required key to cache the filter
809
+ })
810
+ const searchFilter = createSearchFilter('bitcoin')
811
+ const searchedCommunities = communityAddresses.map((communityAddress) => ({ name: communityAddress }))
812
+ const {feed, hasMore, loadMore} = useFeed({communities: searchedCommunities, filter: searchFilter})
813
+
814
+ // image only feed
815
+ const imageOnlyFilter = {
816
+ filter: (comment) => getCommentLinkMediaType(comment?.link) === 'image',
817
+ key: 'image-only' // required key to cache the filter
818
+ }
819
+ const {feed, hasMore, loadMore} = useFeed({
820
+ communities: searchedCommunities,
821
+ filter: imageOnlyFilter,
822
+ })
823
+
824
+ // widen a freshness window without replacing the current feed instance
825
+ const {feed, expandTimeWindow} = useFeed({
826
+ communities: [{name: 'news.eth'}],
827
+ sortType: 'active',
828
+ newerThan: 60 * 60 * 24,
829
+ })
830
+
831
+ await expandTimeWindow(60 * 60 * 24 * 7)
832
+ ```
833
+
834
+ #### Get mod queue (pending approval)
835
+
836
+ ```jsx
837
+ import {Virtuoso} from 'react-virtuoso'
838
+ const {feed, hasMore, loadMore} = useFeed({
839
+ communities: [{name: 'memes.eth'}, {publicKey: '12D3KooW...'}, {publicKey: '12D3KooW...'}],
840
+ modQueue: ['pendingApproval']
841
+ })
842
+
843
+ <Virtuoso
844
+ data={feed}
845
+ itemContent={(index, post) => <Post index={index} post={post}/>}
846
+ useWindowScroll={true}
847
+ components={{Footer: hasMore ? () => <Loading/> : undefined}}
848
+ endReached={loadMore}
849
+ increaseViewportBy={{bottom: 600, top: 600}}
850
+ />
851
+ ```
852
+
853
+ Comments automatically drop out of this feed once they are no longer returned by the pending-approval mod-queue pages.
854
+
855
+ #### Approve a pending approval comment
856
+
857
+ ```jsx
858
+ const publishCommentModerationOptions = {
859
+ commentCid: "QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui",
860
+ communityAddress: "news.eth",
861
+ commentModeration: { approved: true },
862
+ onChallenge,
863
+ onChallengeVerification,
864
+ onError,
865
+ };
866
+ const { state, error, publishCommentModeration } = usePublishCommentModeration(
867
+ publishCommentModerationOptions,
868
+ );
869
+
870
+ await publishCommentModeration();
871
+ console.log(state);
872
+ console.log(error);
873
+ ```
874
+
875
+ #### Edit an account
876
+
877
+ ```jsx
878
+ import {useAccount, setAccount, useResolvedAuthorAddress} from '@bitsocial/bitsocial-react-hooks'
879
+ const account = useAccount() // or useAccount('Account 2') to use an account other than the active one
880
+
881
+ // `account.author.wallets` only auto-generates an `eth` wallet by default.
882
+ // `account.chainProviders` is the canonical chain config for wallets, NFT lookups, and other chain reads.
883
+ // `account.nameResolversChainProviders` optionally overrides only the RPCs used for `.eth` / `.bso` author-name resolution.
884
+ console.log(account.author.wallets.eth)
885
+
886
+ const author: {...account.author, displayName: 'John'}
887
+ const editedAccount = {
888
+ ...account,
889
+ author,
890
+ chainProviders: {
891
+ ...account.chainProviders,
892
+ eth: { urls: ['https://ethereum-rpc.publicnode.com', 'viem', 'ethers.js'], chainId: 1 },
893
+ },
894
+ nameResolversChainProviders: {
895
+ eth: { urls: ['https://ethereum-rpc.publicnode.com', 'viem'], chainId: 1 },
896
+ },
897
+ }
898
+
899
+ await setAccount(editedAccount)
900
+
901
+ // check if the user has set their .eth or .bso author name properly, use {cache: false} or it won't update
902
+ const author = {...account.author, address: 'username.bso'} // or 'username.eth'
903
+ // authorAddress should equal to account.signer.address
904
+ const {resolvedAddress, state, error, chainProvider, nameResolver} = useResolvedAuthorAddress({author, cache: false})
905
+
906
+ // result
907
+ if (state === 'succeeded') {
908
+ console.log('Succeeded resolving address', resolvedAddress)
909
+ }
910
+ if (state === 'failed') {
911
+ console.log('Failed resolving address', error.message)
912
+ }
913
+
914
+ // pending
915
+ if (state === 'resolving' && nameResolver) {
916
+ console.log(`Resolving ${nameResolver.nameSystem} address from ${nameResolver.providerLabel}`)
917
+ console.log('Matching chain provider URLs', chainProvider?.urls)
918
+ }
919
+ ```
920
+
921
+ #### Delete account
922
+
923
+ > Note: deleting account is unrecoverable, warn the user to export/backup his account before deleting
924
+
925
+ ```jsx
926
+ import { deleteAccount } from "@bitsocial/bitsocial-react-hooks";
927
+
928
+ // delete active account
929
+ await deleteAccount();
930
+
931
+ // delete account by name
932
+ await deleteAccount("Account 2");
933
+ ```
934
+
935
+ #### Get your own comments and votes
936
+
937
+ ```jsx
938
+ // all my own comments
939
+ const { accountComments } = useAccountComments();
940
+ for (const accountComment of accountComments) {
941
+ // it is recommended to show a label in the UI if accountComment.state is 'pending' or 'failed'
942
+ console.log("comment", accountComment.index, "is status", accountComment.state);
943
+ }
944
+ // `state` becomes `failed` as soon as a pending local publish records terminal failure (`publishingState === "failed"` and `state === "stopped"`) or a publish error, instead of waiting for the 20-minute fallback.
945
+ // note: accountComment.index can change after deletions; prefer commentCid for stable identifiers
946
+
947
+ // all my own votes
948
+ const { accountVotes } = useAccountVotes();
949
+
950
+ // my own comments in memes.eth
951
+ const communityAddress = "memes.eth";
952
+ const myCommentsInMemesEth = useAccountComments({ communityAddress });
953
+
954
+ // my own posts in memes.eth
955
+ const filter = useCallback(
956
+ (comment) => comment.communityAddress === communityAddress && !comment.parentCid,
957
+ [communityAddress],
958
+ );
959
+ const myPostsInMemesEth = useAccountComments({ filter });
960
+
961
+ // my own replies in a post with cid 'Qm...'
962
+ const postCid = "Qm...";
963
+ const filter = useCallback((comment) => comment.postCid === postCid, [postCid]);
964
+ const myCommentsInSomePost = useAccountComments({ filter });
965
+
966
+ // my own replies to a comment with cid 'Qm...'
967
+ const parentCommentCid = "Qm...";
968
+ const myRepliesToSomeComment = useAccountComments({ parentCid: parentCommentCid });
969
+
970
+ // recent own comments in memes.eth, newest first, one page at a time
971
+ const recentMyCommentsInMemesEth = useAccountComments({
972
+ communityAddress,
973
+ newerThan: 60 * 60 * 24 * 30,
974
+ sortType: "new",
975
+ page: 0,
976
+ pageSize: 20,
977
+ });
978
+
979
+ // get one own comment directly by cid
980
+ const accountComment = useAccountComment({ commentCid: "Qm..." });
981
+
982
+ // get a specific set of own comments by account comment index
983
+ const replacementReplies = useAccountComments({ commentIndices: [5, 7, 9] });
984
+
985
+ // voted profile tab helpers
986
+ const recentUpvotes = useAccountVotes({
987
+ vote: 1,
988
+ newerThan: 60 * 60 * 24 * 30,
989
+ sortType: "new",
990
+ page: 0,
991
+ pageSize: 20,
992
+ });
993
+
994
+ // know if you upvoted a comment already with cid 'Qm...'
995
+ const { vote } = useAccountVote({ commentCid: "Qm..." });
996
+ console.log(vote); // 1, -1 or 0
997
+
998
+ // my own pending posts in a feed
999
+ const { feed } = useFeed({
1000
+ communities: [{ name: communityAddress }],
1001
+ accountComments: { newerThan: Infinity, append: false },
1002
+ });
1003
+
1004
+ // my own pending replies in a replies feed
1005
+ const { replies } = useReplies({
1006
+ comment: post,
1007
+ accountComments: { newerThan: Infinity, append: false },
1008
+ });
1009
+ ```
1010
+
1011
+ #### Determine if a comment is your own
1012
+
1013
+ ```jsx
1014
+ const account = useAccount();
1015
+ const comment = useComment({ commentCid });
1016
+ const isMyOwnComment = account?.author.address === comment?.author.address;
1017
+ ```
1018
+
1019
+ #### Get account notifications
1020
+
1021
+ ```jsx
1022
+ const { notifications, markAsRead } = useNotifications();
1023
+ for (const notification of notifications) {
1024
+ console.log(notification);
1025
+ }
1026
+ await markAsRead();
1027
+
1028
+ const johnsNotifications = useNotifications({ accountName: "John" });
1029
+ for (const notification of johnsNotifications.notifications) {
1030
+ console.log(notification);
1031
+ }
1032
+ await johnsNotifications.markAsRead();
1033
+
1034
+ // get the unread notification counts for all accounts
1035
+ const { accounts } = useAccounts();
1036
+ const accountsUnreadNotificationsCounts = accounts?.map(
1037
+ (account) => account.unreadNotificationCount,
1038
+ );
1039
+ ```
1040
+
1041
+ #### Block an address (author, community or multisub)
1042
+
1043
+ ```jsx
1044
+ const address: 'community-address.eth' // or 'author-address.eth' or '12D3KooW...'
1045
+ const {blocked, unblock, block} = useBlock({address})
1046
+
1047
+ if (blocked) {
1048
+ console.log(`'${address}' is blocked`)
1049
+ }
1050
+ else {
1051
+ console.log(`'${address}' is not blocked`)
1052
+ }
1053
+
1054
+ // to block
1055
+ block()
1056
+
1057
+ // to unblock
1058
+ unblock()
1059
+ ```
1060
+
1061
+ #### Block a cid (hide a comment)
1062
+
1063
+ ```jsx
1064
+ const { blocked, unblock, block } = useBlock({ cid: "Qm..." });
1065
+
1066
+ if (blocked) {
1067
+ console.log(`'${cid}' is blocked`);
1068
+ } else {
1069
+ console.log(`'${cid}' is not blocked`);
1070
+ }
1071
+
1072
+ // to block
1073
+ block();
1074
+
1075
+ // to unblock
1076
+ unblock();
1077
+ ```
1078
+
1079
+ #### (Desktop only) Create a community
1080
+
1081
+ ```jsx
1082
+ const createCommunityOptions = { title: "My community title" };
1083
+ const { createdCommunity, createCommunity } = useCreateCommunity(createCommunityOptions);
1084
+ await createCommunity();
1085
+
1086
+ // it is recommended to redirect to `p/${createdCommunity.address}` after creation
1087
+ if (createdCommunity?.address) {
1088
+ console.log("created community with title", createdCommunity.title);
1089
+ history.push(`/p/${createdCommunity.address}`);
1090
+ }
1091
+
1092
+ // after the community is created, fetch it using
1093
+ const { accountCommunities } = useAccountCommunities();
1094
+ const accountCommunityAddresses = Object.keys(accountCommunities);
1095
+ const communities = useCommunities({
1096
+ communities: accountCommunityAddresses.map((communityAddress) => ({ name: communityAddress })),
1097
+ });
1098
+ // or
1099
+ const _community = useCommunity({ community: { name: createdCommunity.address } });
1100
+ ```
1101
+
1102
+ #### (Desktop only) List the communities you created
1103
+
1104
+ ```jsx
1105
+ const { accountCommunities } = useAccountCommunities();
1106
+ const ownerCommunityAddresses = Object.keys(accountCommunities).filter(
1107
+ (communityAddress) => accountCommunities[communityAddress].role?.role === "owner",
1108
+ );
1109
+ const communities = useCommunities({
1110
+ communities: ownerCommunityAddresses.map((communityAddress) => ({ name: communityAddress })),
1111
+ });
1112
+ ```
1113
+
1114
+ #### (Desktop only) Edit your community settings
1115
+
1116
+ ```jsx
1117
+ const onChallenge = async (challenges: Challenge[], communityEdit: CommunityEdit) => {
1118
+ let challengeAnswers: string[]
1119
+ try {
1120
+ challengeAnswers = await getChallengeAnswersFromUser(challenges)
1121
+ }
1122
+ catch (e) {}
1123
+ if (challengeAnswers) {
1124
+ await communityEdit.publishChallengeAnswers(challengeAnswers)
1125
+ }
1126
+ }
1127
+
1128
+ const onChallengeVerification = (challengeVerification, communityEdit) => {
1129
+ console.log('challenge verified', challengeVerification)
1130
+ }
1131
+
1132
+ const onError = (error, communityEdit) => console.error(error)
1133
+
1134
+ // add ENS to your community
1135
+ const editCommunityOptions = {
1136
+ communityAddress: '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z', // the previous address before changing it
1137
+ address: 'your-community-address.eth', // the new address to change to
1138
+ onChallenge,
1139
+ onChallengeVerification,
1140
+ onError
1141
+ }
1142
+
1143
+ await publishCommunityEdit()
1144
+
1145
+ // edit other community settings
1146
+ const editCommunityOptions = {
1147
+ communityAddress: 'your-community-address.eth', // the address of the community to change
1148
+ title: 'Your title',
1149
+ description: 'Your description',
1150
+ onChallenge,
1151
+ onChallengeVerification,
1152
+ onError
1153
+ }
1154
+ const {publishCommunityEdit} = usePublishCommunityEdit(editCommunityOptions)
1155
+ await publishCommunityEdit()
1156
+
1157
+ // verify if ENS was set correctly, use {cache: false} or it won't update
1158
+ const {resolvedAddress} = useResolvedCommunityAddress({communityAddress: 'your-community-address.eth', cache: false})
1159
+
1160
+ // result
1161
+ if (state === 'succeeded') {
1162
+ console.log('Succeeded resolving address', resolvedAddress)
1163
+ console.log('ENS set correctly', resolvedAddress === community.signer.address)
1164
+ }
1165
+ if (state === 'failed') {
1166
+ console.log('Failed resolving address', error.message)
1167
+ }
1168
+
1169
+ // pending
1170
+ if (state === 'resolving') {
1171
+ console.log('Resolving address from chain provider URL', chainProvider.urls)
1172
+ }
1173
+ ```
1174
+
1175
+ #### Export and import account
1176
+
1177
+ ```jsx
1178
+ import {
1179
+ exportAccount,
1180
+ importAccount,
1181
+ setActiveAccount,
1182
+ setAccountsOrder,
1183
+ } from "@bitsocial/bitsocial-react-hooks";
1184
+
1185
+ // get active account 'Account 1'
1186
+ const activeAccount = useAccount();
1187
+
1188
+ // export active account, tell user to copy or download this json
1189
+ const activeAccountJson = await exportAccount();
1190
+
1191
+ // import account
1192
+ await importAccount(activeAccountJson);
1193
+
1194
+ // get imported account 'Account 1 2' (' 2' gets added to account.name if account.name already exists)
1195
+ const importedAccount = useAccount("Account 1 2");
1196
+
1197
+ // make imported account active account
1198
+ await setActiveAccount("Account 1 2");
1199
+
1200
+ // reorder the accounts list
1201
+ await setAccountsOrder(["Account 1 2", "Account 1"]);
1202
+ ```
1203
+
1204
+ #### View the status of a comment edit
1205
+
1206
+ ```jsx
1207
+ let comment = useComment({ commentCid });
1208
+ const { state: editedCommentState, editedComment } = useEditedComment({ comment });
1209
+
1210
+ // if the comment has a succeeded, failed or pending edit, use the edited comment
1211
+ if (editedComment) {
1212
+ comment = editedComment;
1213
+ }
1214
+
1215
+ let editLabel;
1216
+ if (editedCommentState === "succeeded") {
1217
+ editLabel = { text: "EDITED", color: "green" };
1218
+ }
1219
+ if (editedCommentState === "pending") {
1220
+ editLabel = { text: "PENDING EDIT", color: "orange" };
1221
+ }
1222
+ if (editedCommentState === "failed") {
1223
+ editLabel = { text: "FAILED EDIT", color: "red" };
1224
+ }
1225
+ ```
1226
+
1227
+ #### View the status of a specific comment edit property
1228
+
1229
+ ```jsx
1230
+ const comment = useComment({ commentCid });
1231
+ const editedComment = useEditedComment({ comment });
1232
+ if (editedComment.failedEdits.removed !== undefined) {
1233
+ console.log("failed editing comment.removed property");
1234
+ }
1235
+ if (editedComment.succeededEdits.removed !== undefined) {
1236
+ console.log("succeeded editing comment.removed property");
1237
+ }
1238
+ if (editedCommentResult.pendingEdits.removed !== undefined) {
1239
+ console.log("pending editing comment.removed property");
1240
+ }
1241
+
1242
+ // view the full comment with all edited properties (both succeeded and pending)
1243
+ console.log(editedComment.editedComment);
1244
+ console.log(editedComment.editedComment.commentModeration?.removed);
1245
+
1246
+ // view the state of all edits of the comment
1247
+ console.log(editedComment.state); // 'unedited' | 'succeeded' | 'pending' | 'failed'
1248
+ ```
1249
+
1250
+ Moderation fields are mirrored on both the top-level keys like `comment.removed` and the nested `comment.commentModeration.removed` shape.
1251
+
1252
+ #### List all comment and community edits the account has performed
1253
+
1254
+ ```jsx
1255
+ const { accountEdits } = useAccountEdits();
1256
+ for (const accountEdit of accountEdits) {
1257
+ console.log(accountEdit);
1258
+ }
1259
+ console.log(`there's ${accountEdits.length} account edits`);
1260
+
1261
+ // get only the account edits of a specific comment
1262
+ const commentCid = "Qm...";
1263
+ const filter = useCallback((edit) => edit.commentCid === commentCid, [commentCid]); // important to use useMemo or the same function or will cause rerenders
1264
+ const { accountEdits } = useAccountEdits({ filter });
1265
+
1266
+ // only get account edits in a specific community
1267
+ const communityAddress = "news.eth";
1268
+ const filter = useCallback(
1269
+ (edit) => edit.communityAddress === communityAddress,
1270
+ [communityAddress],
1271
+ );
1272
+ const { accountEdits } = useAccountEdits({ filter });
1273
+ ```
1274
+
1275
+ #### Get replies to a post (nested or flat)
1276
+
1277
+ ```jsx
1278
+ import { useReplies, useComment, useAccountComment } from "@bitsocial/bitsocial-react-hooks";
1279
+
1280
+ // NOTE: recommended to use the same replies options for all depths, or will load slower
1281
+ const useRepliesOptions = {
1282
+ sortType: "best",
1283
+ flat: false,
1284
+ repliesPerPage: 20,
1285
+ onlyIfCached: false,
1286
+ accountComments: { newerThan: Infinity, append: false },
1287
+ };
1288
+
1289
+ const Reply = ({ reply, updatedReply }) => {
1290
+ const { replies, updatedReplies, bufferedReplies, hasMore, loadMore } = useReplies({
1291
+ ...useRepliesOptions,
1292
+ comment: reply,
1293
+ });
1294
+
1295
+ // updatedReply updates values in real time, reply does not
1296
+ const score = (updatedReply?.upvoteCount || 0) - (updatedReply?.downvoteCount || 0);
1297
+
1298
+ // bufferedReplies updates in real time, can show new replies count in real time
1299
+ const moreReplies =
1300
+ hasMore && bufferedReplies?.length !== 0 ? `(${bufferedReplies.length} more replies)` : "";
1301
+
1302
+ // publishing states exist only on account comment
1303
+ const accountReply = useAccountComment({ commentIndex: reply.index });
1304
+ const state = accountReply?.state;
1305
+ const publishingStateString = useStateString(accountReply);
1306
+
1307
+ return (
1308
+ <div>
1309
+ <div>
1310
+ {score} {reply.author.address} {reply.timestamp} {moreReplies}
1311
+ </div>
1312
+ {state === "pending" && <div>PENDING ({publishingStateString})</div>}
1313
+ {state === "failed" && <div>FAILED</div>}
1314
+ <div>{reply.content}</div>
1315
+ <div style={{ marginLeft: 4 }}>
1316
+ {replies.map((reply, index) => (
1317
+ <Reply
1318
+ key={reply?.index || reply?.cid}
1319
+ reply={reply}
1320
+ updatedReply={updatedReplies[index]}
1321
+ />
1322
+ ))}
1323
+ </div>
1324
+ </div>
1325
+ );
1326
+ };
1327
+
1328
+ const comment = useComment({ commentCid });
1329
+ const { replies, updatedReplies, hasMore, loadMore } = useReplies({
1330
+ ...useRepliesOptions,
1331
+ comment,
1332
+ });
1333
+ const repliesComponents = replies.map((reply, index) => (
1334
+ <Reply key={reply?.index || reply?.cid} reply={reply} updatedReply={updatedReplies[index]} />
1335
+ ));
1336
+ ```
1337
+
1338
+ #### Format short CIDs and addresses
1339
+
1340
+ ```jsx
1341
+ import { useShortAddress, useShortCid } from "@bitsocial/bitsocial-react-hooks";
1342
+
1343
+ const shortParentCid = useShortCid(comment.parentCid);
1344
+ const shortAddress = useShortAddress(address);
1345
+ ```
1346
+
1347
+ #### useBufferedFeeds with concurrency
1348
+
1349
+ ```jsx
1350
+ const useBufferedFeedsWithConcurrency = ({feedOptions}) => {
1351
+
1352
+ const communities = useCommunities()
1353
+
1354
+ return useBufferedFeeds({feedsOptions})
1355
+ }
1356
+
1357
+ const feedOptions = [
1358
+ {communities: [{name: 'news.eth'}, {name: 'crypto.eth'}], sortType: 'new'},
1359
+ {communities: [{name: 'memes.eth'}], sortType: 'topWeek'},
1360
+ {communities: [{publicKey: '12D3KooW...'}, {publicKey: '12D3KooW...'}, {publicKey: '12D3KooW...'}, {publicKey: '12D3KooW...'}], sortType: 'hot'},
1361
+ ...
1362
+ ]
1363
+
1364
+ useBufferedFeedsWithConcurrency({feedOptions})
1365
+ ```