2025-09-25 21:12:17 +05:30
|
|
|
## @opchan/react
|
|
|
|
|
|
|
|
|
|
Lightweight React provider and hooks for building OpChan clients on top of `@opchan/core`.
|
|
|
|
|
|
|
|
|
|
### Install
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm i @opchan/react @opchan/core react react-dom
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Quickstart
|
|
|
|
|
|
2025-09-25 21:25:32 +05:30
|
|
|
#### Basic Usage
|
|
|
|
|
|
2025-09-25 21:12:17 +05:30
|
|
|
```tsx
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import { createRoot } from 'react-dom/client';
|
|
|
|
|
import { OpChanProvider } from '@opchan/react';
|
2025-09-25 21:25:32 +05:30
|
|
|
import type { OpChanClientConfig } from '@opchan/core';
|
2025-09-25 21:12:17 +05:30
|
|
|
|
2025-09-25 21:25:32 +05:30
|
|
|
const config: OpChanClientConfig = {
|
|
|
|
|
ordiscanApiKey: 'YOUR_ORDISCAN_API_KEY',
|
2025-09-25 21:12:17 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Optional: bridge your wallet to OpChan
|
|
|
|
|
const walletAdapter = {
|
|
|
|
|
getAccount() {
|
|
|
|
|
// Return { address, walletType: 'bitcoin' | 'ethereum' } or null
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
onChange(cb) {
|
|
|
|
|
// Subscribe to wallet changes; return an unsubscribe function
|
|
|
|
|
return () => {};
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function App() {
|
|
|
|
|
return (
|
|
|
|
|
<OpChanProvider config={config} walletAdapter={walletAdapter}>
|
|
|
|
|
{/* your app */}
|
|
|
|
|
</OpChanProvider>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createRoot(document.getElementById('root')!).render(<App />);
|
|
|
|
|
```
|
|
|
|
|
|
2025-09-25 21:25:32 +05:30
|
|
|
#### (Suggested) With Reown AppKit Integration
|
|
|
|
|
|
|
|
|
|
Using Reown AppKit for wallet management:
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import { createRoot } from 'react-dom/client';
|
|
|
|
|
import { WagmiProvider } from 'wagmi';
|
|
|
|
|
import { AppKitProvider } from '@reown/appkit/react';
|
|
|
|
|
import { OpchanWithAppKit } from './providers/OpchanWithAppKit';
|
|
|
|
|
|
|
|
|
|
// Define your own config for networks, or use our by default (supports Bitcoin and Ethereum)
|
|
|
|
|
import { config, appkitConfig } from '@opchan/core';
|
|
|
|
|
|
|
|
|
|
const opchanConfig = { ordiscanApiKey: 'YOUR_ORDISCAN_API_KEY' };
|
|
|
|
|
|
|
|
|
|
function App() {
|
|
|
|
|
return (
|
|
|
|
|
<WagmiProvider config={config}>
|
|
|
|
|
<AppKitProvider {...appkitConfig}>
|
|
|
|
|
<OpchanWithAppKit config={opchanConfig}>
|
|
|
|
|
{/* your app */}
|
|
|
|
|
</OpchanWithAppKit>
|
|
|
|
|
</AppKitProvider>
|
|
|
|
|
</WagmiProvider>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createRoot(document.getElementById('root')!).render(<App />);
|
|
|
|
|
```
|
|
|
|
|
|
2025-09-25 21:12:17 +05:30
|
|
|
### Common usage
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { useForum } from '@opchan/react';
|
|
|
|
|
|
|
|
|
|
export function NewPostButton({ cellId }: { cellId: string }) {
|
|
|
|
|
const { user, content, permissions } = useForum();
|
|
|
|
|
|
|
|
|
|
const onCreate = async () => {
|
|
|
|
|
if (!permissions.canPost) return;
|
|
|
|
|
await content.createPost({ cellId, title: 'Hello', content: 'World' });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<button disabled={!permissions.canPost || !user.isAuthenticated} onClick={onCreate}>
|
|
|
|
|
New post
|
|
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { useAuth, useUserDisplay } from '@opchan/react';
|
|
|
|
|
|
|
|
|
|
export function Connect() {
|
|
|
|
|
const { currentUser, connect, disconnect, verifyOwnership } = useAuth();
|
|
|
|
|
const display = useUserDisplay(currentUser?.address ?? '');
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
{currentUser ? (
|
|
|
|
|
<>
|
|
|
|
|
<span>{display.displayName}</span>
|
|
|
|
|
<button onClick={() => verifyOwnership()}>Verify</button>
|
|
|
|
|
<button onClick={() => disconnect()}>Disconnect</button>
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<button
|
|
|
|
|
onClick={() =>
|
|
|
|
|
connect({ address: '0xabc...1234', walletType: 'ethereum' })
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
Connect
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### API
|
|
|
|
|
|
|
|
|
|
- **Providers**
|
|
|
|
|
- **`OpChanProvider`**: High-level provider that constructs an `OpChanClient` and wires persistence/events.
|
|
|
|
|
- Props:
|
|
|
|
|
- `config: OpChanClientConfig` — core client configuration.
|
|
|
|
|
- `walletAdapter?: WalletAdapter` — optional bridge to your wallet system.
|
|
|
|
|
- `children: React.ReactNode`.
|
|
|
|
|
- Types:
|
|
|
|
|
- `WalletAdapterAccount`: `{ address: string; walletType: 'bitcoin' | 'ethereum' }`.
|
|
|
|
|
- `WalletAdapter`:
|
|
|
|
|
- `getAccount(): WalletAdapterAccount | null`
|
|
|
|
|
- `onChange(cb: (a: WalletAdapterAccount | null) => void): () => void`
|
2025-09-25 21:25:32 +05:30
|
|
|
- **`OpchanWithAppKit`**: Convenience wrapper around `OpChanProvider` that integrates with Reown AppKit.
|
|
|
|
|
- Props:
|
|
|
|
|
- `config: OpChanClientConfig` — core client configuration.
|
|
|
|
|
- `children: React.ReactNode`.
|
|
|
|
|
- Automatically bridges AppKit wallet connections to OpChan's wallet adapter interface.
|
|
|
|
|
- Requires `WagmiProvider` and `AppKitProvider` from Reown AppKit as parent providers.
|
2025-09-25 21:12:17 +05:30
|
|
|
- **`ClientProvider`**: Low-level provider if you construct `OpChanClient` yourself.
|
|
|
|
|
- Props: `{ client: OpChanClient; children: React.ReactNode }`.
|
|
|
|
|
|
|
|
|
|
- **Hooks**
|
|
|
|
|
- **`useForum()`** → `{ user, content, permissions, network }` — convenience bundle of the hooks below.
|
|
|
|
|
|
|
|
|
|
- **`useAuth()`** → session & identity actions
|
|
|
|
|
- Data: `currentUser`, `verificationStatus`, `isAuthenticated`, `delegationInfo`.
|
|
|
|
|
- Actions: `connect({ address, walletType })`, `disconnect()`, `verifyOwnership()`,
|
|
|
|
|
`delegate(duration)`, `delegationStatus()`, `clearDelegation()`,
|
|
|
|
|
`updateProfile({ callSign?, displayPreference? })`.
|
|
|
|
|
|
|
|
|
|
- **`useContent()`** → forum data & actions
|
|
|
|
|
- Data: `cells`, `posts`, `comments`, `bookmarks`, `postsByCell`, `commentsByPost`,
|
|
|
|
|
`cellsWithStats`, `userVerificationStatus`, `lastSync`, `pending` helpers.
|
|
|
|
|
- Actions: `createCell({ name, description, icon? })`,
|
|
|
|
|
`createPost({ cellId, title, content })`,
|
|
|
|
|
`createComment({ postId, content })`,
|
|
|
|
|
`vote({ targetId, isUpvote })`,
|
|
|
|
|
`moderate.{post,unpost,comment,uncomment,user,unuser}(...)`,
|
|
|
|
|
`togglePostBookmark(post, cellId?)`, `toggleCommentBookmark(comment, postId?)`,
|
|
|
|
|
`removeBookmark(bookmarkId)`, `clearAllBookmarks()`, `refresh()`.
|
|
|
|
|
|
|
|
|
|
- **`usePermissions()`** → permission checks
|
|
|
|
|
- Booleans: `canPost`, `canComment`, `canVote`, `canCreateCell`, `canDelegate`.
|
|
|
|
|
- Functions: `canModerate(cellId)`, `check(action, cellId?) → { allowed, reason }`, `reasons`.
|
|
|
|
|
|
|
|
|
|
- **`useNetwork()`** → connection state
|
|
|
|
|
- Data: `isConnected`, `statusMessage`, `issues`, `canRefresh`.
|
|
|
|
|
- Actions: `refresh()` — triggers a light data refresh via core.
|
|
|
|
|
|
|
|
|
|
- **`useUIState(key, defaultValue, category?)`** → persisted UI state
|
|
|
|
|
- Returns `[value, setValue, { loading, error? }]`.
|
|
|
|
|
- Categories: `'wizardStates' | 'preferences' | 'temporaryStates'` (default `'preferences'`).
|
|
|
|
|
|
|
|
|
|
- **`useUserDisplay(address)`** → identity details for any address
|
|
|
|
|
- Returns `{ address, displayName, callSign?, ensName?, ordinalDetails?, verificationStatus, displayPreference, lastUpdated, isLoading, error }`.
|
|
|
|
|
- Backed by a centralized identity cache; updates propagate automatically.
|
|
|
|
|
|
|
|
|
|
- **`useClient()`** → access the underlying `OpChanClient` (advanced use only).
|
|
|
|
|
|
|
|
|
|
### Notes
|
|
|
|
|
|
|
|
|
|
- Identity resolution, verification states, and display preferences are centralized and cached;
|
|
|
|
|
`useUserDisplay` and `useAuth.verifyOwnership()` will keep store and local DB in sync.
|
|
|
|
|
- This package is UI-agnostic; pair with your component library of choice.
|
|
|
|
|
|
|
|
|
|
### License
|
|
|
|
|
|
|
|
|
|
MIT
|
|
|
|
|
|
|
|
|
|
|