From ae767d730de9a70ed554e81bd3c323d0b452f7c3 Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 17 Apr 2026 10:15:39 +0200 Subject: [PATCH 1/7] =?UTF-8?q?docs:=20add=20AI-focused=20v13=20=E2=86=92?= =?UTF-8?q?=20v14=20migration=20guide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds ai-docs/ai-migration.md, a token-optimized execution guide for coding agents performing the v13 → v14 migration. Distills the 62 confirmed breaking changes from ai-docs/breaking-changes.md into ordered phases, rename lists, and code-level rewrites. Removes the superseded ai-docs/DEPRECATED_API_REMOVAL_PLAN.md. --- ai-docs/DEPRECATED_API_REMOVAL_PLAN.md | 117 ------- ai-docs/ai-migration.md | 415 +++++++++++++++++++++++++ 2 files changed, 415 insertions(+), 117 deletions(-) delete mode 100644 ai-docs/DEPRECATED_API_REMOVAL_PLAN.md create mode 100644 ai-docs/ai-migration.md diff --git a/ai-docs/DEPRECATED_API_REMOVAL_PLAN.md b/ai-docs/DEPRECATED_API_REMOVAL_PLAN.md deleted file mode 100644 index 25c191345..000000000 --- a/ai-docs/DEPRECATED_API_REMOVAL_PLAN.md +++ /dev/null @@ -1,117 +0,0 @@ -# Deprecated API Removal Plan - -## Context - -The SDK (currently at v14 beta) has **31 deprecated items** accumulated over several major versions. Some have been deprecated since v10 ("will be removed in v11.0.0"). Since v14 is still in beta, now is the ideal time to clean these up before stable release. All removals are breaking changes, but they're expected in a major version bump. - -## Inventory - -| # | Deprecated Item | Replacement | Location | Status | -| ---------------------------------------------------------- | ---------------------------------------- | ------------------------------------------- | ----------------------------------------------------------- | ---------------- | -| **Pin Permissions (7 items)** | | | | | -| 1 | `pinPermissions` prop (MessageContext) | `channelCapabilities` | `src/context/MessageContext.tsx` | ✅ Done | -| 2 | `pinPermissions` prop (MessageProps) | `channelCapabilities` | `src/components/Message/types.ts` | ✅ Done | -| 3 | `pinPermissions` prop (MessageListProps) | `channelCapabilities` | `src/components/MessageList/MessageList.tsx` | ✅ Done | -| 4 | `PinEnabledUserRoles` type | `channelCapabilities` | `src/components/Message/hooks/usePinHandler.ts` | ✅ Done | -| 5 | `PinPermissions` type | `channelCapabilities` | `src/components/Message/hooks/usePinHandler.ts` | ✅ Done | -| 6 | `defaultPinPermissions` constant | `channelCapabilities` | `src/components/Message/utils.tsx` | ✅ Done | -| 7 | `_permissions` param in `usePinHandler` | `channelCapabilities` | `src/components/Message/hooks/usePinHandler.ts` | ✅ Done | -| **Pagination Renames (6 items, #1804)** | | | | | -| 8 | `hasMore` prop (InfiniteScroll) | `hasPreviousPage` | `src/components/InfiniteScrollPaginator/InfiniteScroll.tsx` | ✅ Done | -| 9 | `hasMoreNewer` prop (InfiniteScroll) | `hasNextPage` | `src/components/InfiniteScrollPaginator/InfiniteScroll.tsx` | ✅ Done | -| 10 | `loadMore` prop (InfiniteScroll) | `loadPreviousPage` | `src/components/InfiniteScrollPaginator/InfiniteScroll.tsx` | ✅ Done | -| 11 | `loadMoreNewer` prop (InfiniteScroll) | `loadNextPage` | `src/components/InfiniteScrollPaginator/InfiniteScroll.tsx` | ✅ Done | -| 12 | `refreshing` prop (LoadMoreButton) | `isLoading` | `src/components/LoadMore/LoadMoreButton.tsx` | ✅ Done | -| 13 | `refreshing` prop (PaginatorProps) | `isLoading` | `src/types/types.ts` | ✅ Done | -| **useUserRole (3 items)** | | | | | -| 14 | `isAdmin` return value | `channelCapabilities` | `src/components/Message/hooks/useUserRole.ts:13` | Pending | -| 15 | `isOwner` return value | `channelCapabilities` | `src/components/Message/hooks/useUserRole.ts:20` | Pending | -| 16 | `isModerator` return value | `channelCapabilities` | `src/components/Message/hooks/useUserRole.ts:26` | Pending | -| **VirtualizedMessageList (4 items, deprecated since v10)** | | | | | -| 17 | `defaultItemHeight` prop | `additionalVirtuosoProps.defaultItemHeight` | `VirtualizedMessageList.tsx:617` | Pending | -| 18 | `head` prop | `additionalVirtuosoProps.components.Header` | `VirtualizedMessageList.tsx:636` | Pending | -| 19 | `overscan` prop | `additionalVirtuosoProps.overscan` | `VirtualizedMessageList.tsx:663` | Pending | -| 20 | `scrollSeekPlaceHolder` prop | `additionalVirtuosoProps.scrollSeek*` | `VirtualizedMessageList.tsx:675` | Pending | -| **Other** | | | | | -| 21 | `StreamEmoji` component | (none) | `src/components/Reactions/StreamEmoji.tsx` | ✅ Done | -| 22 | `UploadButton` / `UploadButtonProps` | `FileInput` / `FileInputProps` | `src/components/ReactFileUtilities/UploadButton.tsx` | ✅ Done | -| 23 | `moveChannelUp` utility | `moveChannelUpwards` | `src/components/ChannelList/utils.ts` | ✅ Done | -| 24 | `latestMessage` prop | `latestMessagePreview` | `src/components/ChannelListItem/ChannelListItem.tsx` | ✅ Done | -| 25 | `total_unread_count` mock field | `unread_count` | `src/mock-builders/event/notificationMarkUnread.ts` | ✅ Done | -| 26 | `hasNotMoreMessages` function | `hasMoreMessagesProbably` | `src/components/MessageList/utils.ts` | ✅ Done | -| 27 | `popperOptions` prop (EmojiPicker) | `placement` | `src/plugins/Emojis/EmojiPicker.tsx` | ✅ Done | -| 28 | `useActionHandler` string overload | `FormData` object form | `src/components/Message/hooks/useActionHandler.ts:34` | Pending | -| 29 | `formatDate` prop (MessageProps) | (TBD) | `src/components/Message/types.ts:29` (`// todo: remove`) | Pending | -| 30 | `LegacyReactionOptions` array format | Object format `{ quick: {...} }` | `src/components/Reactions/reactionOptions.tsx:5` | Needs discussion | -| 31 | `LegacyThreadContext` | (internal plumbing, not public API) | `src/components/Thread/LegacyThreadContext.ts` | Needs discussion | - -## Bug Fix (not a removal) - -~~`MessageList.tsx:83` has a **wrong deprecation comment**: `loadMoreNewer` is labeled `@deprecated in favor of channelCapabilities` but it's a pagination callback, not related to capabilities. This comment was copy-pasted from pin permissions deprecation and should be removed.~~ ✅ Fixed - -## Also addressed - -- `deprecationAndReplacementWarning` utility deleted (zero callers remain after Batch 3) -- Tests updated: `InfiniteScroll.test.tsx`, `LoadMoreButton.test.tsx`, `LoadMorePaginator.test.tsx` -- Unused `fireEvent` import cleaned up in `InfiniteScroll.test.tsx` - -## Remaining Work - -### Batch 4 (remaining): useUserRole (items 14–16) - -**Risk: Medium** - -| Action | File | -| ----------------------------------------------------------------- | ------------------------------------------------------------- | -| Remove `isAdmin`, `isOwner`, `isModerator` computations + returns | `src/components/Message/hooks/useUserRole.ts` | -| Delete ~120 lines of tests for removed values | `src/components/Message/hooks/__tests__/useUserRole.test.tsx` | - -### Batch 5: VirtualizedMessageList Props (items 17–20) - -**Risk: Medium-High** — Deprecated since v10. The `head` prop needs care since it may affect thread rendering. - -| Action | File | -| ----------------------------------------------------------------------- | ------------------------------------------------------- | -| Remove `defaultItemHeight` prop + Virtuoso spread | `src/components/MessageList/VirtualizedMessageList.tsx` | -| Remove `head` prop + rendering logic (verify thread headers still work) | `src/components/MessageList/VirtualizedMessageList.tsx` | -| Remove `overscan` prop + Virtuoso prop | `src/components/MessageList/VirtualizedMessageList.tsx` | -| Remove `scrollSeekPlaceHolder` prop + Virtuoso config | `src/components/MessageList/VirtualizedMessageList.tsx` | - -### Batch 6: Misc Cleanup (items 28, 29) - -**Risk: Medium** - -| Action | File | -| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | -| Remove string overload branch (`typeof dataOrName === 'string'`) | `src/components/Message/hooks/useActionHandler.ts` | -| Update `ActionHandlerReturnType` to only accept `FormData` | `src/components/Message/hooks/useActionHandler.ts` | -| Update tests if any use the string overload | `src/components/Message/hooks/__tests__/useActionHandler.test.tsx` | -| Remove `formatDate` prop from `MessageProps` and `MessageContextValue` (has `// todo: remove`) | `src/components/Message/types.ts`, `src/context/MessageContext.tsx` | -| Remove `formatDate` pass-through in Message component and i18n integration | `src/i18n/utils.ts`, `src/i18n/types.ts` | -| **Note:** `DateSeparator.formatDate` is a separate prop and should be kept | | - -### Batch 7: Legacy Formats (items 30, 31) — Needs Discussion - -**Risk: High** — These are not formally `@deprecated` but are named "Legacy" and have newer replacements. Removal is more invasive. - -| Item | Details | -| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `LegacyReactionOptions` array format | The `ReactionOptions` type is a union of `LegacyReactionOptions` (array) and the new object format. Removing the array branch requires updating `Array.isArray()` guards in `ReactionSelector.tsx`, `useProcessReactions.tsx`, `MessageReactionsDetail.tsx`, and tests. | -| `LegacyThreadContext` | Internal compatibility bridge used in `Thread.tsx`, `useMessageComposerController.ts`, `useNotificationTarget.ts`, and tests. Named "Legacy" explicitly. Not a public API — removal depends on whether the new thread model fully replaces it. | - -**Recommendation:** These warrant their own investigation before committing to removal in v14. They may be better suited for v15 unless the team confirms the newer alternatives fully cover all use cases. - -## Items to NOT Remove - -| Item | Reason | -| ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -| `hasMore`/`hasMoreNewer`/`loadMore`/`loadMoreNewer` on **MessageList** and **VirtualizedMessageList** | NOT deprecated — canonical API from ChannelStateContext/ChannelActionContext | -| `_componentName` params on context hooks | Backward-compatible call-site markers, no benefit to removing | - -## Verification (per batch) - -1. `yarn types` — No type errors from removed types/props -2. `yarn test` — Full test suite passes -3. `yarn build` — Build succeeds -4. `yarn lint-fix` — No lint issues -5. For Batch 5: manually verify thread rendering in the example app diff --git a/ai-docs/ai-migration.md b/ai-docs/ai-migration.md new file mode 100644 index 000000000..7c8ac5446 --- /dev/null +++ b/ai-docs/ai-migration.md @@ -0,0 +1,415 @@ +# stream-chat-react v13 → v14 AI Migration Guide + +Execution-only guide for a coding agent migrating a third-party app from `stream-chat-react` v13 to v14. Run the phases in order; stop and fix each phase's errors before moving to the next. For evidence or edge cases, fall back to `./breaking-changes.md`. + +## Prerequisites + +1. Upgrade both packages to their latest versions together. The v14 peer-dep floor moved; leaving `stream-chat` on a v13-era version will fail peer resolution. + - `stream-chat-react` → latest v14 + - `stream-chat` → latest +2. Update stylesheet imports. The `/v2` path no longer resolves. + ```ts + // before + import 'stream-chat-react/dist/css/v2/index.css'; + // after + import 'stream-chat-react/dist/css/index.css'; + ``` +3. If the app uses `EmojiPicker`, also import its dedicated stylesheet: + ```ts + import 'stream-chat-react/dist/css/emoji-picker.css'; + ``` + +--- + +## Phase 1 — Import and Symbol Renames + +Apply these everywhere. Compile-breaks; they apply to imports, JSX usage, `ComponentContext` override keys, and type names (append `Props` where applicable). Also: import `MessageActions` and `Search` from `stream-chat-react`, not `stream-chat-react/experimental`. + +- `MessageInput` → `MessageComposer` +- `MessageInputFlat` → `MessageComposerUI` +- `MessageInputContext` → `MessageComposerContext` +- `useMessageInputContext` → `useMessageComposerContext` +- `additionalMessageInputProps` → `additionalMessageComposerProps` +- `MessageDeleted` → `MessageDeletedBubble` +- `MessageEditedTimestamp` → `MessageEditedIndicator` +- `MessageNotification` → `NewMessageNotification` +- `ScrollToBottomButton` → `ScrollToLatestMessageButton` +- `MessageIsThreadReplyInChannelButtonIndicator` → `MessageAlsoSentInChannelIndicator` +- `ReactionsListModal` → `MessageReactionsDetail` +- `ReactionsList` → `MessageReactions` +- `ChannelPreview` → `ChannelListItem` +- `ChannelPreviewMessenger` → `ChannelListItemUI` +- `ChannelPreviewActionButtons` → `ChannelListItemActionButtons` +- `ChannelListMessenger` → `ChannelListUI` +- `ChannelSearch` → `Search` +- `Modal` → `GlobalModal` +- `UploadButton` → `FileInput` +- `AddCommentForm` → `AddCommentPrompt` +- `EndPollDialog` → `EndPollAlert` +- `SuggestPollOptionForm` → `SuggestPollOptionPrompt` +- `useAudioController` → `useAudioPlayer` +- `getDisplayTitle` → `useChannelDisplayName` (hook) or `useChannelPreviewInfo().displayTitle` +- `getDisplayImage` → `getChannelDisplayImage` + +--- + +## Phase 2 — Removed Symbols: Code Rewrites + +### `with*Context` HOCs → hooks + +```tsx +// before +import { withMessageContext } from 'stream-chat-react'; +const CustomMessage = withMessageContext(({ message }) =>
{message.text}
); + +// after +import { useMessageContext } from 'stream-chat-react'; +const CustomMessage = () => { + const { message } = useMessageContext(); + return
{message.text}
; +}; +``` + +Applies to: `withChannelActionContext`, `withChannelStateContext`, `withChatContext`, `withComponentContext`, `withMessageContext`, `withTranslationContext`, `withTypingContext`. + +### `MessageOptions` / `MessageActionsBox` / `MessageActionsWrapper` / `CustomMessageActionsList` → `MessageActions` + `messageActionSet` + +```tsx +import { MessageActions, defaultMessageActionSet } from 'stream-chat-react'; + +const messageActionSet = defaultMessageActionSet.filter( + ({ placement, type }) => placement === 'quick-dropdown-toggle' || type !== 'delete', +); + +; +``` + +Custom action sets MUST include a `quick-dropdown-toggle` item or the dropdown trigger disappears. + +### `FixedHeightMessage` → custom `VirtualMessage` + +```tsx +import type { MessageUIComponentProps } from 'stream-chat-react'; + +const VirtualMessage = (props: MessageUIComponentProps) => ; + +...; +``` + +### `EditMessageForm` / `EditMessageModal` / `useEditHandler` / `clearEditingState` → composer-driven edit + +```tsx +// customize preview via EditedMessagePreview in WithComponents +import { useMessageComposer, useMessageContext } from 'stream-chat-react'; + +const { message } = useMessageContext(); +const messageComposer = useMessageComposer(); + +messageComposer.initState({ composition: message }); // start +messageComposer.clear(); // cancel +``` + +### `MessageListNotifications` → `NotificationList` + direct overrides + +Wrapper is gone. Override individual floating indicators (`NewMessageNotification`, `UnreadMessagesNotification`, `ScrollToLatestMessageButton`) or the layout slot (`MessageListMainPanel`). Use `NotificationList` for client-emitted notifications. + +### `ConnectionStatus` → app-owned system banner + +```tsx +import { useReportLostConnectionSystemNotification } from 'stream-chat-react'; + +const ConnectionBanner = () => { + useReportLostConnectionSystemNotification(); + return null; +}; +``` + +Combine with `useSystemNotifications()` if rendering UI explicitly. + +### `ButtonWithSubmenu` → `ContextMenu` primitives + +Rebuild submenus with `ContextMenu`, `ContextMenuButton`, `ContextMenuHeader`, `ContextMenuBackButton`. + +### `QuotedPoll` / `` → quoted-message layer + +```tsx +import { QuotedMessagePreviewUI } from 'stream-chat-react'; + +; +``` + +For richer rendering, override `QuotedMessage` or `QuotedMessagePreview` in `WithComponents` and render `quotedMessage.poll` yourself. + +### Other removed symbols + +- `isOnlyEmojis` → `countEmojis()` or `messageTextHasEmojisOnly()` +- `showMessageActionsBox`, `shouldRenderMessageActions` → build via `messageActionSet` +- `MessageErrorText`, `MessageErrorIcon` → removed; default error badge renders under `.str-chat__message-error-indicator` +- `QuotedMessagePreviewHeader` → `QuotedMessagePreviewUI` +- `CardAudio` → inline the audio card UI in your own component +- `attachmentTypeIconMap` → inline your own map or use `SummarizedMessagePreview` +- `ReactionDetailsComparator`, `sortReactionDetails` prop → `reactionDetailsSort` with `ReactionSort` +- `SimpleReactionsList` → `MessageReactions` or a custom compact list +- Standalone icons (`ActionsIcon`, `ReactionIcon`, `ThreadIcon`, `MessageErrorIcon`, `CloseIcon`, `SendIcon`, `MicIcon`, `MessageSentIcon`, `MessageDeliveredIcon`, `RetryIcon`, `DownloadIcon`, `LinkIcon`) → public `Icons` set (e.g. `IconXmark`, `IconCheckmark1Small`, `IconDoubleCheckmark1Small`) or higher-level components (`SendButton`, `MessageStatus`, `MessageActions`) +- `useChannelDeletedListener`, `useNotificationMessageNewListener`, `useMobileNavigation`, siblings → no shim; remove the calls (`ChannelList` handles these events internally) +- `ToggleSidebarButton`, `ToggleButtonIcon`, `MenuIcon`, `NAV_SIDEBAR_DESKTOP_BREAKPOINT` → app-owned sidebar (see Phase 6) +- `pinPermissions`, `PinPermissions`, `PinEnabledUserRoles`, `defaultPinPermissions` → rely on `channelCapabilities['pin-message']`; remove the props +- `usePinHandler(message, pinPermissions, notifications)` → `usePinHandler(message, notifications?)` +- `ChannelListItemUI.latestMessage` → `latestMessagePreview` +- `EmojiPicker.popperOptions` → `placement` +- `InfiniteScroll` / `LoadMoreButton` / `LoadMorePaginator`: `hasMore`, `loadMore`, `refreshing` → `hasNextPage`, `loadNextPage`, `isLoading` +- `InfiniteScroll` / `LoadMoreButton` / `LoadMorePaginator`: `hasMoreNewer`, `loadMoreNewer` → no alias; use `hasNextPage` / `loadNextPage` where appropriate + +--- + +## Phase 3 — Move UI Overrides to `WithComponents` + +`Channel` and `ChannelList` no longer accept UI override props. Move them all to `WithComponents` / `ComponentContext`. + +```tsx +// before + + +// after + + ... + +``` + +Override-key renames when you move them over: + +- `MessageOptions` → `MessageActions` +- `MessageNotification` → `NewMessageNotification` +- `ReactionsListModal` → `MessageReactionsDetail` +- `MessageIsThreadReplyInChannelButtonIndicator` → `MessageAlsoSentInChannelIndicator` +- `ChannelPreviewActionButtons` → `ChannelListItemActionButtons` +- `ChannelPreviewMessenger` → `ChannelListItemUI` +- `FileUploadIcon` → `AttachmentSelectorInitiationButtonContents` +- `Input` → `MessageComposerUI` + +Removed override keys (no rename; redesign the surface): `EditMessageInput`, `EditMessageModal`, `QuotedPoll`, `CustomMessageActionsList`, `MessageListNotifications`. + +`ChannelList` direct UI props `List`, `Preview`, `Avatar`, `LoadingIndicator`, `LoadingErrorIndicator` are removed. Pass them through `WithComponents` under keys `ChannelListUI`, `ChannelListItemUI`, `Avatar`, `LoadingIndicator`, `LoadingErrorIndicator`. + +`Channel` empty-state default changed: no active channel now renders `EmptyStateIndicator` (was blank). To restore v13: + +```tsx +... +``` + +--- + +## Phase 4 — Composer, Edit, and Cooldown + +After the `MessageInput*` → `MessageComposer*` rename (Phase 1): + +### `handleSubmit` signature + +```tsx +// before +handleSubmit(customMessageData, { skip_enrich_url: true }); +// after +handleSubmit(event?); +``` + +Custom message data flows through composer middleware or `messageComposer.customDataManager`, not `handleSubmit` args. + +### Cooldown + +`MessageComposerContext` no longer exposes `cooldownInterval`, `cooldownRemaining`, `setCooldownRemaining`. + +```tsx +import { + useCooldownRemaining, + useIsCooldownActive, + CooldownTimer, +} from 'stream-chat-react'; + +const cooldownRemaining = useCooldownRemaining(); +const isCooldownActive = useIsCooldownActive(); + +; // zero-prop +``` + +### Voice recordings + +Moved out of `AttachmentPreviewList` into `VoiceRecordingPreviewSlot`. Custom composer UIs must render `` separately. + +### Smaller behavior changes + +- **Textarea default is now 10 rows** (was 1). Pass `` to restore. +- **`LinkPreviewList` default `displayLinkCount` is 1** (was all). Pass `` for more. Also: link previews now render while quoting a message (v13 suppressed them). +- **`AutocompleteSuggestionItem`**: inner wrapper is gone; custom items receive `ComponentProps<'button'>` directly. Prefer overriding `CommandItem`, `EmoticonItem`, `UserItem` instead of the full item. +- **`AttachmentSelector` action set**: can include `selectCommand`; filters out upload actions when disabled; cooldown disables the trigger. Custom `attachmentSelectorActionSet` gains `id`, `Header`, `Submenu`, `submenuItems`, `submenuHeader`. + +--- + +## Phase 5 — Message Actions Model + +### Props removed from `Message`, `MessageList`, `VirtualizedMessageList` + +Delete usages of: + +- `customMessageActions` +- `onlySenderCanEdit` +- All `get*Notification` callbacks (`getDeleteMessageErrorNotification`, `getFlagMessageSuccessNotification`, `getMuteUserSuccessNotification`, `getMarkMessageUnreadSuccessNotification`, `getPinMessageErrorNotification`, `getFetchReactionsErrorNotification`, and friends). + +For sender-only edit behavior, filter the `edit` action out of your custom `messageActionSet` for foreign messages. For app-owned notifications, use `useNotificationApi()` or override the relevant `Streami18n` translation keys. + +### `handleDelete` — new signature and semantics + +`(event, options?) => void` → `(options?) => Promise`. Removes unsent/failed messages locally; rethrows server errors. + +```tsx +try { + await handleDelete({ hardDelete: true }); +} catch { + // app-owned recovery; SDK already shows default error notification +} +``` + +### Default action set changed + +- Now includes a built-in `download` action for downloadable attachments. +- Order is different. If UX depends on stable order, define a custom `messageActionSet` rather than relying on `defaultMessageActionSet` passthrough. +- `markUnread` is limited to foreign messages, even with `read-events` capability. + +### Custom `Message` overrides + +Read `groupedByUser`, `firstOfGroup`, `endOfGroup` from `useMessageContext()`, not props. `MessageUIComponentProps` is no longer guaranteed to be injected. + +--- + +## Phase 6 — Search, Sidebar, Chat View, Query Limits + +### Search + +```tsx +// before +; + +// after +const CustomSearch = () => ; + + + +; +``` + +Drop `additionalChannelSearchProps` and `ChannelSearch` props from `ChannelList`. Customize `Search`, `SearchBar`, `SearchResults` via `WithComponents`. + +### `useChannelListContext()` no longer accepts a diagnostic component name + +```tsx +// before +const { channels } = useChannelListContext('Sidebar'); +// after +const { channels } = useChannelListContext(); +``` + +### Sidebar is app-owned + +`Chat` no longer owns sidebar state. Removed: `initialNavOpen` prop, `navOpen`, `openMobileNav`, `closeMobileNav` from `useChatContext()`. `ChannelHeader.MenuIcon` is removed. Put your app-owned toggle UI into the `HeaderStartContent` (or `HeaderEndContent`) override: + +```tsx + + ... + +``` + +### `ChatView.Selector` defaults to icon-only + +```tsx + +``` + +### `ChatView.ThreadAdapter` renders an empty state by default + +To preserve the old blank pane, override `EmptyStateIndicator` with a `null`-returning component within that scope, or wire `ThreadProvider` manually. + +### Explicit query limits + +`Channel` and `ChannelList` no longer inject default query limits. + +```tsx +... + +``` + +--- + +## Phase 7 — Behavior Changes That Compile Cleanly + +- **`ChannelHeader.live` removed.** Default header no longer renders `channel.data.subtitle`. If either matters, ship a custom `ChannelHeader`. +- **`ThreadHeader.overrideImage` removed.** Subtitle now includes reply count and can flip to typing state. `data-testid` changed from `close-button` to `close-thread-button`. +- **`TypingIndicator` contract changed.** Custom overrides must accept `scrollToBottom` and optionally `isMessageListScrolledToBottom` to preserve the new pinned-scroll behavior. `TypingIndicatorHeader` is a separate component rendered by the default `ChannelHeader` / `ThreadHeader`. +- **`MessageTimestamp` default is now `HH:mm`.** To restore calendar format, override the `timestamp/MessageTimestamp` key in `Streami18n`. +- **`MessageStatusProps.Avatar` removed.** Default read state no longer renders reader avatar. Implement a custom `MessageReadStatus` if required. +- **`MessageTextProps.theme` removed.** `MessageText` no longer renders quoted messages — handle quotes in the surrounding message UI. Classes like `str-chat__message-${theme}-text-inner` are gone. +- **`DateSeparator.position` / `.unread`.** Props are still accepted but no longer drive rendering. Provide a custom `DateSeparator` if lines or `New -` labels are required. +- **`UnreadMessagesSeparator`.** Now shows unread count and a mark-read button. Pass `showCount={false}` to suppress. +- **`Avatar` contract.** `image`/`name` → `imageUrl`/`userName`, `size` is now required. +- **`ChannelAvatar` / `GroupAvatar`.** `groupChannelDisplayInfo` array of `{image, name}` → `displayMembers`; `size` required. +- **`useChannelPreviewInfo()`** returns a stable empty group info object instead of `null`/`undefined` — any `groupChannelDisplayInfo == null` checks are now always false. +- **`useLatestMessagePreview()`** reports native `giphy` attachments with `type: 'giphy'`, not `image`. +- **`FileIcon` props.** `filename` → `fileName`; `big`, `size`, `sizeSmall`, `type` removed. `mimeTypeToIcon(type, mimeType)` → `mimeTypeToIcon(mimeType?)`. +- **`Gallery` / `ModalGallery`.** `ModalGallery` API went from `{ images, index }` to `{ items, initialIndex? }`. `Gallery` alone no longer renders a thumbnail grid — supply `GalleryUI` or use `ModalGallery` for the old behavior. +- **Low-level attachment containers.** `MediaContainer` now takes `attachments` (plural). Gallery payloads changed `images` → `items`. Audio custom components: rename prop `og` → `attachment`. Native `giphy` stays inline (no `ModalGallery` expansion). +- **`AttachmentProps.Gallery` → `AttachmentProps.ModalGallery`.** `Media` now uses `VideoPlayerProps`, not `ReactPlayerProps`. +- **`PinIndicator`** no longer receives `t` as prop — use `useTranslationContext()` inside. +- **Suggestion `UserItem`** no longer receives `Avatar` prop — render avatar/menu UI directly. +- **Modal prompt components.** `MessageBouncePrompt`, `RecordingPermissionDeniedNotification`, poll `PollOptionsFullList` / `PollResults` / `PollAnswerList` / `SuggestPollOptionPrompt` no longer receive `onClose` / `close` props. Dismiss via `useModalContext().close()`. +- **`handleDelete` behavior.** Unsent and network-failed messages are removed locally without a server round-trip. + +--- + +## Phase 8 — CSS, DOM, and Selector Audit + +- `stream-chat-react/dist/css/v2/*` → `stream-chat-react/dist/css/*` +- `.str-chat__modal__inner` → removed; children render directly +- `.str-chat__channel-list-messenger` → `.str-chat__channel-list-inner` +- `.str-chat__channel-list-messenger__main` → `.str-chat__channel-list-inner__main` +- `.str-chat__channel-list-react` → removed +- `.str-chat__message-input-inner` → `.str-chat__message-composer` +- `.str-chat__message-textarea-container` → `.str-chat__message-composer-compose-area` +- `.str-chat__message-textarea-with-emoji-picker` → `.str-chat__message-composer-controls` +- `.str-chat__message-error-icon` → `.str-chat__message-error-indicator` +- `aria-selected` on channel/thread list items → `aria-pressed` +- `.str-chat__header-hamburger` → app-owned toggle via `HeaderStartContent` +- `--str-chat__jump-to-latest-message-*` CSS vars → removed; use current tokens +- `BaseIcon` `viewBox="0 0 16 16"` → `viewBox="0 0 20 20"` (affects custom icons) + +--- + +## Verification + +1. **Detect the consumer's package manager.** Inspect the repo root: + - `pnpm-lock.yaml` → `pnpm` + - `yarn.lock` → `yarn` + - `bun.lockb` or `bun.lock` → `bun` + - `package-lock.json` → `npm` + - otherwise check `packageManager` in `package.json`; if still ambiguous, ask the user. +2. **Read `package.json` scripts.** Script names vary (`typecheck`, `types`, `tsc`, `lint`, `test`, `build`). Pick the closest matches. +3. **Run in order**, fixing errors before continuing: + - install dependencies + - typecheck → expect zero errors + - lint → expect zero errors + - tests → expect zero failures + - build → expect clean build +4. **Smoke-test runtime flows** that compile cleanly but changed in v14: + - compose, send, edit, delete a message (including an unsent/failed message) + - add and remove a reaction; open the reactions detail dialog + - start and reply to a thread + - open channel search; verify search results + - toggle sidebar (app-owned) + - custom message-action menu shows the dropdown toggle (i.e. `quick-dropdown-toggle` item is present) + - empty channel placeholder renders `EmptyStateIndicator` (or is suppressed via `EmptyPlaceholder={null}`) From 1fb0b16866a16491937ed9a4daedddce22e9bd79 Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 17 Apr 2026 10:31:07 +0200 Subject: [PATCH 2/7] docs(ai-migration): add Source of Truth section Instruct agents to verify against installed node_modules rather than relying on pretraining knowledge, which skews to v13 and frequently hallucinates removed APIs. --- ai-docs/ai-migration.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ai-docs/ai-migration.md b/ai-docs/ai-migration.md index 7c8ac5446..f163e293c 100644 --- a/ai-docs/ai-migration.md +++ b/ai-docs/ai-migration.md @@ -2,6 +2,24 @@ Execution-only guide for a coding agent migrating a third-party app from `stream-chat-react` v13 to v14. Run the phases in order; stop and fix each phase's errors before moving to the next. For evidence or edge cases, fall back to `./breaking-changes.md`. +## Source of Truth + +Before writing any code, and whenever ambiguity hits, verify against the user's installed SDK source. Never rely on training knowledge — the v13 surface is still heavily represented in pretraining data and will mislead you. + +Authoritative locations, in order of preference: + +1. `node_modules/stream-chat-react/dist/index.d.ts` (and `dist/index.d.cts`) — public type surface. Fastest way to confirm a symbol exists, check a prop signature, or see an override-key name. +2. `node_modules/stream-chat-react/package.json` — `exports` map and peer dependencies. +3. `node_modules/stream-chat-react/dist/` — transpiled JS when runtime behavior matters more than types. +4. `node_modules/stream-chat/dist/index.d.ts` — core client types (channel capabilities, event names, `ReactionSort`, etc.). +5. `node_modules/stream-chat-react/dist/css/index.css` — default class names and CSS variables when auditing selectors. + +Required workflow: + +- Before claiming a symbol, prop, or override key exists: grep the installed `.d.ts` files. +- Before writing a replacement snippet: read the current signature, don't reconstruct it from memory. +- If a file or symbol referenced in this guide is missing from the installed package, stop and report — don't invent a fallback. + ## Prerequisites 1. Upgrade both packages to their latest versions together. The v14 peer-dep floor moved; leaving `stream-chat` on a v13-era version will fail peer resolution. From 45380161ed8e1ca97cf749c667e6cbcba7ce4b5c Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 17 Apr 2026 10:46:11 +0200 Subject: [PATCH 3/7] docs(ai-migration): fix Source of Truth paths; drop dead scss exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .d.ts types are emitted under dist/types/, not dist/ root. Update the three affected paths and remove the incorrect .d.cts reference (the SDK emits .d.ts only). Point at dist/es/ and dist/cjs/ for transpiled JS. Also remove ./dist/scss/* and ./scss/* from the package.json exports map — dist/scss/ is no longer emitted, so those exports were dead. --- ai-docs/ai-migration.md | 6 +++--- package.json | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/ai-docs/ai-migration.md b/ai-docs/ai-migration.md index f163e293c..33ef49c83 100644 --- a/ai-docs/ai-migration.md +++ b/ai-docs/ai-migration.md @@ -8,10 +8,10 @@ Before writing any code, and whenever ambiguity hits, verify against the user's Authoritative locations, in order of preference: -1. `node_modules/stream-chat-react/dist/index.d.ts` (and `dist/index.d.cts`) — public type surface. Fastest way to confirm a symbol exists, check a prop signature, or see an override-key name. +1. `node_modules/stream-chat-react/dist/types/index.d.ts` — public type surface. Fastest way to confirm a symbol exists, check a prop signature, or see an override-key name. (The SDK emits `.d.ts` only; there is no `.d.cts`.) 2. `node_modules/stream-chat-react/package.json` — `exports` map and peer dependencies. -3. `node_modules/stream-chat-react/dist/` — transpiled JS when runtime behavior matters more than types. -4. `node_modules/stream-chat/dist/index.d.ts` — core client types (channel capabilities, event names, `ReactionSort`, etc.). +3. `node_modules/stream-chat-react/dist/es/` and `dist/cjs/` — transpiled JS when runtime behavior matters more than types. +4. `node_modules/stream-chat/dist/types/index.d.ts` — core client types (channel capabilities, event names, `ReactionSort`, etc.). 5. `node_modules/stream-chat-react/dist/css/index.css` — default class names and CSS variables when auditing selectors. Required workflow: diff --git a/package.json b/package.json index dac440d26..721e8ad7e 100644 --- a/package.json +++ b/package.json @@ -37,14 +37,8 @@ "./dist/css/*": { "default": "./dist/css/*" }, - "./dist/scss/*": { - "default": "./dist/scss/*" - }, "./css/*": { "default": "./dist/css/*" - }, - "./scss/*": { - "default": "./dist/scss/*" } }, "typesVersions": { From 03c3118a18ff852a94d836412d30a4df2886400e Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 17 Apr 2026 11:03:07 +0200 Subject: [PATCH 4/7] chore: remove legacy @stream-io/stream-chat-css wiring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDK no longer depends on @stream-io/stream-chat-css — the package is absent from package.json and yarn.lock, and dist/ has no scss/, css/v2/, or assets/ directories. Removes the now-dead tooling around it: - scripts/copy-css.sh: not wired into the build script and unreferenced outside of stale docs. - scripts/merge-stream-chat-css-docs.sh: copies docs into ./docusaurus/docs/React, a directory that no longer exists in this repo. - .github/dependabot.yml: drop the @stream-io/stream-chat-css auto-bump entry since the package isn't installed. Also update CLAUDE.md to reflect the current 4-step build (build-translations, vite build, tsc, build-styling) and the single- source styling tree under src/styling/. --- .github/dependabot.yml | 1 - CLAUDE.md | 46 ++++++++++----------------- scripts/copy-css.sh | 7 ---- scripts/merge-stream-chat-css-docs.sh | 20 ------------ 4 files changed, 16 insertions(+), 58 deletions(-) delete mode 100755 scripts/copy-css.sh delete mode 100755 scripts/merge-stream-chat-css-docs.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3aa3440f9..d7f7274cb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,6 @@ updates: schedule: interval: 'daily' allow: - - dependency-name: '@stream-io/stream-chat-css' - dependency-name: 'stream-chat' labels: - stream-dependencies diff --git a/CLAUDE.md b/CLAUDE.md index 0adc187ac..bd34e16ad 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ```bash # Development (requires Node 24 — see .nvmrc) yarn install # Setup -yarn build # Full build (CSS, translations, Vite, types, SCSS) +yarn build # Full build (translations, Vite, types, SCSS) yarn test # Run Jest tests yarn test # Run specific test (e.g., yarn test Channel) yarn lint-fix # Fix all lint/format issues (prettier + eslint) @@ -258,29 +258,23 @@ When deprecating, use `@deprecated` JSDoc tag with reason and docs link. Commit ## Build System -The build runs 5 steps in parallel via `concurrently`: +The build runs 4 steps in parallel via `concurrently`: -1. **`copy-css.sh`** — Copies pre-built CSS/SCSS/assets from `@stream-io/stream-chat-css` into `dist/` -2. **`build-translations`** — Extracts `t()` calls from source via `i18next-cli` -3. **`vite build`** — Bundles 3 entry points (index, emojis, mp3-encoder) as CJS + ESM, no minification -4. **`tsc`** — Generates `.d.ts` type declarations only (`tsconfig.lib.json`) -5. **`build-styling`** — Compiles `src/styling/index.scss` → `dist/css/index.css` +1. **`build-translations`** — Extracts `t()` calls from source via `i18next-cli` +2. **`vite build`** — Bundles 3 entry points (index, emojis, mp3-encoder) as CJS + ESM, no minification +3. **`tsc`** — Generates `.d.ts` type declarations only (`tsconfig.lib.json`) to `dist/types/` +4. **`build-styling`** — Compiles `src/styling/index.scss` → `dist/css/index.css` All steps write to separate directories under `dist/` so they don't conflict. ## Styling Architecture -### Dual-Layer CSS System - -This repo has **two style sources**: - -1. **`@stream-io/stream-chat-css`** (external dep) — Base design system. Copied to `dist/css/v2/` and `dist/scss/v2/` at build time. Organized as `*-layout.scss` (structure) + `*-theme.scss` (colors/typography) per component. -2. **`src/styling/`** (this repo) — Component styles, theme variables, animations. Compiled to `dist/css/index.css`. +All component styles live in `src/styling/` (master entry: `src/styling/index.scss`) and in `src/components/*/styling/index.scss`. The Sass build compiles the tree to `dist/css/index.css`. There is no longer any step that pulls CSS/SCSS from an external design-system package. ### CSS Layers (cascade order, low → high) ``` -css-reset → stream (v2 base) → stream-new (compiled index.css) → stream-overrides → stream-app-overrides +css-reset → stream-new (compiled index.css) → stream-overrides → stream-app-overrides ``` See `examples/vite/src/index.scss` for reference implementation. Layers eliminate the need for `!important`. @@ -300,23 +294,16 @@ See `examples/vite/src/index.scss` for reference implementation. Layers eliminat - **Date/time**: `Streami18n` class wraps i18next + Dayjs with per-locale calendar formats - **When adding translatable strings**: Use `t()` from `useTranslationContext()`, then run `yarn build-translations` to update JSON files. All 12 language files must have non-empty values. -## Styling Architecture - -### Dual-Layer CSS System - -Styles come from two sources: - -1. **`@stream-io/stream-chat-css`** — Base design system (copied to `dist/css/v2/` and `dist/scss/v2/` via `scripts/copy-css.sh`). Layout and theme SCSS split per component (`*-layout.scss` + `*-theme.scss`). -2. **`src/styling/`** — SDK-specific styles compiled to `dist/css/index.css` via Sass. Master entry: `src/styling/index.scss`. +## Styling Architecture (Theming & Build Details) -Component styles live in `src/components/*/styling/index.scss` and are imported by the master stylesheet. +All styles live in `src/styling/` (master entry: `src/styling/index.scss`) and in `src/components/*/styling/index.scss`. Component styles are imported by the master stylesheet and compiled to `dist/css/index.css` via Sass. ### CSS Layers & Theming CSS layers control cascade order (no `!important` needed): ``` -css-reset → stream (v2 base) → stream-new (compiled SDK CSS) → stream-overrides → stream-app-overrides +css-reset → stream-new (compiled SDK CSS) → stream-overrides → stream-app-overrides ``` See `examples/vite/src/index.scss` for the reference layer setup. @@ -329,13 +316,12 @@ See `examples/vite/src/index.scss` for the reference layer setup. ### Build System -`yarn build` runs 5 tasks in parallel via `concurrently`: +`yarn build` runs 4 tasks in parallel via `concurrently`: -1. `scripts/copy-css.sh` — Copies `stream-chat-css` assets into `dist/` -2. `yarn build-translations` — Extracts `t()` calls via `i18next-cli` -3. `vite build` — Bundles 3 entry points (index, emojis, mp3-encoder) as ESM + CJS -4. `tsc --project tsconfig.lib.json` — Generates `.d.ts` type declarations only -5. `yarn build-styling` — Compiles SCSS to `dist/css/index.css` +1. `yarn build-translations` — Extracts `t()` calls via `i18next-cli` +2. `vite build` — Bundles 3 entry points (index, emojis, mp3-encoder) as ESM + CJS +3. `tsc --project tsconfig.lib.json` — Generates `.d.ts` type declarations to `dist/types/` +4. `yarn build-styling` — Compiles SCSS to `dist/css/index.css` **Library entry points** (from `package.json` exports): diff --git a/scripts/copy-css.sh b/scripts/copy-css.sh deleted file mode 100755 index 64bba0a20..000000000 --- a/scripts/copy-css.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p dist/assets dist/css/v2 dist/scss/v2 - -cp -r node_modules/@stream-io/stream-chat-css/dist/assets/* dist/assets -cp -r node_modules/@stream-io/stream-chat-css/dist/v2/css/*.css dist/css/v2 -cp -r node_modules/@stream-io/stream-chat-css/dist/v2/scss/* dist/scss/v2 diff --git a/scripts/merge-stream-chat-css-docs.sh b/scripts/merge-stream-chat-css-docs.sh deleted file mode 100755 index 1cd6f4bb0..000000000 --- a/scripts/merge-stream-chat-css-docs.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -usage() { - echo "Missing path to stream-chat-css directory" - echo "Usage: $(basename $0) " -} - -main() { - if [ $# -eq 0 ]; then - usage - exit 1 - fi - - STREAM_CHAT_CSS_DOCS_PATH=$1; - cp -r "$STREAM_CHAT_CSS_DOCS_PATH"/* ./docusaurus/docs/React; -} - - -main $* -exit 0 From b6a220c19e25535f9afdc9cabdec3f7bf7ebefcd Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 17 Apr 2026 11:30:45 +0200 Subject: [PATCH 5/7] docs(ai-migration): fix signature/prop claims; prefer /css/* alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four SDK-mismatch fixes surfaced by validating the guide against node_modules/stream-chat-react/dist/types: - Hook name: useMessageComposer → useMessageComposerController. The former does not exist in v14. The human migration guide had the correct name; the AI guide had a hallucination. - usePinHandler v14 signature is (message) with no second arg; notifications are published internally via useNotificationApi(). - FileIcon props: `size` is not removed, only retyped from number to FileIconSize ('sm' | 'md' | 'lg' | 'xl'); new `sizeConfig` prop exists. Only `big`, `sizeSmall`, `type` were removed. - ModalGalleryProps: no `initialIndex`. Actual props are { items, className?, modalClassName?, closeOnBackgroundClick? }. Also recommend the /css/* alias over /dist/css/* for stylesheet imports: package.json exports map aliases it, so consumers do not need to depend on the internal dist/ layout. --- ai-docs/ai-migration.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ai-docs/ai-migration.md b/ai-docs/ai-migration.md index 33ef49c83..d5be31a52 100644 --- a/ai-docs/ai-migration.md +++ b/ai-docs/ai-migration.md @@ -25,16 +25,16 @@ Required workflow: 1. Upgrade both packages to their latest versions together. The v14 peer-dep floor moved; leaving `stream-chat` on a v13-era version will fail peer resolution. - `stream-chat-react` → latest v14 - `stream-chat` → latest -2. Update stylesheet imports. The `/v2` path no longer resolves. +2. Update stylesheet imports. The `/v2` path no longer resolves. Prefer the aliased `stream-chat-react/css/*` path over `stream-chat-react/dist/css/*` — the alias is defined in the package's `exports` map and hides the internal `dist/` layout. ```ts // before import 'stream-chat-react/dist/css/v2/index.css'; // after - import 'stream-chat-react/dist/css/index.css'; + import 'stream-chat-react/css/index.css'; ``` 3. If the app uses `EmojiPicker`, also import its dedicated stylesheet: ```ts - import 'stream-chat-react/dist/css/emoji-picker.css'; + import 'stream-chat-react/css/emoji-picker.css'; ``` --- @@ -118,10 +118,10 @@ const VirtualMessage = (props: MessageUIComponentProps) => Date: Fri, 17 Apr 2026 11:33:06 +0200 Subject: [PATCH 6/7] docs(breaking-changes): correct four SDK-mismatch claims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same four fixes applied to ai-migration.md, now also at source so the audit tracker matches the installed v14 SDK: - BC-008: useMessageComposer().initState(...) → useMessageComposerController().initState(...) (the former hook does not exist). Also correct the file ref to src/components/MessageActions/MessageActions.defaults.tsx:160. - BC-021: FileIconProps still exposes size (retyped to FileIconSize 'sm' | 'md' | 'lg' | 'xl') and adds sizeConfig; only big, sizeSmall, type were removed. - BC-025: ModalGallery no longer accepts initialIndex. Rename guidance now points consumers at Gallery + GalleryUI when a non-zero starting item is required. - BC-059: usePinHandler v14 signature is (message), not (message, notifications?). Notifications are published internally via useNotificationApi(). --- ai-docs/breaking-changes.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ai-docs/breaking-changes.md b/ai-docs/breaking-changes.md index 088ac399d..f98bb95cb 100644 --- a/ai-docs/breaking-changes.md +++ b/ai-docs/breaking-changes.md @@ -239,7 +239,7 @@ Only confirmed items should move from this file into the migration guide. - `src/context/MessageContext.tsx:143` `setTranslationView` - Replacement: - delete handlers should call `handleDelete(options?)` without an event argument - - edit flows now go through `MessageComposer`; current default message actions call `useMessageComposer().initState({ composition: message })` in `src/components/MessageActions/defaults.tsx:163` + - edit flows now go through `MessageComposer`; current default message actions call `useMessageComposerController().initState({ composition: message })` in `src/components/MessageActions/MessageActions.defaults.tsx:160` - custom message actions need to be implemented through the new `MessageActions` customization surface instead of `customMessageActions` - Evidence: - current `MessageContextValue` removes the old edit-state and custom-action fields @@ -763,11 +763,13 @@ Only confirmed items should move from this file into the migration guide. - `v13.14.2:src/components/ReactFileUtilities/FileIcon/FileIcon.tsx:6` `FileIconProps` exposed `big`, `filename`, `mimeType`, `size`, `sizeSmall`, and `type` - `v13.14.2:src/components/ReactFileUtilities/FileIcon/FileIcon.tsx:16` `mimeTypeToIcon(type, mimeType)` - New API: - - `src/components/FileIcon/FileIcon.tsx:5` `FileIconProps` expose only `className`, `fileName`, and `mimeType` + - `src/components/FileIcon/FileIcon.tsx:5` `FileIconProps` expose `className`, `fileName`, `mimeType`, `size`, and `sizeConfig` - `src/components/FileIcon/FileIcon.tsx:11` `mimeTypeToIcon(mimeType?)` + - `size` is retyped from a pixel number to `FileIconSize` (`'sm' | 'md' | 'lg' | 'xl'`); `sizeConfig` is a new optional per-size dimension override - Replacement: - rename `filename` to `fileName` - - remove `big`, `size`, `sizeSmall`, and `type` + - remove `big`, `sizeSmall`, and `type` + - migrate any custom numeric `size` values to one of `'sm' | 'md' | 'lg' | 'xl'`; use `sizeConfig` if custom pixel dimensions are required - style the rendered icon through `className` or a wrapper instead of size-mode props - Evidence: - current file-icon implementation uses an internal label renderer and no longer passes size variants into the icon set @@ -928,7 +930,7 @@ Only confirmed items should move from this file into the migration guide. - Replacement: - if you need the old thumbnail-grid-plus-lightbox behavior, use `ModalGallery` - if you customize the gallery carousel itself, migrate to the new `Gallery` provider API and a custom `GalleryUI` - - rename `ModalGallery` props from `images` / `index` to `items` / `initialIndex` semantics as appropriate + - rename `ModalGallery` props from `images` / `index` to `items`; `ModalGallery` no longer accepts an `index` / `initialIndex` prop (use `Gallery` + `GalleryUI` with `initialIndex` if a non-zero starting item is required) - rewrite custom `Image` components against the new `GalleryItem` contract and let `ModalGallery` own modal state - Evidence: - current `Gallery` is no longer a visible thumbnail-grid component by itself unless a `GalleryUI` is supplied @@ -2024,7 +2026,7 @@ Only confirmed items should move from this file into the migration guide. - Area: deprecated API cleanup - User impact: - imports or props using `pinPermissions`, `PinPermissions`, `PinEnabledUserRoles`, or `defaultPinPermissions` no longer compile - - custom `usePinHandler(message, pinPermissions, notifications)` calls must be rewritten to the new two-argument signature + - custom `usePinHandler(message, pinPermissions, notifications)` calls must be rewritten to the new one-argument signature `usePinHandler(message)` (notifications are now published internally via `useNotificationApi()`) - wrappers around `InfiniteScroll`, `LoadMoreButton`, and `LoadMorePaginator` can no longer use the deprecated alias props `hasMore`, `hasMoreNewer`, `loadMore`, `loadMoreNewer`, or `refreshing` - top-level imports using `UploadButton` / `UploadButtonProps` no longer resolve; use `FileInput` / `FileInputProps` instead - `ChannelListItemUI` no longer accepts the deprecated `latestMessage` alias; use `latestMessagePreview` @@ -2041,7 +2043,7 @@ Only confirmed items should move from this file into the migration guide. - `v13.14.2:src/plugins/Emojis/EmojiPicker.tsx:30` exposed `popperOptions?: Partial<{ placement: PopperLikePlacement }>` - `git grep` on `v13.14.2` finds public exports for `StreamEmoji`, `moveChannelUp`, `hasNotMoreMessages`, `useChannelDeletedListener`, `useNotificationMessageNewListener`, and the rest of the old standalone channel-list listener hooks - New API: - - `src/components/Message/hooks/usePinHandler.ts:16` through `:19` now accept only `(message, notifications?)` + - `src/components/Message/hooks/usePinHandler.ts:16` through `:19` now accept only `(message)` (no second argument) - current source has no `PinPermissions`, `PinEnabledUserRoles`, or `defaultPinPermissions` - `src/components/InfiniteScrollPaginator/InfiniteScroll.tsx:16` through `:23` now expose only `hasNextPage`, `hasPreviousPage`, `loadNextPage`, and `loadPreviousPage` through `PaginatorProps` - `src/types/types.ts:14` through `:18` expose `hasNextPage`, `hasPreviousPage`, and `isLoading`, with no `refreshing` @@ -2052,7 +2054,7 @@ Only confirmed items should move from this file into the migration guide. - current `rg` over `src` returns no `StreamEmoji`, `moveChannelUp`, `hasNotMoreMessages`, `useChannelDeletedListener`, `useNotificationMessageNewListener`, or sibling standalone listener-hook exports - Replacement: - remove `pinPermissions` customization and rely on `channelCapabilities['pin-message']` - - rewrite `usePinHandler(message, notifications?)` callers to the new signature + - rewrite `usePinHandler` callers to the new one-argument signature `usePinHandler(message)` - update paginator wrappers to `hasNextPage`, `hasPreviousPage`, `loadNextPage`, `loadPreviousPage`, and `isLoading` - replace top-level `UploadButton` imports with `FileInput` - replace `latestMessage` with `latestMessagePreview` From 07575d6dc9f986427cdc49104a84ae2a3262c69a Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 17 Apr 2026 12:13:54 +0200 Subject: [PATCH 7/7] docs(ai-migration): orient agents on the v14 notification and gallery models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two short orienting subsections that name the replacement primitives for pieces scattered across Phase 2 / Phase 5 / Phase 7: - Notification system (end of Phase 2): useNotificationApi(), NotificationList, useSystemNotifications() / useReportLostConnectionSystemNotification(). Consolidates the mental model behind MessageListNotifications / ConnectionStatus / get*Notification removals. - Gallery model (mid Phase 7): ModalGallery vs. Gallery vs. GalleryUI as three distinct primitives, with a migration shortcut for the common v13 usage. No teaching content — just enough to point consumers at the right primitives. Feature docs own the full usage. --- ai-docs/ai-migration.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ai-docs/ai-migration.md b/ai-docs/ai-migration.md index d5be31a52..5a823fd38 100644 --- a/ai-docs/ai-migration.md +++ b/ai-docs/ai-migration.md @@ -178,6 +178,16 @@ For richer rendering, override `QuotedMessage` or `QuotedMessagePreview` in `Wit - `InfiniteScroll` / `LoadMoreButton` / `LoadMorePaginator`: `hasMore`, `loadMore`, `refreshing` → `hasNextPage`, `loadNextPage`, `isLoading` - `InfiniteScroll` / `LoadMoreButton` / `LoadMorePaginator`: `hasMoreNewer`, `loadMoreNewer` → no alias; use `hasNextPage` / `loadNextPage` where appropriate +### About the v14 notification system + +The removed pieces above (`MessageListNotifications`, `ConnectionStatus`, `get*Notification` callbacks) were parts of ad-hoc flows. v14 consolidates them into three primitives: + +- **`useNotificationApi()`** — publish app-owned notifications (toasts, inline feedback). Replaces the old `get*Notification` callback props on `Message` / `MessageList` / `VirtualizedMessageList`. +- **`NotificationList`** — renders client-emitted notifications inside a panel. Replaces the removed `MessageListNotifications` wrapper. `MessageList`, `VirtualizedMessageList`, `ChannelList`, and `ThreadList` render it by default. +- **`useSystemNotifications()` / `useReportLostConnectionSystemNotification()`** — publish persistent system banners (connection status, etc.). Replaces `ConnectionStatus`. + +For custom notification text, prefer overriding `Streami18n` translation keys under the `notification` namespace over wrapping `useNotificationApi()` yourself. + --- ## Phase 3 — Move UI Overrides to `WithComponents` @@ -383,6 +393,19 @@ To preserve the old blank pane, override `EmptyStateIndicator` with a `null`-ret - **`Gallery` / `ModalGallery`.** `ModalGallery` API went from `{ images, index }` to `{ items }` (also accepts `className`, `modalClassName`, `closeOnBackgroundClick`). `Gallery` alone no longer renders a thumbnail grid — supply `GalleryUI` or use `ModalGallery` for the old behavior. - **Low-level attachment containers.** `MediaContainer` now takes `attachments` (plural). Gallery payloads changed `images` → `items`. Audio custom components: rename prop `og` → `attachment`. Native `giphy` stays inline (no `ModalGallery` expansion). - **`AttachmentProps.Gallery` → `AttachmentProps.ModalGallery`.** `Media` now uses `VideoPlayerProps`, not `ReactPlayerProps`. + +### About the v14 gallery model + +`Gallery`, `ModalGallery`, and `GalleryUI` are three distinct primitives in v14, not interchangeable: + +- **`ModalGallery`** — full replacement for the v13 "thumbnail grid that opens a lightbox" component. Takes `{ items }`. Use this when migrating direct `` usage. +- **`Gallery`** — provider-only. Exposes gallery state (active item, close handler) via `GalleryContext` but renders nothing visible unless a `GalleryUI` is supplied. +- **`GalleryUI`** — the visual layer consumed by `Gallery` / `GalleryContext`. Supply a custom one for a custom carousel / viewer. + +Migration shortcut: if the v13 usage was ``, change the import to `ModalGallery` and pass `items`. Only reach for the provider-style `Gallery` + custom `GalleryUI` if the app genuinely needs carousel customization. + +Remaining Phase 7 behavior bullets: + - **`PinIndicator`** no longer receives `t` as prop — use `useTranslationContext()` inside. - **Suggestion `UserItem`** no longer receives `Avatar` prop — render avatar/menu UI directly. - **Modal prompt components.** `MessageBouncePrompt`, `RecordingPermissionDeniedNotification`, poll `PollOptionsFullList` / `PollResults` / `PollAnswerList` / `SuggestPollOptionPrompt` no longer receive `onClose` / `close` props. Dismiss via `useModalContext().close()`.