diff --git a/ai-docs/ai-migration.md b/ai-docs/ai-migration.md new file mode 100644 index 0000000000..fe270625da --- /dev/null +++ b/ai-docs/ai-migration.md @@ -0,0 +1,708 @@ +# stream-chat-react-native v8 → v9 — Agent Migration Guide + +> Machine-oriented migration reference for AI coding agents. The prose-heavy +> human version lives at +> https://getstream.io/chat/docs/sdk/react-native/basics/upgrading-from-v8/ +> — do not load it for agent-driven migrations; this file supersedes it. + +## 0. For the agent (read first) + +1. **Your training data predates v9.** Do not rely on memory for v9 symbols, prop + shapes, or export paths. Always verify against the installed SDK source. +2. **Package name in `node_modules/` is `stream-chat-react-native-core`.** The + published packages are: + + - `stream-chat-react-native-core` — the actual SDK; ships `src/` + `lib/`. + - `stream-chat-react-native` — bare-RN wrapper; peer-deps on core. + - `stream-chat-expo` — Expo wrapper; peer-deps on core. + + Every source path below is rooted at + `node_modules/stream-chat-react-native-core/src/`. Do **not** look under + `node_modules/stream-chat-react-native/src/` for SDK source — that's the + wrapper, not the SDK. + +3. **Order of work.** Do §2 (prereqs) → §3 (big-3 structural moves) → §4/§5 + (leaf renames + replacements) → §6 (behavior changes) → §9 (verify). + Many leaf renames disappear automatically once §3.1 (`WithComponents`) + lands, so doing §3 first saves churn. +4. **Detect before editing.** Run §1 first. Skip any section whose patterns + don't match the customer codebase. +5. **When uncertain, read source.** §8 lists the files to consult. If a symbol + is not in this guide, read the public barrel + (`node_modules/stream-chat-react-native-core/src/index.ts`) — do not guess. + +## 1. Detection (run first) + +Run each ripgrep against the customer's app source directory (replace `src/` +with their source root). Zero hits = skip the corresponding section. + +```bash +# §3.1 — WithComponents migration needed? +rg '<(Channel|ChannelList|Chat|Thread)\s[^>]*\b([A-Z]\w+)=\{' src/ + +# §3.2 — five breaking component renames +rg '\b(MessageSimple|MessageAvatar|ChannelListMessenger|ChannelPreviewMessenger)\b' src/ +# MessageInput is special: the component was renamed, but MessageInputContext, +# useMessageInputContext, and the MessageInput*View helpers were NOT. Filter +# the noise so only real component usages remain: +rg '\bMessageInput\b' src/ | rg -v 'MessageInputContext|MessageInputHeaderView|MessageInputFooterView|MessageInputLeadingView|MessageInputTrailingView|useMessageInputContext|MessageInputContextValue' + +# §3.3 — inverted audio semantics +rg '\basyncMessagesMultiSendEnabled\b' src/ + +# §4.2 — hook renames +rg '\buseAudioController\b' src/ +# useMutedUsers is scope-split: only rename Chat-level uses (ones that take a +# StreamChat client arg). Review each match against §4.2 before editing. +rg '\buseMutedUsers\b' src/ + +# §5 — removed components with replacements +rg '\b(AttachmentActions|AttachmentUploadProgressIndicator|Card|CardCover|CardFooter|CardHeader|ImageReloadIndicator|MessagePreview|imageGalleryCustomComponents)\b' src/ +rg '\b(CameraSelectorIcon|FileSelectorIcon|ImageSelectorIcon|VideoRecorderSelectorIcon)\b' src/ + +# §6 — behavior / contract changes +rg '\b(latestMessagePreview|deletedMessagesVisibilityType|messageContentWidth|setMessageContentWidth|legacyImageViewerSwipeBehaviour)\b' src/ +# Custom MessageMenu override is silently dead in v9 — flag any. +rg '\boverrides=\{[^}]*\bMessageMenu\b' src/ +rg '\bMessageMenu=\{' src/ + +# Theme — v8 namespace names that moved +rg "\b(messageInput|messageSimple|channelListMessenger)\s*:" src/ + +# Semantic token renames +rg '\b(backgroundCoreSurface|badgeTextInverse|textInverse|backgroundCoreElevation4)\b' src/ + +# Icon removals / renames +rg '\b(CircleStop|Refresh|Close|User|MessageIcon|ArrowRight|ArrowLeft|Attach|ChatIcon|CheckSend|Folder|MenuPointVertical|Notification|PinHeader|LOLReaction|LoveReaction|ThumbsUpReaction|ThumbsDownReaction|WutReaction)\b' src/ +``` + +## 2. Prerequisites (hard blockers — fail fast) + +- **React Native New Architecture is required.** RN 0.76+ or an Expo SDK that + defaults to the new arch. If the app is on the old architecture, stop and + tell the user to migrate the arch first. +- **New required peer dependency.** Run `yarn add react-native-teleport` (or + the npm equivalent). Minimum version `0.5.4`. +- **Keyboard handling cleanup.** Remove every one of these v8 workarounds + before running the app: + - Negative `keyboardVerticalOffset` values (e.g. `-300`) on any screen that + renders `MessageComposer`. Set it to the navigation header height instead + (same value on iOS and Android). + - `SafeAreaView` wrappers placed around `MessageComposer` purely to fix + bottom spacing. `MessageComposer` handles its own safe-area in v9. + - Manual Android IME padding hacks that pushed the composer above the + keyboard. + +## 3. The big 3 structural migrations (do these first, in order) + +### 3.1 Component override props → `` + +Every `ComponentType` prop on `Channel`, `ChannelList`, `Chat`, and `Thread` +was removed. They are now provided via a single `` wrapper and +read with `useComponentsContext()`. + +**Before (v8):** + +```tsx + + + + + + + + +``` + +**After (v9):** + +```tsx + + + + + + + + + + +``` + +Key rules: + +- `WithComponents` nests. Inner overrides merge over outer ones (closest wins). +- Two prop keys were renamed when moving into `overrides`: + - `Chat` prop `LoadingIndicator` -> overrides key `ChatLoadingIndicator` + - `Thread` prop `MessageComposer` -> overrides key `ThreadMessageComposer` +- If a custom component used to read other components from a specific context, + switch to `useComponentsContext()`: + + ```tsx + // v8 + const { MessageItemView } = useMessagesContext(); + const { SendButton } = useMessageInputContext(); + const { Preview } = useChannelsContext(); + + // v9 + const { MessageItemView, SendButton, Preview } = useComponentsContext(); + ``` + +- For the full list of overridable keys, read + `node_modules/stream-chat-react-native-core/src/contexts/componentsContext/defaultComponents.ts`. + The `DEFAULT_COMPONENTS` map is the source of truth; `ComponentOverrides` is + derived from it. **Do not use a hardcoded list from your training data.** + +### 3.2 Five breaking component renames + +Pure symbol renames. Apply with find-and-replace (whole-word, case-sensitive). +Each rename also carries a prop-type rename and, in some cases, a theme +namespace rename. + +Components + props: + +- `MessageSimple` -> `MessageItemView` + - `MessageSimpleProps` -> `MessageItemViewProps` + - `MessageSimplePropsWithContext` -> `MessageItemViewPropsWithContext` + - Theme namespace: `messageSimple` -> `messageItemView` + - Test ID: `message-simple-wrapper` -> `message-item-view-wrapper` +- `MessageAvatar` -> `MessageAuthor` + - `MessageAvatarProps` -> `MessageAuthorProps` + - `MessageAvatarPropsWithContext` -> `MessageAuthorPropsWithContext` + - Theme sub-key: `messageItemView.avatarWrapper` -> `messageItemView.authorWrapper` + - Test ID: `message-avatar` -> `message-author` +- `MessageInput` -> `MessageComposer` + - `MessageInputProps` -> `MessageComposerProps` + - Theme namespace: `messageInput` -> `messageComposer` + - `additionalMessageInputProps` -> `additionalMessageComposerProps` + - `Thread` prop `MessageInput` -> overrides key `ThreadMessageComposer` (see §3.1) +- `ChannelListMessenger` -> `ChannelListView` + - `ChannelListMessengerProps` -> `ChannelListViewProps` + - Theme namespace: `channelListMessenger` -> `channelListView` + - Test ID: `channel-list-messenger` -> `channel-list-view` +- `ChannelPreviewMessenger` -> `ChannelPreviewView` + - `ChannelPreviewMessengerProps` -> `ChannelPreviewViewProps` + - Theme namespace for `channelPreview` is **unchanged** (stays `channelPreview`). + - Test ID `channel-preview-button` is **unchanged**. + - `` becomes the `ChannelPreview` + overrides key on ``. + +**Do NOT rename these (common over-migration traps):** + +- `MessageInputContext`, `useMessageInputContext`, `MessageInputContextValue` — + the **component** was renamed, the context was not. +- The `MessageInput/` source folder name. Stays as `MessageInput/`. +- `MessageInputHeaderView`, `MessageInputFooterView`, `MessageInputLeadingView`, + `MessageInputTrailingView` — these helpers keep their names. +- `channelPreview` theme namespace and `channel-preview-button` test ID. +- `MessagePinnedHeader` — still exists; just now rendered inside the + consolidated `MessageHeader` flow. + +### 3.3 Audio recording: semantics inverted + +`asyncMessagesMultiSendEnabled` was removed and replaced with +`audioRecordingSendOnComplete`. This is **not a rename** — the boolean meaning +is inverted **and** the default changed. + +Rewrite rule: + +- v8 `asyncMessagesMultiSendEnabled={true}` ≡ v9 `audioRecordingSendOnComplete={false}` +- v8 `asyncMessagesMultiSendEnabled={false}` ≡ v9 `audioRecordingSendOnComplete={true}` +- **Default behavior flipped.** Both defaults are the literal boolean `true`, + but the semantics are inverted — so the runtime behavior changed: + - v8 default `asyncMessagesMultiSendEnabled=true` → recordings **stayed** in + the composer. + - v9 default `audioRecordingSendOnComplete=true` → recordings **send + immediately**. + - If v8 code omitted the prop (relying on the default), pass + `audioRecordingSendOnComplete={false}` in v9 to preserve the old UX. + +Semantics in plain terms: + +- v9 `audioRecordingSendOnComplete={true}` — upload completes → send immediately. +- v9 `audioRecordingSendOnComplete={false}` — upload completes → stays in composer + so the user can add text or more attachments before sending. + +Apply on `Channel`, `MessageComposer`, and any direct +`useMessageInputContext()` consumer. Also update direct +`uploadVoiceRecording(sendOnComplete)` callsites: the boolean now carries +`sendOnComplete` semantics, not the old `multiSendEnabled` semantics. + +## 4. Rename lists (bullets, not tables) + +### 4.1 Components (rename or removed-with-1:1-replacement) + +- `MessageSimple` -> `MessageItemView` +- `MessageAvatar` -> `MessageAuthor` +- `MessageInput` -> `MessageComposer` +- `ChannelListMessenger` -> `ChannelListView` +- `ChannelPreviewMessenger` -> `ChannelPreviewView` +- `AudioAttachment` — **not renamed.** Source folder moved from + `Attachment/AudioAttachment/` to `Attachment/Audio/`, but the public export + name and the `ComponentOverrides` key are both still `AudioAttachment`. + Do not rename import sites or override keys. +- `Card` -> `UrlPreview` (new props type `URLPreviewProps`; see §5) +- `Avatar` (numeric `size`) -> `Avatar` (string enum `'xs'..'2xl'`); prop `image` -> `imageUrl`; `name` -> `placeholder`; `online`/`presenceIndicator` -> separate `OnlineIndicator` component +- `GroupAvatar` -> `AvatarGroup` / `AvatarStack` +- `CameraSelectorIcon` -> `AttachmentTypePickerButton` +- `FileSelectorIcon` -> `AttachmentTypePickerButton` +- `ImageSelectorIcon` -> `AttachmentTypePickerButton` +- `VideoRecorderSelectorIcon` -> `AttachmentTypePickerButton` +- `CreatePollIcon` -> (no replacement; poll creation is internal now) +- `MessageMenu` -> split into `MessageReactionPicker` + `MessageActionList` + + `MessageActionListItem` + `MessageUserReactions` (see §6) +- `MessageMenuProps` -> no direct replacement (customize the four components + above individually) +- `ChannelAvatar` (legacy) -> `ChannelAvatar` from `ui/Avatar/` (different API; `size='xl'` default) +- `PreviewAvatar` -> `ChannelAvatar` with `size='xl'` + +### 4.2 Hooks + +- `useAudioController` -> `useAudioRecorder` +- `useMutedUsers` (Chat-level, called with a `StreamChat` client) -> + `useClientMutedUsers`. **Scope-split rename.** A separate `useMutedUsers` + from `ChannelList/hooks/` still exists in v9 and was **not** renamed — if + the call site uses it inside a channel-list context (no `client` arg), + leave it alone. To decide: read the import path or the argument. If it + takes a `StreamChat` client, rename. Otherwise keep. +- `useAudioPlayerControl` -> `useAudioPlayer` (also rename type + `UseAudioPlayerControlProps` -> `UseAudioPlayerProps`). The v9 barrel + exports `useAudioPlayer`; `useAudioPlayerControl` is removed. +- New hooks that replace removed components/values: + - `useMessageDeliveryStatus`, `useGroupedAttachments`, `useMessagePreviewIcon`, + `useMessagePreviewText` (replace the removed `MessagePreview` component) + - `useMessageComposer` (direct access to composer state — replaces reading + from `useMessageInputContext()` for most composer needs) + - `useAttachmentPickerState` (replaces context-based `selectedPicker` / + `toggleAttachmentPicker`) + - `useImageGalleryVideoPlayer`, `useHasOwnReaction`, + `useChannelPreviewDraftMessage`, `useChannelPreviewPollLabel`, + `useChannelTypingState` + +### 4.3 Props (breaking only) + +On `Channel` / `MessageComposer` / `MessageInputContext`: + +- `asyncMessagesMultiSendEnabled` -> `audioRecordingSendOnComplete` (SEMANTICS INVERTED — see §3.3) +- `toggleAttachmentPicker` -> `openAttachmentPicker` / `closeAttachmentPicker` +- `selectedPicker` -> `useAttachmentPickerState()` hook +- `cooldownEndsAt` -> (removed; cooldown managed internally by `OutputButtons`) +- `getMessagesGroupStyles` -> (removed; grouping handled internally) +- `legacyImageViewerSwipeBehaviour` -> (removed; legacy viewer gone) +- `deletedMessagesVisibilityType` -> (removed; deleted messages always shown — see §6) +- `isAttachmentEqual` -> (removed) +- `additionalMessageInputProps` -> `additionalMessageComposerProps` + +On `ChannelPreviewContext` (and `ChannelList`): + +- `latestMessagePreview` (pre-formatted) -> `lastMessage` (raw). Text access + goes from `latestMessagePreview.messageObject.text` to `lastMessage.text`. +- `` -> `` + (the prop is on `ChannelList`, but migrates to the `ChannelPreview` key.) + +On `MessageList`: + +- `isListActive` -> (removed) +- `setMessages` -> (removed; use centralized state store) +- `channelUnreadState` -> `channelUnreadStateStore` +- `additionalFlatListProps` type: `FlatListProps` -> `FlatListProps` +- `setFlatListRef` type: `FlatListType` -> `FlatListType` + +On `OverlayProvider`: + +- `imageGalleryCustomComponents` (nested) -> flat keys on `` (see §5) +- `MessageOverlayBackground`, `ImageGalleryHeader`, `ImageGalleryFooter`, `ImageGalleryGrid`, `ImageGalleryVideoControls` -> moved from `OverlayProvider` props to `` overrides +- `imageGalleryGridHandleHeight` -> (removed) +- `imageGalleryGridSnapPoints` -> (removed) + +On `AudioAttachment`: + +- `onLoad` / `onPlayPause` / `onProgress` -> `useAudioPlayer` hook +- `titleMaxLength` -> `showTitle` boolean + +### 4.4 Theme namespaces + +- `messageInput` -> `messageComposer` +- `messageSimple` -> `messageItemView` +- `messageItemView.avatarWrapper` -> `messageItemView.authorWrapper` +- `channelListMessenger` -> `channelListView` +- `messageItemView.card` — still exists but restructured. Inner keys + `authorName`, `authorNameContainer`, `authorNameFooter`, `noURI`, + `playButtonStyle`, `playIcon` were removed; `linkPreview` + `linkPreviewText` + were added. A sibling key `messageItemView.compactUrlPreview` was added for + the new compact URL preview. Diff the full shape against `theme.ts`. +- `channelPreview` namespace: **unchanged** (internal structure changed heavily; if + the app had custom `channelPreview` overrides, diff against + `node_modules/stream-chat-react-native-core/src/contexts/themeContext/utils/theme.ts`) + +For every other theme change: read the current `Theme` type at +`node_modules/stream-chat-react-native-core/src/contexts/themeContext/utils/theme.ts` +and let TypeScript report the mismatches. Do not try to remember the full +sub-key restructuring. + +### 4.5 Icons (breaking only) + +Renamed (public): + +- `CircleStop` -> `Stop` +- `Refresh` -> `Reload` + +Removed with no SDK replacement (supply your own): + +- Navigation / UI: `ArrowRight`, `ArrowLeft`, `Close`, `User`, `MessageIcon`, + `Attach`, `Back`, `AtMentions`, `ChatIcon`, `CheckSend`, `CircleClose`, + `CirclePlus`, `CircleRight`, `DownloadArrow`, `DownloadCloud`, `DragHandle`, + `Error`, `Eye`, `Folder`, `GenericFile`, `GiphyLightning`, `Grid`, `Group`, + `Logo`, `MailOpen`, `MenuPointVertical`, `MessageBubble`, `Notification`, + `PinHeader`, `SendCheck`, `SendPoll`, `SendUp`, `ShareRightArrow`, + `UserAdmin`, `UserMinus` +- Reactions: `LOLReaction`, `LoveReaction`, `ThumbsDownReaction`, + `ThumbsUpReaction`, `WutReaction` +- File-type icons: `CSV`, `DOCX`, `HTML`, `MD`, `ODT`, `PPT`, `PPTX`, `RAR`, + `RTF`, `SEVEN_Z`, `TAR`, `TXT`, `XLS`, `XLSX` + +Visually updated but keep the same import name (no code change needed; flag if +the app has pixel-precise snapshots): `Archive`, `ArrowUp`, `Audio`, `Camera`, +`Check`, `CheckAll`, `Copy`, `Delete`, `Down`, `Edit`, `Flag`, `GiphyIcon`, +`Imgur`, `Lightning`, `Link`, `Loading`, `Lock`, `MenuPointHorizontal`, `Mic`, +`Mute`, `PDF`, `Pause`, `Picture`, `Pin`, `Play`, `Recorder`, `Reload`, +`Resend`, `Search`, `SendRight`, `Share`, `Smile`, `Sound`, `ThreadReply`, +`Time`, `Unpin`, `UserAdd`, `UserDelete`, `Video`, `Warning`, `ZIP`. + +## 5. Removed-with-structural-replacement + +Not simple renames — require a code shape change. + +### 5.1 `AttachmentUploadProgressIndicator` → six granular indicators + +One component split into six. Pick the right one per upload type × state: + +- `FileUploadInProgressIndicator` +- `FileUploadRetryIndicator` +- `FileUploadNotSupportedIndicator` +- `ImageUploadInProgressIndicator` +- `ImageUploadRetryIndicator` +- `ImageUploadNotSupportedIndicator` + +Provide any custom ones as `` overrides using the matching key. + +### 5.2 `Card` / `CardCover` / `CardFooter` / `CardHeader` → `UrlPreview` + `URLPreviewCompact` + +- Old type `CardProps` -> new type `URLPreviewProps`. +- Choose rendering style via the new `urlPreviewType` prop on `Channel`: + `'full'` (default) or `'compact'`. +- Customize via ``. + +### 5.3 `AttachmentActions` → inline + +Removed with no standalone replacement — actions now render inline on +attachments. Delete any custom `AttachmentActions` override. + +### 5.4 `MessagePreview` → four hooks + +Component removed. Replace with composable hooks: + +- `useMessageDeliveryStatus` +- `useGroupedAttachments` +- `useMessagePreviewIcon` +- `useMessagePreviewText` + +### 5.5 `imageGalleryCustomComponents` → flat `WithComponents` keys + +- v8: nested object on `OverlayProvider` + (`imageGalleryCustomComponents={{ header: { Component: ... }, footer: {...}, grid: {...}, gridHandle: {...} }}`). +- v9: flat ``. + Note the v8 `gridHandle` has no replacement (use `ImageGalleryGrid` instead). + +### 5.6 Individual attachment-picker selector icons → `AttachmentTypePickerButton` + +`CameraSelectorIcon`, `FileSelectorIcon`, `ImageSelectorIcon`, and +`VideoRecorderSelectorIcon` are all replaced by the unified +`AttachmentTypePickerButton`. `AttachmentPickerBottomSheetHandle`, +`AttachmentPickerError`, and `AttachmentPickerErrorImage` are removed with no +replacement. + +## 6. Behavior changes (runtime semantics, not grep renames) + +- **`messageContentOrder` default swapped `'text'`/`'attachments'`.** If the v8 + app depended on text rendering before attachments, pass the old order + explicitly: + `messageContentOrder={['quoted_reply','gallery','files','poll','ai_text','text','attachments','location']}`. +- **`deletedMessagesVisibilityType` removed.** Deleted messages are always + shown now. The `'sender' | 'receiver' | 'never'` modes are gone; delete the + prop. +- **Swipe-to-reply boundary moved.** Was `MessageBubble`, now the full + `MessageItemView`. Gesture behavior (`messageSwipeToReplyHitSlop`, thresholds, + haptics, `MessageSwipeContent`, spring-back) is unchanged. +- **`messageContentWidth` / `setMessageContentWidth` removed.** Strip any + custom `MessageContent`, `MessageBubble`, or `ReactionListTop` that read or + called them (and the matching test mocks). +- **`MessagesContext` no longer carries any UI component keys.** The v9 + design is that all component overrides live in `ComponentsContext` (provided + via ``; see §3.1). If an app wraps its own `MessagesContext` + provider directly, stop passing `Message`, `MessageItemView`, `MessageHeader`, + `Attachment`, etc. into the value — those fields are gone. Move them to + ``. + - **Known intentional exception:** `FlatList` (typed as + `typeof NativeHandlers.FlatList | undefined`) is still a key on + `MessagesContextValue`. It's not a user-overridable `ComponentType`; + it's injected by `registerNativeHandlers()` at module load from the + native-package or expo-package. Don't flag it as a leak; don't try to + route it through ``. +- **Audio recording defaults changed.** See §3.3. +- **Semantic token renames:** + - `backgroundCoreSurface` -> `backgroundCoreSurfaceDefault` + - `badgeTextInverse` -> `badgeTextOnInverse` + - `textInverse` -> `textOnInverse` + - `backgroundCoreElevation4` -> **removed, no replacement.** Remove any + dependency on it. +- **Reaction-list default changed.** `reactionListType` default is now + `'clustered'`. If the app relied on the v8 segmented style, set + `reactionListType='default'` explicitly on `Channel`. +- **`MessageMenu` component removed.** The v9 overlay path does not render + `MessageMenu`; the component and its `MessageMenuProps` type are removed + from the public barrel. Any v8 custom `MessageMenu` override becomes a + type error on v9 — migrate that logic to the four components that actually + render in v9: `MessageReactionPicker`, `MessageActionList`, + `MessageActionListItem`, and `MessageUserReactions`. Provide them via + ``. +- **`MessageActionType` shape changed.** Two breaking changes for apps with + custom message actions: + 1. New required field `type: 'standard' | 'destructive'` on every + `MessageActionType` object. Destructive entries render in a visually + separated group in the action list. Add `type` to every entry in any + custom `messageActions` array. + 2. The `ActionType` union gained `'blockUser'`. If the app has an + exhaustive switch over `ActionType`, add a case for `'blockUser'` or + the TypeScript compiler will flag the switch as non-exhaustive. +- **Hook exports flattened.** Subpath imports like + `'stream-chat-react-native/ChannelPreview/hooks/useChannelPreviewDisplayName'` + no longer resolve. Import from the package root: + `import { useChannelPreviewDisplayName } from 'stream-chat-react-native'`. +- **Attachment-picker defaults.** `attachmentPickerBottomSheetHeight` is now + fixed pixels (`disableAttachmentPicker ? 72 : 333`), not viewport-relative. + `attachmentSelectionBarHeight` default is `72` (was `52`). + `numberOfAttachmentImagesToLoadPerCall` default is `25` (was `60`). + +## 7. Machine-readable rename block + +Parseable JSON for deterministic find-and-replace. Keys are v8 symbols; +values are v9 replacements. `null` = removed with no in-SDK replacement. + +```json +{ + "components": { + "MessageSimple": "MessageItemView", + "MessageAvatar": "MessageAuthor", + "MessageInput": "MessageComposer", + "ChannelListMessenger": "ChannelListView", + "ChannelPreviewMessenger": "ChannelPreviewView", + "Card": "UrlPreview", + "CardCover": "UrlPreview", + "CardFooter": "UrlPreview", + "CardHeader": "UrlPreview", + "GroupAvatar": "AvatarGroup", + "CameraSelectorIcon": "AttachmentTypePickerButton", + "FileSelectorIcon": "AttachmentTypePickerButton", + "ImageSelectorIcon": "AttachmentTypePickerButton", + "VideoRecorderSelectorIcon": "AttachmentTypePickerButton", + "PreviewAvatar": "ChannelAvatar", + "AttachmentActions": null, + "ImageReloadIndicator": null, + "AttachmentPickerBottomSheetHandle": null, + "AttachmentPickerError": null, + "AttachmentPickerErrorImage": null, + "MessagePreview": null, + "MessageEditedTimestamp": null, + "MessageMenu": "MessageReactionPicker + MessageActionList + MessageActionListItem + MessageUserReactions", + "CreatePollIcon": null, + "CommandsButton": null, + "MoreOptionsButton": null, + "InputEditingStateHeader": "MessageInputHeaderView", + "InputReplyStateHeader": "MessageInputHeaderView", + "CommandInput": null + }, + "componentProps": { + "MessageSimpleProps": "MessageItemViewProps", + "MessageAvatarProps": "MessageAuthorProps", + "MessageInputProps": "MessageComposerProps", + "ChannelListMessengerProps": "ChannelListViewProps", + "ChannelPreviewMessengerProps": "ChannelPreviewViewProps", + "CardProps": "URLPreviewProps", + "MessageMenuProps": null + }, + "hooks": { + "useAudioController": "useAudioRecorder", + "useAudioPlayerControl": "useAudioPlayer", + "useMutedUsers (Chat-level)": "useClientMutedUsers" + }, + "props": { + "asyncMessagesMultiSendEnabled": "audioRecordingSendOnComplete", + "toggleAttachmentPicker": "openAttachmentPicker|closeAttachmentPicker", + "additionalMessageInputProps": "additionalMessageComposerProps", + "latestMessagePreview": "lastMessage", + "imageGalleryCustomComponents": "WithComponents overrides", + "deletedMessagesVisibilityType": null, + "legacyImageViewerSwipeBehaviour": null, + "isAttachmentEqual": null, + "cooldownEndsAt": null, + "getMessagesGroupStyles": null, + "messageContentWidth": null, + "setMessageContentWidth": null, + "imageGalleryGridSnapPoints": null, + "imageGalleryGridHandleHeight": null, + "titleMaxLength": "showTitle", + "onLoad": "useAudioPlayer", + "onPlayPause": "useAudioPlayer", + "onProgress": "useAudioPlayer" + }, + "overridesKeyRenames": { + "LoadingIndicator (on Chat)": "ChatLoadingIndicator", + "MessageComposer (on Thread)": "ThreadMessageComposer" + }, + "themeNamespaces": { + "messageInput": "messageComposer", + "messageSimple": "messageItemView", + "channelListMessenger": "channelListView" + }, + "themeSubKeys": { + "messageItemView.avatarWrapper": "messageItemView.authorWrapper" + }, + "semanticTokens": { + "backgroundCoreSurface": "backgroundCoreSurfaceDefault", + "badgeTextInverse": "badgeTextOnInverse", + "textInverse": "textOnInverse", + "backgroundCoreElevation4": null + }, + "icons": { + "CircleStop": "Stop", + "Refresh": "Reload", + "ArrowRight": null, + "ArrowLeft": null, + "Close": null, + "User": null, + "MessageIcon": null, + "Attach": null, + "Back": null, + "ChatIcon": null, + "LOLReaction": null, + "LoveReaction": null, + "ThumbsUpReaction": null, + "ThumbsDownReaction": null, + "WutReaction": null, + "CSV": null, + "DOCX": null, + "HTML": null, + "MD": null, + "ODT": null, + "PPT": null, + "PPTX": null, + "RAR": null, + "RTF": null, + "SEVEN_Z": null, + "TAR": null, + "TXT": null, + "XLS": null, + "XLSX": null + } +} +``` + +## 8. When to read source (not training data) + +All paths are under `node_modules/stream-chat-react-native-core/src/`. Read the +file directly — do not attempt to reconstruct its contents from memory. + +- Public exports barrel: `index.ts` +- Components barrel: `components/index.ts` +- Hooks barrel: `hooks/index.ts` +- Contexts barrel: `contexts/index.ts` +- `WithComponents` + `useComponentsContext` + `ComponentOverrides` type: + `contexts/componentsContext/ComponentsContext.tsx` +- Full overridable component list (source of truth): + `contexts/componentsContext/defaultComponents.ts` — the `DEFAULT_COMPONENTS` + map. +- Theme type (source of truth for every theme key): + `contexts/themeContext/utils/theme.ts` +- `useAudioRecorder`: `components/MessageInput/hooks/useAudioRecorder.tsx` +- `useAudioPlayer`: `hooks/useAudioPlayer.ts` +- `useMessageComposer`: `contexts/messageInputContext/hooks/useMessageComposer.ts` +- `useAttachmentPickerState`: `hooks/useAttachmentPickerState.ts` +- Message item stack: `components/Message/MessageItemView/` + (`MessageItemView.tsx`, `MessageAuthor.tsx`, `MessageHeader.tsx`, etc.) +- Composer: `components/MessageInput/MessageComposer.tsx` (folder stays named + `MessageInput/`) +- Channel preview: `components/ChannelPreview/ChannelPreviewView.tsx` +- Channel list view: `components/ChannelList/ChannelListView.tsx` +- Attachment-picker redesign: `components/AttachmentPicker/components/AttachmentPickerContent.tsx` + +Rule of thumb for anything not in this guide: open `index.ts`, grep for the +symbol you think you need, and follow the re-export path to the definition. +If it's not in `index.ts`, it's not a public API — do not rely on it. + +## 9. Verification workflow + +An agent MUST run all of these before declaring the migration complete. Every +bullet is a hard gate; none are optional. + +```bash +# 1. No v8 symbols remain in source. +rg '\b(MessageSimple|MessageAvatar|ChannelPreviewMessenger|ChannelListMessenger)\b' src/ +rg '\buseAudioController\b' src/ +# useMutedUsers is scope-split (§4.2). Any remaining matches must be verified by +# hand — Chat-level usage is a bug, ChannelList-level usage is fine. +rg '\buseMutedUsers\b' src/ +rg '\basyncMessagesMultiSendEnabled\b' src/ +rg '\b(latestMessagePreview|deletedMessagesVisibilityType|messageContentWidth|setMessageContentWidth|legacyImageViewerSwipeBehaviour)\b' src/ +rg '\b(AttachmentActions|AttachmentUploadProgressIndicator|Card|CardCover|CardFooter|CardHeader|ImageReloadIndicator|MessagePreview)\b' src/ +rg '\b(CameraSelectorIcon|FileSelectorIcon|ImageSelectorIcon|VideoRecorderSelectorIcon)\b' src/ +rg '\b(backgroundCoreSurface|badgeTextInverse|textInverse|backgroundCoreElevation4)\b' src/ + +# 2. Component override props no longer appear on Channel/ChannelList/Chat/Thread. +# This regex matches JSX like `]*\b([A-Z]\w+)=\{' src/ + +# 3. `MessageInput` as a component reference is gone (component renamed to MessageComposer). +# The context name, hook name, and helper-view names are allowed — filter them out: +rg '\bMessageInput\b' src/ | rg -v 'MessageInputContext|MessageInputHeaderView|MessageInputFooterView|MessageInputLeadingView|MessageInputTrailingView|useMessageInputContext|MessageInputContextValue' + +# 4. New required peer dep present in package.json. +rg '"react-native-teleport"' package.json + +# 5. TypeScript passes. +yarn tsc --noEmit # or: npx tsc --noEmit + +# 6. Lint + tests if the project has them. +yarn lint +yarn test +``` + +If any `rg` above returns hits (except #4 which should return a hit), either +finish migrating the matches or verify each match against §3–§6 and explain in +the agent's final report why the match is legitimate (e.g. a test string or a +comment referencing the v8 name). + +Smoke-test the running app at minimum: + +- Render `` → `` → `` + ``. +- Send a text message; long-press it → confirm reactions and action list + appear via the new overlay. +- Swipe a message right → confirm reply preview appears (whole + `MessageItemView` row is the hit area now, not just the bubble). +- Attach an image; confirm upload indicators render. +- Start an audio recording; confirm send behavior matches §3.3 intent. +- Open the image gallery from a message with media. + +If anything renders but looks wrong, the theme likely has stale keys — diff +against `contexts/themeContext/utils/theme.ts` (§8) and let TypeScript +highlight every mismatch. diff --git a/package/src/components/MessageMenu/MessageMenu.tsx b/package/src/components/MessageMenu/MessageMenu.tsx deleted file mode 100644 index bf0564e020..0000000000 --- a/package/src/components/MessageMenu/MessageMenu.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React, { PropsWithChildren } from 'react'; - -import { useWindowDimensions } from 'react-native'; - -import { MessageActionType } from './MessageActionListItem'; - -import { MessageContextValue } from '../../contexts/messageContext/MessageContext'; -import { useTheme } from '../../contexts/themeContext/ThemeContext'; -import { BottomSheetModal } from '../UIComponents/BottomSheetModal'; - -export type MessageMenuProps = PropsWithChildren< - Partial> & { - /** - * Function to close the message actions bottom sheet - * @returns void - */ - dismissOverlay: () => void; - /** - * An array of message actions to render - */ - messageActions: MessageActionType[]; - /** - * Boolean to determine if there are message actions - */ - showMessageReactions: boolean; - /** - * Boolean to determine if the overlay is visible. - */ - visible: boolean; - /** - * Function to handle reaction on press - * @param reactionType - * @returns - */ - handleReaction?: (reactionType: string) => Promise; - /** - * The selected reaction - */ - selectedReaction?: string; - - layout: { - x: number; - y: number; - w: number; - h: number; - }; - } ->; - -// TODO: V9: Either remove this or refactor it so that it's useful again, as its logic -// is offloaded to other components now. -export const MessageMenu = (props: MessageMenuProps) => { - const { - dismissOverlay, - // handleReaction, - // message: propMessage, - // MessageActionList: propMessageActionList, - // MessageActionListItem: propMessageActionListItem, - // messageActions, - // MessageReactionPicker: propMessageReactionPicker, - // MessageUserReactions: propMessageUserReactions, - // MessageUserReactionsAvatar: propMessageUserReactionsAvatar, - // MessageUserReactionsItem: propMessageUserReactionsItem, - // selectedReaction, - showMessageReactions, - visible, - // layout, - children, - } = props; - const { height } = useWindowDimensions(); - // const { - // MessageActionList: contextMessageActionList, - // MessageActionListItem: contextMessageActionListItem, - // MessageReactionPicker: contextMessageReactionPicker, - // MessageUserReactions: contextMessageUserReactions, - // MessageUserReactionsAvatar: contextMessageUserReactionsAvatar, - // MessageUserReactionsItem: contextMessageUserReactionsItem, - // } = useMessagesContext(); - // const { message: contextMessage } = useMessageContext(); - // const MessageActionList = propMessageActionList ?? contextMessageActionList; - // const MessageActionListItem = propMessageActionListItem ?? contextMessageActionListItem; - // const MessageReactionPicker = propMessageReactionPicker ?? contextMessageReactionPicker; - // const MessageUserReactions = propMessageUserReactions ?? contextMessageUserReactions; - // const MessageUserReactionsAvatar = - // propMessageUserReactionsAvatar ?? contextMessageUserReactionsAvatar; - // const MessageUserReactionsItem = propMessageUserReactionsItem ?? contextMessageUserReactionsItem; - // const message = propMessage ?? contextMessage; - const { - theme: { - messageMenu: { - bottomSheet: { height: bottomSheetHeight }, - }, - }, - } = useTheme(); - - return ( - - {children} - - ); -}; diff --git a/package/src/components/index.ts b/package/src/components/index.ts index 1cf41c4af6..9e20d0428a 100644 --- a/package/src/components/index.ts +++ b/package/src/components/index.ts @@ -163,7 +163,6 @@ export * from './MessageList/hooks/useMessageGroupStyles'; export * from './MessageMenu/MessageActionList'; export * from './MessageMenu/MessageActionListItem'; -export * from './MessageMenu/MessageMenu'; export * from './MessageMenu/MessageUserReactions'; export * from './MessageMenu/MessageUserReactionsAvatar'; export * from './MessageMenu/MessageReactionPicker'; diff --git a/package/src/contexts/componentsContext/defaultComponents.ts b/package/src/contexts/componentsContext/defaultComponents.ts index 27ae44feca..544a415853 100644 --- a/package/src/contexts/componentsContext/defaultComponents.ts +++ b/package/src/contexts/componentsContext/defaultComponents.ts @@ -123,7 +123,6 @@ import { TypingIndicatorContainer } from '../../components/MessageList/TypingInd import { UnreadMessagesNotification } from '../../components/MessageList/UnreadMessagesNotification'; import { MessageActionList } from '../../components/MessageMenu/MessageActionList'; import { MessageActionListItem } from '../../components/MessageMenu/MessageActionListItem'; -import { MessageMenu } from '../../components/MessageMenu/MessageMenu'; import { MessageReactionPicker } from '../../components/MessageMenu/MessageReactionPicker'; import { MessageUserReactions } from '../../components/MessageMenu/MessageUserReactions'; import { MessageUserReactionsAvatar } from '../../components/MessageMenu/MessageUserReactionsAvatar'; @@ -226,7 +225,6 @@ const components = { MessageInputTrailingView, MessageItemView, MessageList, - MessageMenu, MessagePinnedHeader, MessageReactionPicker, MessageReminderHeader, diff --git a/package/src/hooks/index.ts b/package/src/hooks/index.ts index 094549af5b..8e368a9532 100644 --- a/package/src/hooks/index.ts +++ b/package/src/hooks/index.ts @@ -12,7 +12,7 @@ export * from './useClientNotifications'; export * from './useInAppNotificationsState'; export * from './usePortalSettledCallback'; export * from './useRAFCoalescedValue'; -export * from './useAudioPlayerControl'; +export * from './useAudioPlayer'; export * from './useAttachmentPickerState'; export * from './messagePreview/useMessageDeliveryStatus'; export * from './messagePreview/useGroupedAttachments'; diff --git a/package/src/hooks/useAudioPlayerControl.ts b/package/src/hooks/useAudioPlayerControl.ts deleted file mode 100644 index cbe30f1704..0000000000 --- a/package/src/hooks/useAudioPlayerControl.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { useMemo } from 'react'; - -import { useAudioPlayerContext } from '../contexts/audioPlayerContext/AudioPlayerContext'; -import { AudioPlayerOptions } from '../state-store/audio-player'; - -export type UseAudioPlayerControlProps = { - /** - * Identifier of the entity that requested the audio playback, e.g. message ID. - * Asset to specific audio player is a many-to-many relationship - * - one URL can be associated with multiple UI elements, - * - one UI element can display multiple audio sources. - * Therefore, the AudioPlayer ID is a combination of request:src. - * - * The requester string can take into consideration whether there are multiple instances of - * the same URL requested by the same requester (message has multiple attachments with the same asset URL). - * In reality the fact that one message has multiple attachments with the same asset URL - * could be considered a bad practice or a bug. - */ - requester?: string; -} & Partial; - -const makeAudioPlayerId = ({ - requester, - src, - id, -}: { - src: string; - requester?: string; - id?: string; -}) => `${requester ?? 'requester-unknown'}:${src}:${id ?? ''}`; - -export const useAudioPlayerControl = ({ - duration, - mimeType, - playbackRates, - previewVoiceRecording, - requester = '', - type, - uri, - id: fileId, -}: UseAudioPlayerControlProps) => { - const { audioPlayerPool } = useAudioPlayerContext(); - const id = makeAudioPlayerId({ id: fileId, requester, src: uri ?? '' }); - const audioPlayer = useMemo( - () => - audioPlayerPool?.getOrAddPlayer({ - duration: duration ?? 0, - id, - mimeType: mimeType ?? '', - playbackRates, - previewVoiceRecording, - type: type ?? 'audio', - uri: uri ?? '', - }), - [audioPlayerPool, duration, id, mimeType, playbackRates, previewVoiceRecording, type, uri], - ); - - return audioPlayer; -};