diff --git a/docs/guides/js-waku/waku-forum-opchan.md b/docs/guides/js-waku/waku-forum-opchan.md new file mode 100644 index 0000000..b38d208 --- /dev/null +++ b/docs/guides/js-waku/waku-forum-opchan.md @@ -0,0 +1,557 @@ +--- +title: Build a Forum +sidebar_label: Build a Forum +description: Ship decentralized forum experiences over Waku using the OpChan React SDK. +--- + +`@opchan/react` adds a forum-focused state layer on top of `@opchan/core`, Wagmi, and the Waku network. + +## Why OpChan on Waku? + +- **Relevance-based sorting** – content scores combine engagement metrics (upvotes +1, comments +0.5), verification bonuses (ENS authors +25%, wallet-connected +10%), exponential time decay, and moderation penalties to surface quality discussions. +- **Forum-first primitives** – cells, posts, comments, votes, bookmarks, and moderation helpers. +- **Identity blending** – anonymous sessions, wallet accounts, ENS verified owners, and call signs share one cache. +- **Permission reasoning** – UI gates and string reasons with zero custom role logic. +- **Delegated signing** – short-lived browser keys prevent repetitive wallet prompts while keeping signatures verifiable. +- **Local-first sync** – IndexedDB hydrates instantly while Waku relays stream live updates in the background. + +## Prerequisites + +- React 18+ with a modern build tool (Vite examples below). +- Node.js 18+ and a package manager (npm or yarn). +- A WalletConnect / Reown project id (`VITE_REOWN_PROJECT_ID`) so users can connect wallets. +- Basic familiarity with [Waku content topics](/learn/concepts/content-topics) and message reliability concepts. + +## Quick Links + +- [Install the SDKs](#install-the-sdks) +- [Bootstrap the provider](#1-bootstrap-the-provider) +- [Hook surface overview](#2-understand-the-hook-surface) +- [Common UI patterns](#3-compose-ux-patterns) +- [Authentication flows](#4-authentication-flows) +- [Reference app (collapsible)](#6-reference-layout) +- [Troubleshooting](#8-troubleshooting) + +## Install the SDKs + +```mdx-code-block +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +``` + + + + +```bash +npm install @opchan/react react react-dom buffer +``` + + + + +```bash +yarn add @opchan/react react react-dom buffer +``` + + + + +For Vite + TypeScript projects also install `@vitejs/plugin-react`, `typescript`, and `@types/react`. + +## 1. Bootstrap the provider + +Add the Buffer polyfill (still required by many crypto libraries) and wrap your tree with `OpChanProvider`. The provider internally wires Wagmi, React Query, and the OpChan client to the Waku relay layer. Configure it with the content topic you want to publish under plus the reliable channel id you use for the [Waku Reliable Message Channel](https://specs.vac.dev/specs/waku/waku-reliable-message-delivery/). + +**Pick your own identifiers.** The content topic and reliable channel ID form the address of your forum data. The sample values below point to the public `https://opchan.app/` deployment; leave them as-is only if you want to read/write the shared data graph. For a separate app, use two unique values (for example `/myapp/1/forum/proto` and `myapp-forum`) and reuse them everywhere you run the OpChan SDK so all clients join the same silo. + +```tsx title="src/main.tsx" +import { createRoot } from 'react-dom/client'; +import { OpChanProvider } from '@opchan/react'; +import { Buffer } from 'buffer'; +import App from './App'; + +if (!(window as any).Buffer) { + (window as any).Buffer = Buffer; +} + +const config = { + // These defaults stream from the hosted https://opchan.app/ deployment; + // change both values to point the SDK at your own isolated data silo. + wakuConfig: { + contentTopic: '/opchan/1/messages/proto', + reliableChannelId: 'opchan-messages', + }, + reownProjectId: import.meta.env.VITE_REOWN_PROJECT_ID, +}; + +createRoot(document.getElementById('root')!).render( + + + , +); +``` + +**Understanding `reownProjectId`:** + +The `reownProjectId` is required by WalletConnect/Reown (formerly WalletConnect) to identify your application when users connect their wallets. Here's how it works: + +- **Why it's needed:** WalletConnect/Reown uses project IDs to manage application registrations, track usage, and provide secure wallet connection services. Without a valid project ID, wallet connection attempts will fail. + +- **How the value flows:** + 1. Set `VITE_REOWN_PROJECT_ID` in your `.env` file (e.g., `VITE_REOWN_PROJECT_ID=your-project-id-here`) + 2. Vite injects it at build time via `import.meta.env.VITE_REOWN_PROJECT_ID` + 3. The value is passed to `OpChanProvider`'s `config` object + 4. `OpChanProvider` internally forwards it to Wagmi's configuration, which initializes WalletConnect/Reown connectors + 5. When users click "Connect Wallet", WalletConnect/Reown uses this project ID to establish the connection + +- **Getting a project ID:** Register your application at [cloud.reown.com](https://cloud.reown.com) to receive a free project ID. This ID uniquely identifies your app in the WalletConnect network. + +:::tip +Keep secrets out of the bundle: reference the Reown id via an environment variable (`VITE_REOWN_PROJECT_ID`) rather than hard-coding it. +::: + +## 2. Understand the hook surface + +Most apps can reach for the high-level `useForum()` hook and destructure the four core stores it exposes. + +```tsx title="src/App.tsx" +import { useForum } from '@opchan/react'; + +const { user, content, permissions, network } = useForum(); +``` + +For bespoke logic, drop down to the individual hooks: + +| Hook | Purpose | Typical actions | +| --- | --- | --- | +| `useAuth()` | Manage sessions, ENS verification, call signs, and delegation | `connect()`, `startAnonymous()`, `delegate('7days')`, `updateProfile()` | +| `useContent()` | Read/write cells, posts, comments, votes, bookmarks | `createPost()`, `vote()`, `moderate.post()`, `pending.isPending(id)` | +| `usePermissions()` | Query derived capabilities and friendly denial reasons | `canCreateCell`, `canModerate(cellId)`, `check('canPost')` | +| `useNetwork()` | Reflect Waku connection status and hydration lifecycle | `isConnected`, `statusMessage`, `refresh()` | +| `useUserDisplay(address)` | Resolve ENS + call-sign metadata for any address | `displayName`, `ensAvatar`, `verificationStatus` | +| `useUIState(key, defaultValue, category?)` | Persist UI state to IndexedDB | `[value, setValue]` pair scoped by key | +| `useEthereumWallet()` / `useClient()` | Advanced access to Wagmi connectors or the raw `OpChanClient` | `signMessage()`, direct database interactions | + +### Sample snippets + +```tsx title="AuthButton.tsx" +import { useAuth } from '@opchan/react'; + +export function AuthButton() { + const { currentUser, connect, startAnonymous, disconnect } = useAuth(); + + if (currentUser) return ; + + return ( +
+ + +
+ ); +} +``` + +```tsx title="CreatePostForm.tsx" +import { useContent } from '@opchan/react'; + +export function CreatePostForm({ cellId }: { cellId: string }) { + const { createPost, pending } = useContent(); + const [title, setTitle] = useState(''); + const [body, setBody] = useState(''); + + const handleSubmit = async () => { + const post = await createPost({ cellId, title, content: body }); + if (post) { + setTitle(''); + setBody(''); + } + }; + + return ( +
{ event.preventDefault(); handleSubmit(); }}> + setTitle(event.target.value)} /> +