diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3aa3440f9c..d7f7274cb3 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 0adc187ac6..bd34e16ad1 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/ai-docs/DEPRECATED_API_REMOVAL_PLAN.md b/ai-docs/DEPRECATED_API_REMOVAL_PLAN.md deleted file mode 100644 index 25c191345d..0000000000 --- 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 0000000000..5a823fd38f --- /dev/null +++ b/ai-docs/ai-migration.md @@ -0,0 +1,456 @@ +# 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`. + +## 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/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/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: + +- 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. + - `stream-chat-react` → latest v14 + - `stream-chat` → latest +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/css/index.css'; + ``` +3. If the app uses `EmojiPicker`, also import its dedicated stylesheet: + ```ts + import 'stream-chat-react/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 { useMessageComposerController, useMessageContext } from 'stream-chat-react'; + +const { message } = useMessageContext(); +const messageComposer = useMessageComposerController(); + +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)` (no second argument; notifications are published via `useNotificationApi()` internally) +- `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 + +### 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` + +`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`, `sizeSmall`, `type` removed. `size` is retyped from a pixel number to `FileIconSize` (`'sm' | 'md' | 'lg' | 'xl'`); new `sizeConfig` prop allows overriding per-size dimensions. `mimeTypeToIcon(type, mimeType)` → `mimeTypeToIcon(mimeType?)`. +- **`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()`. +- **`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/css/*` (preferred alias) or `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}`) diff --git a/ai-docs/breaking-changes.md b/ai-docs/breaking-changes.md index 088ac399d3..f98bb95cb1 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` diff --git a/package.json b/package.json index dac440d267..721e8ad7e8 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": { diff --git a/scripts/copy-css.sh b/scripts/copy-css.sh deleted file mode 100755 index 64bba0a208..0000000000 --- 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 1cd6f4bb07..0000000000 --- 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