### Getting Started with Hooks Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates basic usage of `useComment` and `useAccount` hooks from `@plebbit/plebbit-react-hooks`. ```JSX import {useComment, useAccount} from '@plebbit/plebbit-react-hooks' const account = useAccount() const comment = useComment({commentCid}) ``` -------------------------------- ### Install Project Dependencies (yarn) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md Installs all necessary project dependencies using the yarn package manager. ```bash yarn ``` -------------------------------- ### Start E2E Test Server (IPFS) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md Launches the necessary backend services for running end-to-end tests, including an IPFS node and test subplebbits. ```bash yarn test:server ``` -------------------------------- ### Installing Plebbit React Hooks with Yarn - sh Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/mock-content.md Installs the plebbit-react-hooks library directly from the GitHub repository using the Yarn package manager. This command adds the package as a dependency to your project. ```sh yarn add https://github.com/plebbit/plebbit-react-hooks ``` -------------------------------- ### Create Post/Comment using Callbacks (jsx) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates using the `usePublishComment` hook with callback functions (`onChallenge`, `onChallengeVerification`, `onError`) to handle the asynchronous publishing process, including user interaction for challenges, verification results, and errors. Shows examples for both creating a new post and replying to existing content. ```jsx const onChallenge = async (challenges: Challenge[], comment: Comment) => {\n let challengeAnswers: string[]\n try {\n // ask the user to complete the challenges in a modal window\n challengeAnswers = await getChallengeAnswersFromUser(challenges)\n }\n catch (e) {\n // if he declines, throw error and don't get a challenge answer\n }\n if (challengeAnswers) {\n // if user declines, publishChallengeAnswers is not called, retry loop stops\n await comment.publishChallengeAnswers(challengeAnswers)\n }\n}\n\nconst onChallengeVerification = (challengeVerification, comment) => {\n // if the challengeVerification fails, a new challenge request will be sent automatically\n // to break the loop, the user must decline to send a challenge answer\n // if the subplebbit owner sends more than 1 challenge for the same challenge request, subsequents will be ignored\n if (challengeVerification.challengeSuccess === true) {\n console.log('challenge success', {publishedCid: challengeVerification.publication.cid})\n }\n else if (challengeVerification.challengeSuccess === false) {\n console.error('challenge failed', {reason: challengeVerification.reason, errors: challengeVerification.errors})\n }\n}\n\nconst onError = (error, comment) => console.error(error)\n\nconst publishCommentOptions = {\n content: 'hello',\n title: 'hello',\n subplebbitAddress: '12D3KooW...',\n onChallenge,\n onChallengeVerification,\n onError\n}\n\nconst {index, state, publishComment} = usePublishComment(publishCommentOptions)\n\n// create post\nawait publishComment()\n// pending comment index\nconsole.log(index)\n// pending comment state\nconsole.log(state)\n\n// after publishComment is called, the account comment index gets defined\n// it is recommended to immediately redirect the user to a page displaying\n// the user's comment with a "pending" label\nif (index !== undefined) {\n history.push(`/profile/c/${index}`)\n // on the "pending" comment page, you can get the pending comment by doing\n // const accountComment = useAccountComment({commentIndex: index})\n // after accountComment.cid gets defined, it means the comment was published successfully\n // it is recommended to immediately redirect to `/p/${accountComment.subplebbitAddress}/c/${useAccountComment.cid}`\n}\n\n// reply to a post or comment\nconst publishReplyOptions = {\n content: 'hello',\n parentCid: 'Qm...', // the cid of the comment to reply to\n subplebbitAddress: '12D3KooW...',\n onChallenge,\n onChallengeVerification,\n onError\n}\nconst {publishComment} = usePublishComment(publishReplyOptions)\nawait publishComment()\n\n// when displaying replies, it is recommended to include the user's pending replies\n// https://github.com/plebbit/plebbit-react-hooks/#get-replies-to-a-post-nested (nested)\n// https://github.com/plebbit/plebbit-react-hooks/#get-replies-to-a-post-flattened-not-nested (not nested)\n ``` -------------------------------- ### Fetching Single Subplebbit Feed with State and Error Handling in React Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/clients.md This example demonstrates how to use `useFeed`, `useSubplebbit`, and `useFeedStateString` together to fetch a feed for a single subplebbit address ('memes.eth'). It shows how to get the feed data, check for more items, load more, retrieve the subplebbit's state, generate a state string using `useFeedStateString`, and handle potential errors using `useMemo`. ```js const subplebbitAddress = 'memes.eth' const {feed, hasMore, loadMore} = useFeed({subplebbitAddresses: [subplebbitAddress], sortType: 'topAll'}) const subplebbit = useSubplebbit({subplebbitAddress}) const stateString = useFeedStateString(subplebbitAddresses) const errorString = useMemo(() => { if (subplebbit?.state === 'failed') { let errorString = 'Failed fetching subplebbit' if (subplebbit.error) { errorString += `: ${error.toString().slice(0, 300)}` } return errorString } }, [subplebbit?.state]) if (stateString) { console.log(stateString) } if (errorString) { console.log(errorString) } ``` -------------------------------- ### Getting Author Profile Data and Comments (React Hooks) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates fetching author details, avatar, and comments using various hooks. It includes examples for displaying comments with infinite scroll using react-virtuoso, redirecting to the latest comment using react-router-dom, and filtering author comments. Requires a comment CID and author address. ```jsx // NOTE: you must have a comment cid from the author to load his profile page // e.g. the page url would be /#/u//c/ const authorResult = useAuthor({commentCid, authorAddress}) const {imageUrl} = useAuthorAvatar({author: authorResult.author}) const {authorComments, lastCommentCid, hasMore, loadMore} = useAuthorComments({commentCid, authorAddress}) // result if (authorResult.state === 'succeeded') { console.log('Succeeded getting author', authorResult.author) } if (state === 'failed') { console.log('Failed getting author', authorResult.error.message) } // listing the author comments with infinite scroll import {Virtuoso} from 'react-virtuoso' } useWindowScroll={true} components={{Footer: hasMore ? () => : undefined}} endReached={loadMore} increaseViewportBy={{bottom: 600, top: 600}} /> // it is recommended to always redirect the user to the last known comment cid // in case they want to share the url with someone, the author's comments // will load faster when using the last comment cid import {useParams} from 'react-router-dom' const params = useParams() useEffect(() => { if (lastCommentCid && params.comentCid !== lastCommentCid) { history.push(`/u/${params.authorAddress}/c/${lastCommentCid}`); } }, [lastCommentCid]) // search an author's comments const createSearchFilter = (searchTerm) => ({ filter: (comment) => comment.title?.includes(searchTerm) || comment.content?.includes(searchTerm), key: `includes-${searchTerm}` // required key to cache the filter }) const filter = createSearchFilter('bitcoin') const {authorComments, lastCommentCid, hasMore, loadMore} = useAuthorComments({commentCid, authorAddress, filter}) ``` -------------------------------- ### Compile Browser Tests (Watch Mode) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md Starts Webpack in watch mode to compile the browser-specific test files. This command is necessary to prepare the test environment for running E2E tests. ```bash yarn webpack:watch ``` -------------------------------- ### Get and Validate Post Data Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Explains how to fetch a post using `useComment`, validate the author address asynchronously with `useAuthorAddress`, and handle replies using `useReplies` and `useValidateComment`. ```JSX const post = useComment({commentCid}) // post.author.address should not be used directly, it needs to be verified asynchronously using useAuthorAddress const {authorAddress, shortAuthorAddress} = useAuthorAddress({comment: post}) // exception: when linking to an author profile page, /u/${comment.author.address}/c/${comment.cid} should be used, not useAuthorAddress({comment}).authorAddress // use many times in a page without affecting performance const post = useComment({commentCid, onlyIfCached: true}) // post.replies are not validated, to show replies const {replies, hasMore, loadMore} = useReplies({comment: post}) // to show a preloaded reply without rerenders, validate manually const {valid} = useValidateComment({comment: post.replies.pages.best.comments[0]}) if (valid === false) { // don't show this reply, it's malicious } // won't cause any rerenders if true ``` -------------------------------- ### Using useStateString with useSubplebbit Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/clients.md Example demonstrating how to fetch a subplebbit using the `useSubplebbit` hook and then utilize the `useStateString` hook to display its current state. It also includes logic to generate and log an error string if the subplebbit fetching fails. ```js const subplebbit = useSubplebbit({subplebbitAddress}) const stateString = useStateString(subplebbit) const errorString = useMemo(() => { if (subplebbit?.state === 'failed') { let errorString = 'Failed fetching subplebbit' if (subplebbit.error) { errorString += `: ${error.toString().slice(0, 300)}` } return errorString } }, [subplebbit?.state]) if (stateString) { console.log(stateString) } if (errorString) { console.log(errorString) } ``` -------------------------------- ### Edit Subplebbit Settings (Desktop) using Plebbit React Hooks Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Provides examples for editing subplebbit settings, including changing the ENS address and updating title/description, using `usePublishSubplebbitEdit`. It also shows how to verify the ENS resolution using `useResolvedSubplebbitAddress`. Callback functions for challenge handling, verification, and errors are included. ```jsx const onChallenge = async (challenges: Challenge[], subplebbitEdit: SubplebbitEdit) => { let challengeAnswers: string[] try { challengeAnswers = await getChallengeAnswersFromUser(challenges) } catch (e) {} if (challengeAnswers) { await subplebbitEdit.publishChallengeAnswers(challengeAnswers) } } const onChallengeVerification = (challengeVerification, subplebbitEdit) => { console.log('challenge verified', challengeVerification) } const onError = (error, subplebbitEdit) => console.error(error) // add ENS to your subplebbit const editSubplebbitOptions = { subplebbitAddress: '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z', // the previous address before changing it address: 'your-subplebbit-address.eth', // the new address to change to onChallenge, onChallengeVerification, onError } await publishSubplebbitEdit() // edit other subplebbit settings const editSubplebbitOptions = { subplebbitAddress: 'your-subplebbit-address.eth', // the address of the subplebbit to change title: 'Your title', description: 'Your description', onChallenge, onChallengeVerification, onError } const {publishSubplebbitEdit} = usePublishSubplebbitEdit(editSubplebbitOptions) await publishSubplebbitEdit() // verify if ENS was set correctly, use {cache: false} or it won't update const {resolvedAddress} = useResolvedSubplebbitAddress({subplebbitAddress: 'your-subplebbit-address.eth', cache: false}) // result if (state === 'succeeded') { console.log('Succeeded resolving address', resolvedAddress) console.log('ENS set correctly', resolvedAddress === subplebbit.signer.address) } if (state === 'failed') { console.log('Failed resolving address', error.message) } // pending if (state === 'resolving') { console.log('Resolving address from chain provider URL', chainProvider.urls) } ``` -------------------------------- ### Publishing Comment using usePublishComment Hook (JavaScript) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/clients.md This snippet shows how to initialize and use the `usePublishComment` hook to publish a comment. It defines the options for the hook, calls the hook to get the `publishComment` function and the `clients` object, initiates the publishing process, and then iterates through the states of different client types (chain providers, IPFS gateways, PubSub clients, and IPFS clients) to monitor the progress. ```JavaScript const publishCommentOptions = { content: 'hello', title: 'hello', subplebbitAddress: '12D3KooW...', onChallenge, onChallengeVerification, onError } const {clients, publishComment} = usePublishComment(publishCommentOptions) // start publishing await publishComment() // resolving subplebbit address from chain providers for (const chainProviderUrl in clients.chainProviders) { const chainProvider = clients.chainProviders[chainProviderUrl] if (chainProvider.state === 'resolving-address') { console.log(`Resolving subplebbit address from ${chainProviderUrl}`) } } // fetching from gateways for (const ipfsGatewayUrl in clients.ipfsGateways) { const ipfsGateway = clients.ipfsGateways[ipfsGatewayUrl] if (ipfsGateway.state === 'fetching-subplebbit-ipns') { console.log(`Fetching subplebbit IPNS from ${ipfsGatewayUrl}`) } } // publishing from pubsub client for (const pubsubClientUrl in clients.pubsubClients) { const pubsubClient = clients.pubsubClients[pubsubClientUrl] if (pubsubClient.state === 'publishing-challenge-request') { console.log(`Publishing challenge request using ${pubsubClientUrl}`) } if (pubsubClient.state === 'waiting-challenge') { console.log(`Waiting for challenge from ${pubsubClientUrl}`) } if (pubsubClient.state === 'publishing-challenge-answer') { console.log(`Publishing challenge answer using ${pubsubClientUrl}`) } if (pubsubClient.state === 'waiting-challenge-verification') { console.log(`Waiting for challenge verification from ${pubsubClientUrl}`) } if (pubsubClient.state === 'succeeded') { console.log(`Published comment using ${pubsubClientUrl}`) } } // fetching and publishing from ipfs client for (const ipfsClientUrl in clients.ipfsClients) { const ipfsClient = clients.ipfsClients[ipfsClientUrl] if (ipfsClient.state === 'fetching-subplebbit-ipns') { console.log(`Fetching subplebbit IPNS from ${ipfsClient.peers.length} peers`) } if (ipfsClient.state === 'fetching-subplebbit-ipfs') { console.log(`Fetching subplebbit IPFS from ${ipfsClient.peers.length} peers`) } if (ipfsClient.state === 'publishing-challenge-request') { console.log(`Publishing challenge request to ${ipfsClient.peers.length} peers`) } if (ipfsClient.state === 'waiting-challenge') { console.log(`Waiting for challenge from ${ipfsClient.peers.length} peers`) } if (ipfsClient.state === 'publishing-challenge-answer') { console.log(`Publishing challenge answer to ${ipfsClient.peers.length} peers`) } if (ipfsClient.state === 'waiting-challenge-verification') { console.log(`Waiting for challenge verification from ${ipfsClient.peers.length} peers`) } if (ipfsClient.state === 'succeeded') { console.log(`Published comment to ${ipfsClient.peers.length} peers`) } } ``` -------------------------------- ### Get Account Notifications (Plebbit React Hooks) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates how to fetch notifications for the active account or a specific account using `useNotifications`. It also shows how to mark notifications as read and how to get unread counts for all accounts using `useAccounts`. ```jsx const {notifications, markAsRead} = useNotifications() for (const notification of notifications) { console.log(notification) } await markAsRead() const johnsNotifications = useNotifications({accountName: 'John'}) for (const notification of johnsNotifications.notifications) { console.log(notification) } await johnsNotifications.markAsRead() // get the unread notification counts for all accounts const {accounts} = useAccounts() const accountsUnreadNotificationsCounts = accounts?.map(account => account.unreadNotificationCount) ``` -------------------------------- ### Get Active Account Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Shows how to retrieve the currently active account using the `useAccount` hook. A default account is created if none exists. ```JSX const account = useAccount() ``` -------------------------------- ### Get Short CID/Address - Plebbit React Hooks Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates how to obtain a shortened CID or address using the `useShortCid` and `useShortAddress` hooks provided by `@plebbit/plebbit-react-hooks`. ```jsx import {useShortAddress, useShortCid} from '@plebbit/plebbit-react-hooks' const shortParentCid = useShortCid(comment.parentCid) const shortAddress = useShortAddress(address) ``` -------------------------------- ### Compile TypeScript (Watch Mode) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md Starts the TypeScript compiler in watch mode. This command automatically recompiles TypeScript files whenever changes are detected, which is useful for development and preparing for E2E tests. ```bash yarn build:watch ``` -------------------------------- ### Using useStateString with usePublishComment Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/clients.md Example demonstrating how to publish a comment using the `usePublishComment` hook and then utilize the `useStateString` hook with the resulting account comment to display its publishing state. It also includes logic to generate and log an error string if the publishing process fails. ```js const {index} = usePublishComment(publishCommentOptions) const accountComment = useAccountComment({commentIndex: index}) const {publishingState, error} = accountComment const stateString = useStateString(accountComment) const errorString = useMemo(() => { if (publishingState === 'failed') { let errorString = 'Failed publishing comment' if (error) { errorString += `: ${error.toString().slice(0, 300)}` } return errorString } }, [publishingState]) if (stateString) { console.log(stateString) } if (errorString) { console.log(errorString) } ``` -------------------------------- ### Deleting an Account (JSX) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Provides examples for deleting the active account or a specific account by name using the `deleteAccount` hook. It includes a note warning that the action is unrecoverable and users should back up their accounts. ```jsx import {deleteAccount} from '@plebbit/plebbit-react-hooks' // delete active account await deleteAccount() // delete account by name await deleteAccount('Account 2') ``` -------------------------------- ### Fetching and Displaying Feed with React Virtuoso (JSX) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates fetching a feed using `useFeed`, integrating with `react-virtuoso` for virtualization, buffering feeds, and applying filters (search, image-only). It shows how to initialize a feed, handle pagination with `loadMore`, and apply custom filtering logic. ```jsx import {Virtuoso} from 'react-virtuoso' const {feed, hasMore, loadMore} = useFeed({subplebbitAddresses: ['memes.eth', '12D3KooW...', '12D3KooW...'], sortType: 'topAll'}) } useWindowScroll={true} components={{Footer: hasMore ? () => : undefined}} endReached={loadMore} increaseViewportBy={{bottom: 600, top: 600}} /> // you probably will want to buffer some feeds in the background so they are already loaded // when you need them useBufferedFeeds({ feedsOptions: [ {subplebbitAddresses: ['news.eth', 'crypto.eth'], sortType: 'new'}, {subplebbitAddresses: ['memes.eth'], sortType: 'topWeek'}, {subplebbitAddresses: ['12D3KooW...', '12D3KooW...', '12D3KooW...', '12D3KooW...'], sortType: 'hot'} ] }) // search a feed const createSearchFilter = (searchTerm) => ({ filter: (comment) => comment.title?.includes(searchTerm) || comment.content?.includes(searchTerm), key: `includes-${searchTerm}` // required key to cache the filter }) const filter = createSearchFilter('bitcoin') const {feed, hasMore, loadMore} = useFeed({subplebbitAddresses, filter}) // image only feed const filter = { filter: (comment) => getCommentLinkMediaType(comment?.link) === 'image', key: 'image-only' // required key to cache the filter' } const {feed, hasMore, loadMore} = useFeed({subplebbitAddresses, filter}) ``` -------------------------------- ### Create Subplebbit (Desktop) using Plebbit React Hooks Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Explains how to create a new subplebbit using `useCreateSubplebbit`. It includes options for creation, handling the result, and subsequently fetching the created subplebbit using `useAccountSubplebbits` or `useSubplebbit`. ```jsx const createSubplebbitOptions = {title: 'My subplebbit title'} const {createdSubplebbit, createSubplebbit} = useCreateSubplebbit(createSubplebbitOptions) await createSubplebbit() // it is recommended to redirect to `p/${createdSubplebbit.address}` after creation if (createdSubplebbit?.address) { console.log('created subplebbit with title', createdSubplebbit.title) history.push(`/p/${createdSubplebbit.address}`) } // after the subplebbit is created, fetch it using const {accountSubplebbits} = useAccountSubplebbits() const accountSubplebbitAddresses = Object.keys(accountSubplebbits) const subplebbits = useSubplebbits({subplebbitAddresses: accountSubplebbits}) // or const _subplebbit = useSubplebbit({subplebbitAddress: createdSubplebbit.address}) ``` -------------------------------- ### Get Short CID/Address - Plebbit JS (Note: Not React Hook) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Shows how to get a shortened CID or address using functions from the `@plebbit/plebbit-js` library. This functionality is not available directly within `@plebbit/plebbit-react-hooks`. ```jsx // NOTE: not possible to do from plebbit-react-hooks, needs plebbit-js import {getShortAddress, getShortCid} from '@plebbit/plebbit-js' const shortParentCid = getShortAddress(comment.parentCid) const shortAddress = getShortCid(address) ``` -------------------------------- ### Build Project Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md Builds the project for production or distribution. This command compiles the source code and prepares the final output files. ```bash yarn build ``` -------------------------------- ### Manage Accounts and Active Account Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Illustrates how to use `useAccount`, `useAccounts`, `createAccount`, and `setActiveAccount` to manage multiple accounts and switch the active one. ```JSX import {useAccount, useAccounts, createAccount, setActiveAccount} from '@plebbit/plebbit-react-hooks' const account = useAccount() const {accounts} = useAccounts() // on first render console.log(accounts.length) // 1 console.log(account.name) // 'Account 1' await createAccount() // create 'Account 2' await createAccount() // create 'Account 3' await setActiveAccount('Account 3') // on render after updates console.log(accounts.length) // 3 console.log(account.name) // 'Account 3' // you are now publishing from 'Account 3' because it is the active one const {publishComment} = usePublishComment(publishCommentOptions) await publishComment() ``` -------------------------------- ### Managing Subscriptions using useSubscribe and useFeed (React/JSX) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md This snippet illustrates how to subscribe and unsubscribe from a subplebbit using the `useSubscribe` hook. It shows how to check the subscription status and perform the actions. It also demonstrates how to retrieve the list of all subscriptions using the `useAccount` hook and fetch a content feed from subscribed subplebbits using the `useFeed` hook. ```jsx let subplebbitAddress = 'news.eth' subplebbitAddress = '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z' subplebbitAddress = 'tech.eth' const {subscribed, subscribe, unsubscribe} = useSubscribe({subplebbitAddress}) await subscribe() console.log(subscribed) // true // view subscriptions const account = useAccount() console.log(account.subscriptions) // ['news.eth', '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z', 'tech.eth'] // unsubscribe await unsubscribe() // get a feed of subscriptions const {feed, hasMore, loadMore} = useFeed({subplebbitAddresses: account.subscriptions, sortType: 'topAll'}) console.log(feed) ``` -------------------------------- ### Run Unit Tests (jest) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md Executes the project's unit tests using Jest and JSDOM. These tests utilize a mock of the plebbit-js library. ```bash yarn test ``` -------------------------------- ### Run End-to-End Tests (karma/mocha) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md Executes the project's end-to-end tests using Karma and Mocha. This requires the test server and compiled files to be running. ```bash yarn test:e2e ``` -------------------------------- ### Get and Validate Comment Data Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Shows how to fetch single or multiple comments using `useComment` and `useComments`, access content, and validate the author address using `useAuthorAddress`. ```JSX const comment = useComment({commentCid}) const {comments} = useComments({commentCids: [commentCid1, commentCid2, commentCid3]}) // content console.log(comment.content || comment.link || comment.title) // comment.author.address should not be used directly, it needs to be verified asynchronously using useAuthorAddress const {authorAddress, shortAuthorAddress} = useAuthorAddress({comment}) // exception: when linking to an author profile page, /u/${comment.author.address}/c/${comment.cid} should be used, not useAuthorAddress({comment}).authorAddress // use without affecting performance const {comments} = useComments({commentCids, onlyIfCached: true}) ``` -------------------------------- ### Get Author Avatar Image URL Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Details how to fetch the NFT avatar image URL for a comment author using `useAuthorAvatar` and handle different states of the asynchronous process. ```JSX const comment = useComment({commentCid}) // get the nft avatar image url of the comment author const {imageUrl, state, error, chainProvider, metadataUrl} = useAuthorAvatar({author: comment.author}) // result if (state === 'succeeded') { console.log('Succeeded getting avatar image URL', imageUrl) } if (state === 'failed') { console.log('Failed getting avatar image URL', error.message) } // pending if (state === 'fetching-owner') { console.log('Fetching NFT owner address from chain provider', chainProvider.urls) } if (state === 'fetching-uri') { console.log('Fetching NFT URI from chain provider URL', chainProvider.urls) } if (state === 'fetching-metadata') { console.log('Fetching NFT URI from', metadataUrl) } ``` -------------------------------- ### Create Post/Comment using Hook State (jsx) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Illustrates using the `usePublishComment` hook by directly accessing the state variables it returns (`index`, `state`, `challenge`, `challengeVerification`, `error`) to manage the publishing flow and react to different states without explicit callbacks. ```jsx const publishCommentOptions = {\n content: 'hello',\n title: 'hello',\n subplebbitAddress: '12D3KooW...',\n}\n\nconst {index, state, publishComment, challenge, challengeVerification, publishChallengeAnswers, error} = usePublishComment(publishCommentOptions)\n\nif (challenge) {\n // display challenges to user and call publishChallengeAnswers(challengeAnswers)\n}\n\nif (challengeVerification) {\n // display challengeVerification.challengeSuccess to user\n // redirect to challengeVerification.publication.cid\n}\n\nif (error) {\n // display error to user\n}\n\n// after publishComment is called, the account comment index gets defined\n// it is recommended to immediately redirect the user to a page displaying\n// the user's comment with a "pending" label\nif (index !== undefined) {\n history.push(`/profile/c/${index}`)\n // on the "pending" comment page, you can get the pending comment by doing\n // const accountComment = useAccountComment({commentIndex: index})\n // after accountComment.cid gets defined, it means the comment was published successfully\n // it is recommended to immediately redirect to `/p/${accountComment.subplebbitAddress}/c/${useAccountComment.cid}`\n}\n\n// create post\nawait publishComment()\n ``` -------------------------------- ### Defining Replies and Comments Stores - Conceptual Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/algorithms.md This snippet defines the structure and actions available within the repliesStore, commentsStore, and repliesPagesStore. It outlines the state properties and the functions (actions) used to manipulate the data within each store. ```Conceptual repliesStore { feedsOptions: FeedsOptions bufferedFeeds: Feeds bufferedPostsCounts: {[subplebbitAddress+sortType: string]: number} loadedFeeds: Feeds feedsHaveMore: {[feedName: string]: boolean} // actions addFeedToStore: (feedName: string, ...feedOptions: FeedOptions) => void incrementFeedPageNumber: (feedName: string) => void // recalculate all feeds using new subplebbits.post.pages, subplebbitsPagesStore and page numbers updateFeeds: () => void } commentsStore { comments: Comments // actions addCommentToStore: (commentCid: string) => void } repliesPagesStore { repliesPages // actions // a comment instance only knows its first page CID, so take the first page CID as an argument // and scroll through every replies next page in the store until you find the last page, then add it addNextRepliesPageToStore: (repliesFirstPageCid: string) => void } ``` -------------------------------- ### Get Flattened Post Replies - Plebbit React Hooks Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates how to fetch replies to a specific post using `useComment` and `useAccountComments`, flatten the nested replies, merge them with pending account replies, and sort them by timestamp. Requires `@plebbit/plebbit-react-hooks` and its internal utils. ```jsx import {useComment, useAccountComments} from '@plebbit/plebbit-react-hooks' import {flattenCommentsPages} from '@plebbit/plebbit-react-hooks/dist/lib/utils' // the post const comment = useComment({commentCid}) // the default way to display replies is nested, flatten (unnest) them instead const flattenedReplies = useMemo(() => flattenCommentsPages(comment.replies), [comment.replies]) // the account replies to the post (commentCid) and account replies to all the post's replies const postAndRepliesCids = useMemo(() => new Set([(commentCid || 'n/a'), ...flattenedReplies.map(reply => reply.cid)]), [commentCid, flattenedReplies]) const filter = useCallback((accountComment) => postAndRepliesCids.has(accountComment.parentCid), [postAndRepliesCids]) const {accountComments} = useAccountComments({filter}) // the account's replies have a delay before getting published, so get them locally from accountComments instead const accountRepliesNotYetInCommentReplies = useMemo(() => { const commentReplyCids = new Set(flattenedReplies.map(reply => reply.cid)) // filter out the account comments already in comment.replies, so they don't appear twice return accountComments.filter(accountReply => !commentReplyCids.has(accountReply.cid)) }, [flattenedReplies, accountComments]) // merge the not yet published account replies and published replies, and sort them by timestamp const sortedReplies = useMemo(() => [...accountRepliesNotYetInCommentReplies, ...flattenedReplies].sort((a, b) => a.timestamp - b.timestamp), [accountRepliesNotYetInCommentReplies, flattenedReplies]) for (const reply of sortedReplies) { // account replies can have reply.state 'pending' or 'failed' when they are not published yet // it is recommended to add a 'pending' or 'failed' label to these replies console.log(reply) } ``` -------------------------------- ### Export and Import Account using Plebbit React Hooks Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates how to export the current active account's data using `exportAccount`, import account data using `importAccount`, set an account as active using `setActiveAccount`, and reorder the list of accounts using `setAccountsOrder`. ```jsx import {exportAccount, importAccount, setActiveAccount, setAccountsOrder} from '@plebbit/plebbit-react-hooks' // get active account 'Account 1' const activeAccount = useAccount() // export active account, tell user to copy or download this json const activeAccountJson = await exportAccount() // import account await importAccount(activeAccountJson) // get imported account 'Account 1 2' (' 2' gets added to account.name if account.name already exists) const importedAccount = useAccount('Account 1 2') // make imported account active account await setActiveAccount('Account 1 2') // reorder the accounts list await setAccountsOrder(['Account 1 2', 'Account 1']) ``` -------------------------------- ### Fetching Subplebbit Data and Posts (React Hooks) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Shows how to retrieve subplebbit information, statistics, and feeds using dedicated hooks. It covers fetching single or multiple subplebbits, including an option for cached-only retrieval, and validating individual posts for safety. Requires a subplebbit address. ```jsx const subplebbit = useSubplebbit({subplebbitAddress}) const subplebbitStats = useSubplebbitStats({subplebbitAddress}) const {subplebbits} = useSubplebbits({subplebbitAddresses: [subplebbitAddress, subplebbitAddress2, subplebbitAddress3]}) // use without affecting performance const {subplebbits} = useSubplebbits({subplebbitAddresses: [subplebbitAddress, subplebbitAddress2, subplebbitAddress3], onlyIfCached: true}) // subplebbit.posts are not validated, to show posts const {feed, hasMore, loadMore} = useFeed({subplebbitAddresses: [subplebbitAddress]}) // to show a preloaded post without rerenders, validate manually const {valid} = useValidateComment({comment: subplebbit.posts.pages.topAll.comments[0]}) if (valid === false) { // don't show this post, it's malicious } // won't cause any rerenders if true ``` -------------------------------- ### Checking Specific Comment Edit Property Status (Plebbit React Hooks) Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md This example illustrates how to use the useEditedComment hook to inspect the status of edits for individual properties of a comment, such as 'removed'. It also shows how to access the fully edited comment object and the overall state of all edits. ```jsx const comment = useComment({commentCid}) const editedComment = useEditedComment({comment}) if (editedComment.failedEdits.removed !== undefined) { console.log('failed editing comment.removed property') } if (editedComment.succeededEdits.removed !== undefined) { console.log('succeeded editing comment.removed property') } if (editedCommentResult.pendingEdits.removed !== undefined) { console.log('pending editing comment.removed property') } // view the full comment with all edited properties (both succeeded and pending) console.log(editedComment.editedComment) // view the state of all edits of the comment console.log(editedComment.state) // 'unedited' | 'succeeded' | 'pending' | 'failed' ``` -------------------------------- ### Defining Feed and Subplebbit Stores - TypeScript Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/algorithms.md Defines the structure and key properties/actions for the feedsStore, subplebbitsStore, and subplebbitsPagesStore used in the application's state management. These stores manage feed configuration, buffered and loaded feed data, post counts per subplebbit/sort type, subplebbit instances, and fetched subplebbit pages, including actions for adding data and updating feeds based on new information. ```TypeScript feedsStore { feedsOptions: FeedsOptions bufferedFeeds: Feeds bufferedPostsCounts: {[subplebbitAddress+sortType: string]: number} loadedFeeds: Feeds feedsHaveMore: {[feedName: string]: boolean} // actions addFeedToStore: (feedName: string, ...feedOptions: FeedOptions) => void incrementFeedPageNumber: (feedName: string) => void // recalculate all feeds using new subplebbits.post.pages, subplebbitsPagesStore and page numbers updateFeeds: () => void } subplebbitsStore { subplebbits: Subplebbits // actions addSubplebbitToStore: (subplebbitAddress: string) => void } subplebbitsPagesStore { subplebbitsPages // actions // a subplebbit instance only knows its first page CID, so take the first page CID as an argument // and scroll through every subplebbit next page in the store until you find the last page, then add it addNextSubplebbitPageToStore: (subplebbitFirstPageCid: string) => void } ``` -------------------------------- ### Utility Functions Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Provides utility functions for managing the plebbit-js library instance and clearing local databases/caches. ```TypeScript setPlebbitJs(PlebbitJs) // set which plebbit-js version to use, e.g. to mock content for frontend dev or to use the node version in Electron deleteDatabases() // delete all databases, including all caches and accounts data deleteCaches() // delete the cached comments, cached subplebbits and cached pages only, no accounts data ``` -------------------------------- ### Hook useStateString in React Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/clients.md Defines the `useStateString` React hook. This hook takes a comment or subplebbit object, uses `useClientsStates` to get state information from various clients, and formats it into a single descriptive string. It handles different state types and provides a fallback to object-level states if no client states are available. ```js import { useMemo } from 'react' import { useClientsStates } from '@plebbit/plebbit-react-hooks' const clientHosts = {} const getClientHost = (clientUrl) => { if (!clientHosts[clientUrl]) { try { clientHosts[clientUrl] = new URL(clientUrl).hostname || clientUrl } catch (e) { clientHosts[clientUrl] = clientUrl } } return clientHosts[clientUrl] } const useStateString = (commentOrSubplebbit) => { const { states } = useClientsStates({ comment: commentOrSubplebbit }) return useMemo(() => { let stateString = '' for (const state in states) { const clientUrls = states[state] const clientHosts = clientUrls.map((clientUrl) => getClientHost(clientUrl)) // if there are no valid hosts, skip this state if (clientHosts.length === 0) { continue } // separate 2 different states using ' ' if (stateString) { stateString += ', ' } // e.g. 'fetching IPFS from cloudflare-ipfs.com, ipfs.io' const formattedState = state.replaceAll('-', ' ').replace('ipfs', 'IPFS').replace('ipns', 'IPNS') stateString += `${formattedState} from ${clientHosts.join(', ')}` } // fallback to comment or subplebbit state when possible if (!stateString && commentOrSubplebbit?.state !== 'succeeded') { if (commentOrSubplebbit?.publishingState && commentOrSubplebbit?.publishingState !== 'stopped' && commentOrSubplebbit?.publishingState !== 'succeeded') { stateString = commentOrSubplebbit.publishingState } else if (commentOrSubplebbit?.updatingState !== 'stopped' && commentOrSubplebbit?.updatingState !== 'succeeded') { stateString = commentOrSubplebbit.updatingState } if (stateString) { stateString = stateString.replaceAll('-', ' ').replace('ipfs', 'IPFS').replace('ipns', 'IPNS') } } // capitalize first letter if (stateString) { stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1) } // if string is empty, return undefined instead return stateString === '' ? undefined : stateString }, [states, commentOrSubplebbit]) } export default useStateString ``` -------------------------------- ### Configuring Mock Content for Plebbit React Hooks - sh Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/mock-content.md Adds environment variables to enable mock content functionality in plebbit-react-hooks. REACT_APP_PLEBBIT_REACT_HOOKS_MOCK_CONTENT enables mocking, and REACT_APP_PLEBBIT_REACT_HOOKS_MOCK_CONTENT_LOADING_TIME sets a delay for mock data loading in milliseconds. ```sh REACT_APP_PLEBBIT_REACT_HOOKS_MOCK_CONTENT=1 REACT_APP_PLEBBIT_REACT_HOOKS_MOCK_CONTENT_LOADING_TIME=1000 ``` -------------------------------- ### List Owned Subplebbits (Desktop) using Plebbit React Hooks Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Demonstrates how to retrieve a list of subplebbits owned by the current account using `useAccountSubplebbits` and `useSubplebbits`. ```jsx const {accountSubplebbits} = useAccountSubplebbits() const ownerSubplebbitAddresses = Object.keys(accountSubplebbits).map(subplebbitAddress => accountSubplebbits[subplebbitAddress].role === 'owner') const subplebbits = useSubplebbits({subplebbitAddresses: ownerSubplebbitAddresses}) ``` -------------------------------- ### List of Actions Without Hooks Implementations Source: https://github.com/plebbit/plebbit-react-hooks/blob/master/README.md Lists functions available in the plebbit-react-hooks library that do not currently have corresponding React hook implementations. ```TypeScript createAccount(account: Account) deleteAccount(accountName: string) setAccount(account: Account) setActiveAccount(accountName: string) setAccountsOrder(accountNames: string[]) importAccount(serializedAccount: string) exportAccount(accountName: string): string // don't allow undefined to prevent catastrophic bugs deleteSubplebbit(subplebbitAddress: string, accountName?: string) ```