Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/accessibility/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Use `useAnnounceOnStateChange(message, { debounceMs, priority })` for transition

Use `useAnnounceOnShow(visible, message, { delayMs, priority })` for **transient surfaces that appear and disappear repeatedly** (modals, sheets, autocomplete pickers). It announces on each `visible: false → true` transition and resets on hide, so the next show re-announces. The two announcer hooks are not interchangeable: `useAnnounceOnStateChange` dedupes on string equality (correct for "AI is typing" → "AI is generating"), while `useAnnounceOnShow` dedupes on visibility transition (correct for "Suggestions available" each time the picker reopens with the same label). Pair with `useA11yLabel('a11y/…')` for the message so the announcement is i18n'd and gated on the SDK's a11y opt-in.

For incoming messages: use `useIncomingMessageAnnouncements({ channel, ownUserId, activeThreadId, threadList })`. It throttles to 1 announcement per second, batches multi-message bursts, and bounds memory at 500 announced ids.
For incoming messages: use `useIncomingMessageAnnouncements({ channel, ownUserId, activeThreadId })` (set `activeThreadId` in a thread's list; there is no `threadList` flag). It throttles to 1 announcement per second, batches multi-message bursts, and bounds memory at 500 announced ids.

### 3) Modal / sheet focus trap

Expand Down
24 changes: 16 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Targets iOS and Android.
- **Languages:** TypeScript + React Native
- **Runtime:** Node 24 (`.nvmrc` is `v24`; root `engines.node` is `>=22.13.0`; CI runs `24.x`)
- **Package manager:** Yarn 4.15.0 (Berry). The binary lives at `.yarn/releases/yarn-4.15.0.cjs` and is activated via `yarnPath` in `.yarnrc.yml`. Any globally installed `yarn` (even the Homebrew classic 1.x) acts only as a launcher — no Corepack required.
- **Workspaces:** single root `yarn.lock`; workspaces are `configs/typescript-config`, `package`, `package/native-package`, `package/expo-package`, `examples/SampleApp`, `examples/ExpoMessaging`. **No Lerna.**
- **Workspaces:** single root `yarn.lock`; workspaces are `configs/typescript-config`, `package`, `package/native-package`, `package/expo-package`, `examples/SampleApp`, `examples/ExpoMessaging`, `examples/ExpoChatGPTSample`. **No Lerna.**
- **Testing:** Jest with the `@react-native/jest-preset` + `@testing-library/react-native`.
- **Build:** `react-native-builder-bob` → CommonJS (`lib/commonjs`), ESM (`lib/module`), types (`lib/typescript`)
- **Lint/format:** ESLint 9 flat config + Prettier, strict (`--max-warnings 0`)
Expand All @@ -41,7 +41,7 @@ Respect repo-specific rules. Do not suppress lint rules broadly; justify and sco
- `native-package/` — bare RN wrapper (`stream-chat-react-native`)
- `expo-package/` — Expo wrapper (`stream-chat-expo`)
- `shared-native/{ios,android}` — native source shared by both wrappers; synced into them, never edited in place
- `examples/` — `SampleApp` (full-featured), `ExpoMessaging`
- `examples/` — `SampleApp` (full-featured), `ExpoMessaging`, `ExpoChatGPTSample` (AI components)
- `configs/typescript-config/` — shared `tsconfig` presets
- `ai-docs/` — agent-facing deep dives (see [References](#references))
- `perf/` — on-device performance toolkit (see [Accessibility, RTL & performance](#accessibility-rtl--performance))
Expand Down Expand Up @@ -123,15 +123,23 @@ yarn workspace sampleapp android
### Component hierarchy

```
<OverlayProvider> # gesture/overlay host, accessibility config
└─ <Chat client={client}> # root: SDK metadata, offline DB, subscriptions
<OverlayProvider> # gesture/overlay host, accessibility config
└─ <Chat client={client}> # root: SDK metadata, offline DB, subscriptions
├─ <ChannelList>
└─ <Channel> # state container: messages, threads, composer
├─ <MessageList>
├─ <MessageInput>
└─ <Thread>
├─ <Channel channel={channel}> # a channel's main list: lifecycle + config providers
│ ├─ <MessageList>
│ └─ <MessageComposer>
└─ <Thread thread={thread}> # a thread's replies, rendered next to <Channel>, not inside it
├─ <MessageList> # renders the parent message above the replies
└─ <MessageComposer>
```

`<Channel>` and `<Thread>` share one provider shell (`components/Channel/ChannelShell.tsx`) and take
the same configuration props; each owns only its own lifecycle. Only `<Thread>` provides
`ThreadContext`, and `useThreadContext().thread` is `undefined` outside it, which is how a component
knows it is not rendering a thread. Resolve the list's data with `useMessagePaginator()` /
`useChannel()` rather than branching on a thread yourself.

`<Chat>` is the entry point. It sets SDK metadata on the `stream-chat` client (identifier, device info), registers subscriptions for threads/polls/reminders (cleaned up on unmount), initializes `OfflineDB` when `enableOfflineSupport` is set, and wraps children in `ChatProvider` → `TranslationProvider` → `ThemeProvider`.

**Reconnect recovery is owned by the JS client, not this SDK.** `client.connectionRecovery` re-runs
Expand Down
4 changes: 2 additions & 2 deletions ai-docs/accessibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ Importable from `stream-chat-react-native`:
- `useA11yLabel(key, params)` — translated label or `undefined` when disabled.
- `useAnnounceOnStateChange(message, options)` — debounced live-region helper that announces on message **change** and dedupes consecutive identical strings (good for state-driven labels like loading/error transitions).
- `useAnnounceOnShow(visible, message, { delayMs?, priority? })` — announces on each `visible: false → true` transition and resets on hide, so re-shows re-announce. Pair with `useA11yLabel(...)` for the message. Used by `BottomSheetModal` and `AutoCompleteSuggestionList`.
- `useIncomingMessageAnnouncements({ channel, ownUserId, activeThreadId, threadList })` — throttled, batched announcement of new messages.
- `<NotificationAnnouncer />` — connection-state announcer (mounted by `<Channel>`).
- `useIncomingMessageAnnouncements({ channel, ownUserId, activeThreadId })` — throttled, batched announcement of new messages. Pass `activeThreadId` in a thread's list: it then announces that thread's replies only, and a channel list (no `activeThreadId`) announces top-level messages only.
- `<NotificationAnnouncer />` — connection-state announcer (mounted by `<Channel>` and `<Thread>`).

## Cross-SDK parity

Expand Down
Loading
Loading