@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.
- package/LICENSE +674 -0
- package/README.md +1365 -0
- package/dist/hooks/accounts/accounts.d.ts +64 -0
- package/dist/hooks/accounts/accounts.d.ts.map +1 -0
- package/dist/hooks/accounts/accounts.js +706 -0
- package/dist/hooks/accounts/accounts.js.map +1 -0
- package/dist/hooks/accounts/index.d.ts +2 -0
- package/dist/hooks/accounts/index.d.ts.map +1 -0
- package/dist/hooks/accounts/index.js +2 -0
- package/dist/hooks/accounts/index.js.map +1 -0
- package/dist/hooks/accounts/utils.d.ts +6 -0
- package/dist/hooks/accounts/utils.d.ts.map +1 -0
- package/dist/hooks/accounts/utils.js +226 -0
- package/dist/hooks/accounts/utils.js.map +1 -0
- package/dist/hooks/actions/actions.d.ts +19 -0
- package/dist/hooks/actions/actions.d.ts.map +1 -0
- package/dist/hooks/actions/actions.js +552 -0
- package/dist/hooks/actions/actions.js.map +1 -0
- package/dist/hooks/actions/index.d.ts +2 -0
- package/dist/hooks/actions/index.d.ts.map +1 -0
- package/dist/hooks/actions/index.js +2 -0
- package/dist/hooks/actions/index.js.map +1 -0
- package/dist/hooks/authors/author-avatars.d.ts +28 -0
- package/dist/hooks/authors/author-avatars.d.ts.map +1 -0
- package/dist/hooks/authors/author-avatars.js +191 -0
- package/dist/hooks/authors/author-avatars.js.map +1 -0
- package/dist/hooks/authors/authors.d.ts +37 -0
- package/dist/hooks/authors/authors.d.ts.map +1 -0
- package/dist/hooks/authors/authors.js +509 -0
- package/dist/hooks/authors/authors.js.map +1 -0
- package/dist/hooks/authors/index.d.ts +2 -0
- package/dist/hooks/authors/index.d.ts.map +1 -0
- package/dist/hooks/authors/index.js +2 -0
- package/dist/hooks/authors/index.js.map +1 -0
- package/dist/hooks/authors/utils.d.ts +4 -0
- package/dist/hooks/authors/utils.d.ts.map +1 -0
- package/dist/hooks/authors/utils.js +21 -0
- package/dist/hooks/authors/utils.js.map +1 -0
- package/dist/hooks/comments.d.ts +17 -0
- package/dist/hooks/comments.d.ts.map +1 -0
- package/dist/hooks/comments.js +351 -0
- package/dist/hooks/comments.js.map +1 -0
- package/dist/hooks/communities.d.ts +31 -0
- package/dist/hooks/communities.d.ts.map +1 -0
- package/dist/hooks/communities.js +389 -0
- package/dist/hooks/communities.js.map +1 -0
- package/dist/hooks/feeds/feeds.d.ts +18 -0
- package/dist/hooks/feeds/feeds.d.ts.map +1 -0
- package/dist/hooks/feeds/feeds.js +315 -0
- package/dist/hooks/feeds/feeds.js.map +1 -0
- package/dist/hooks/feeds/index.d.ts +2 -0
- package/dist/hooks/feeds/index.d.ts.map +1 -0
- package/dist/hooks/feeds/index.js +2 -0
- package/dist/hooks/feeds/index.js.map +1 -0
- package/dist/hooks/pkc-rpc.d.ts +7 -0
- package/dist/hooks/pkc-rpc.d.ts.map +1 -0
- package/dist/hooks/pkc-rpc.js +88 -0
- package/dist/hooks/pkc-rpc.js.map +1 -0
- package/dist/hooks/replies.d.ts +5 -0
- package/dist/hooks/replies.d.ts.map +1 -0
- package/dist/hooks/replies.js +155 -0
- package/dist/hooks/replies.js.map +1 -0
- package/dist/hooks/states.d.ts +15 -0
- package/dist/hooks/states.d.ts.map +1 -0
- package/dist/hooks/states.js +213 -0
- package/dist/hooks/states.js.map +1 -0
- package/dist/hooks/utils/use-interval.d.ts +3 -0
- package/dist/hooks/utils/use-interval.d.ts.map +1 -0
- package/dist/hooks/utils/use-interval.js +36 -0
- package/dist/hooks/utils/use-interval.js.map +1 -0
- package/dist/hooks/utils/use-previous.d.ts +1 -0
- package/dist/hooks/utils/use-previous.js +10 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/chain/chain.d.ts +36 -0
- package/dist/lib/chain/chain.d.ts.map +1 -0
- package/dist/lib/chain/chain.js +195 -0
- package/dist/lib/chain/chain.js.map +1 -0
- package/dist/lib/chain/index.d.ts +4 -0
- package/dist/lib/chain/index.d.ts.map +1 -0
- package/dist/lib/chain/index.js +4 -0
- package/dist/lib/chain/index.js.map +1 -0
- package/dist/lib/community-address.d.ts +6 -0
- package/dist/lib/community-address.d.ts.map +1 -0
- package/dist/lib/community-address.js +26 -0
- package/dist/lib/community-address.js.map +1 -0
- package/dist/lib/community-ref.d.ts +23 -0
- package/dist/lib/community-ref.d.ts.map +1 -0
- package/dist/lib/community-ref.js +113 -0
- package/dist/lib/community-ref.js.map +1 -0
- package/dist/lib/debug-utils.d.ts +9 -0
- package/dist/lib/debug-utils.d.ts.map +1 -0
- package/dist/lib/debug-utils.js +21 -0
- package/dist/lib/debug-utils.js.map +1 -0
- package/dist/lib/feed-sort-type.d.ts +2 -0
- package/dist/lib/feed-sort-type.d.ts.map +1 -0
- package/dist/lib/feed-sort-type.js +22 -0
- package/dist/lib/feed-sort-type.js.map +1 -0
- package/dist/lib/localforage-lru/index.d.ts +3 -0
- package/dist/lib/localforage-lru/index.d.ts.map +1 -0
- package/dist/lib/localforage-lru/index.js +46 -0
- package/dist/lib/localforage-lru/index.js.map +1 -0
- package/dist/lib/localforage-lru/localforage-lru.d.ts +6 -0
- package/dist/lib/localforage-lru/localforage-lru.d.ts.map +1 -0
- package/dist/lib/localforage-lru/localforage-lru.js +182 -0
- package/dist/lib/localforage-lru/localforage-lru.js.map +1 -0
- package/dist/lib/pkc-compat.d.ts +25 -0
- package/dist/lib/pkc-compat.d.ts.map +1 -0
- package/dist/lib/pkc-compat.js +131 -0
- package/dist/lib/pkc-compat.js.map +1 -0
- package/dist/lib/pkc-js/fixtures/markdown-example.d.ts +3 -0
- package/dist/lib/pkc-js/fixtures/markdown-example.d.ts.map +1 -0
- package/dist/lib/pkc-js/fixtures/markdown-example.js +280 -0
- package/dist/lib/pkc-js/fixtures/markdown-example.js.map +1 -0
- package/dist/lib/pkc-js/index.d.ts +11 -0
- package/dist/lib/pkc-js/index.d.ts.map +1 -0
- package/dist/lib/pkc-js/index.js +85 -0
- package/dist/lib/pkc-js/index.js.map +1 -0
- package/dist/lib/pkc-js/pkc-js-mock-content.d.ts +3 -0
- package/dist/lib/pkc-js/pkc-js-mock-content.d.ts.map +1 -0
- package/dist/lib/pkc-js/pkc-js-mock-content.js +1235 -0
- package/dist/lib/pkc-js/pkc-js-mock-content.js.map +1 -0
- package/dist/lib/pkc-js/pkc-js-mock.d.ts +137 -0
- package/dist/lib/pkc-js/pkc-js-mock.d.ts.map +1 -0
- package/dist/lib/pkc-js/pkc-js-mock.js +644 -0
- package/dist/lib/pkc-js/pkc-js-mock.js.map +1 -0
- package/dist/lib/polyfill.d.ts +3 -0
- package/dist/lib/polyfill.d.ts.map +1 -0
- package/dist/lib/polyfill.js +14 -0
- package/dist/lib/polyfill.js.map +1 -0
- package/dist/lib/protocol-compat.d.ts +14 -0
- package/dist/lib/protocol-compat.d.ts.map +1 -0
- package/dist/lib/protocol-compat.js +67 -0
- package/dist/lib/protocol-compat.js.map +1 -0
- package/dist/lib/test-utils.d.ts +29 -0
- package/dist/lib/test-utils.d.ts.map +1 -0
- package/dist/lib/test-utils.js +184 -0
- package/dist/lib/test-utils.js.map +1 -0
- package/dist/lib/utils/comment-moderation.d.ts +4 -0
- package/dist/lib/utils/comment-moderation.d.ts.map +1 -0
- package/dist/lib/utils/comment-moderation.js +56 -0
- package/dist/lib/utils/comment-moderation.js.map +1 -0
- package/dist/lib/utils/index.d.ts +4 -0
- package/dist/lib/utils/index.d.ts.map +1 -0
- package/dist/lib/utils/index.js +4 -0
- package/dist/lib/utils/index.js.map +1 -0
- package/dist/lib/utils/utils.d.ts +23 -0
- package/dist/lib/utils/utils.d.ts.map +1 -0
- package/dist/lib/utils/utils.js +375 -0
- package/dist/lib/utils/utils.js.map +1 -0
- package/dist/lib/validator.d.ts +30 -0
- package/dist/lib/validator.d.ts.map +1 -0
- package/dist/lib/validator.js +307 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/stores/accounts/account-generator.d.ts +51 -0
- package/dist/stores/accounts/account-generator.d.ts.map +1 -0
- package/dist/stores/accounts/account-generator.js +160 -0
- package/dist/stores/accounts/account-generator.js.map +1 -0
- package/dist/stores/accounts/accounts-actions-internal.d.ts +8 -0
- package/dist/stores/accounts/accounts-actions-internal.d.ts.map +1 -0
- package/dist/stores/accounts/accounts-actions-internal.js +403 -0
- package/dist/stores/accounts/accounts-actions-internal.js.map +1 -0
- package/dist/stores/accounts/accounts-actions.d.ts +46 -0
- package/dist/stores/accounts/accounts-actions.d.ts.map +1 -0
- package/dist/stores/accounts/accounts-actions.js +1341 -0
- package/dist/stores/accounts/accounts-actions.js.map +1 -0
- package/dist/stores/accounts/accounts-database.d.ts +34 -0
- package/dist/stores/accounts/accounts-database.d.ts.map +1 -0
- package/dist/stores/accounts/accounts-database.js +685 -0
- package/dist/stores/accounts/accounts-database.js.map +1 -0
- package/dist/stores/accounts/accounts-store.d.ts +32 -0
- package/dist/stores/accounts/accounts-store.d.ts.map +1 -0
- package/dist/stores/accounts/accounts-store.js +169 -0
- package/dist/stores/accounts/accounts-store.js.map +1 -0
- package/dist/stores/accounts/index.d.ts +4 -0
- package/dist/stores/accounts/index.d.ts.map +1 -0
- package/dist/stores/accounts/index.js +4 -0
- package/dist/stores/accounts/index.js.map +1 -0
- package/dist/stores/accounts/utils.d.ts +49 -0
- package/dist/stores/accounts/utils.d.ts.map +1 -0
- package/dist/stores/accounts/utils.js +419 -0
- package/dist/stores/accounts/utils.js.map +1 -0
- package/dist/stores/authors-comments/authors-comments-store.d.ts +37 -0
- package/dist/stores/authors-comments/authors-comments-store.d.ts.map +1 -0
- package/dist/stores/authors-comments/authors-comments-store.js +338 -0
- package/dist/stores/authors-comments/authors-comments-store.js.map +1 -0
- package/dist/stores/authors-comments/index.d.ts +4 -0
- package/dist/stores/authors-comments/index.d.ts.map +1 -0
- package/dist/stores/authors-comments/index.js +4 -0
- package/dist/stores/authors-comments/index.js.map +1 -0
- package/dist/stores/authors-comments/utils.d.ts +14 -0
- package/dist/stores/authors-comments/utils.d.ts.map +1 -0
- package/dist/stores/authors-comments/utils.js +81 -0
- package/dist/stores/authors-comments/utils.js.map +1 -0
- package/dist/stores/comments/comments-store.d.ts +19 -0
- package/dist/stores/comments/comments-store.d.ts.map +1 -0
- package/dist/stores/comments/comments-store.js +385 -0
- package/dist/stores/comments/comments-store.js.map +1 -0
- package/dist/stores/comments/index.d.ts +4 -0
- package/dist/stores/comments/index.d.ts.map +1 -0
- package/dist/stores/comments/index.js +4 -0
- package/dist/stores/comments/index.js.map +1 -0
- package/dist/stores/communities/communities-store.d.ts +17 -0
- package/dist/stores/communities/communities-store.d.ts.map +1 -0
- package/dist/stores/communities/communities-store.js +304 -0
- package/dist/stores/communities/communities-store.js.map +1 -0
- package/dist/stores/communities/index.d.ts +4 -0
- package/dist/stores/communities/index.d.ts.map +1 -0
- package/dist/stores/communities/index.js +4 -0
- package/dist/stores/communities/index.js.map +1 -0
- package/dist/stores/communities-pages/communities-pages-store.d.ts +23 -0
- package/dist/stores/communities-pages/communities-pages-store.d.ts.map +1 -0
- package/dist/stores/communities-pages/communities-pages-store.js +316 -0
- package/dist/stores/communities-pages/communities-pages-store.js.map +1 -0
- package/dist/stores/communities-pages/index.d.ts +4 -0
- package/dist/stores/communities-pages/index.d.ts.map +1 -0
- package/dist/stores/communities-pages/index.js +4 -0
- package/dist/stores/communities-pages/index.js.map +1 -0
- package/dist/stores/feeds/feed-sorter.d.ts +5 -0
- package/dist/stores/feeds/feed-sorter.d.ts.map +1 -0
- package/dist/stores/feeds/feed-sorter.js +135 -0
- package/dist/stores/feeds/feed-sorter.js.map +1 -0
- package/dist/stores/feeds/feeds-store.d.ts +25 -0
- package/dist/stores/feeds/feeds-store.d.ts.map +1 -0
- package/dist/stores/feeds/feeds-store.js +459 -0
- package/dist/stores/feeds/feeds-store.js.map +1 -0
- package/dist/stores/feeds/index.d.ts +4 -0
- package/dist/stores/feeds/index.d.ts.map +1 -0
- package/dist/stores/feeds/index.js +4 -0
- package/dist/stores/feeds/index.js.map +1 -0
- package/dist/stores/feeds/utils.d.ts +43 -0
- package/dist/stores/feeds/utils.d.ts.map +1 -0
- package/dist/stores/feeds/utils.js +736 -0
- package/dist/stores/feeds/utils.js.map +1 -0
- package/dist/stores/replies/index.d.ts +4 -0
- package/dist/stores/replies/index.d.ts.map +1 -0
- package/dist/stores/replies/index.js +4 -0
- package/dist/stores/replies/index.js.map +1 -0
- package/dist/stores/replies/replies-comments-store.d.ts +8 -0
- package/dist/stores/replies/replies-comments-store.d.ts.map +1 -0
- package/dist/stores/replies/replies-comments-store.js +23 -0
- package/dist/stores/replies/replies-comments-store.js.map +1 -0
- package/dist/stores/replies/replies-store.d.ts +29 -0
- package/dist/stores/replies/replies-store.d.ts.map +1 -0
- package/dist/stores/replies/replies-store.js +413 -0
- package/dist/stores/replies/replies-store.js.map +1 -0
- package/dist/stores/replies/utils.d.ts +25 -0
- package/dist/stores/replies/utils.d.ts.map +1 -0
- package/dist/stores/replies/utils.js +549 -0
- package/dist/stores/replies/utils.js.map +1 -0
- package/dist/stores/replies-pages/index.d.ts +4 -0
- package/dist/stores/replies-pages/index.d.ts.map +1 -0
- package/dist/stores/replies-pages/index.js +4 -0
- package/dist/stores/replies-pages/index.js.map +1 -0
- package/dist/stores/replies-pages/replies-pages-store.d.ts +20 -0
- package/dist/stores/replies-pages/replies-pages-store.d.ts.map +1 -0
- package/dist/stores/replies-pages/replies-pages-store.js +270 -0
- package/dist/stores/replies-pages/replies-pages-store.js.map +1 -0
- package/dist/stores/replies-pages/utils.d.ts +3 -0
- package/dist/stores/replies-pages/utils.d.ts.map +1 -0
- package/dist/stores/replies-pages/utils.js +43 -0
- package/dist/stores/replies-pages/utils.js.map +1 -0
- package/dist/types.d.ts +638 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +160 -0
package/README.md
ADDED
|
@@ -0,0 +1,1365 @@
|
|
|
1
|
+
[](https://github.com/bitsocialnet/bitsocial-react-hooks/actions/workflows/CI.yml)
|
|
2
|
+
[](https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/scripts/write-coverage-badge.mjs)
|
|
3
|
+
[](https://github.com/bitsocialnet/bitsocial-react-hooks/blob/master/LICENSE)
|
|
4
|
+
[](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
|
+
```
|