stream-chat-react-native-core 9.0.0-beta.37 → 9.0.0-beta.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,18 @@
1
1
  import React from 'react';
2
+ import { ImageProps, TextInputProps } from 'react-native';
3
+ import type { LocalMessage, UserResponse } from 'stream-chat';
4
+ import type { MessageTextProps } from '../../components/Message/MessageItemView/MessageTextContainer';
2
5
  import type { MessageActionsProps } from '../../contexts/overlayContext/MessageOverlayHostLayer';
3
6
  /**
4
- * All default component implementations used across the SDK.
5
- * These are the components used when no overrides are provided via WithComponents.
7
+ * Normalizes each component entry to React.ComponentType<P>, stripping
8
+ * extra inferred properties (like `displayName: string` from runtime
9
+ * assignments) that would otherwise leak into the override types and
10
+ * force integrators to match them.
6
11
  */
7
- export declare const DEFAULT_COMPONENTS: {
12
+ type NormalizeComponents<T> = {
13
+ [K in keyof T]: T[K] extends React.ComponentType<infer P> ? React.ComponentType<P> : T[K];
14
+ };
15
+ declare const components: {
8
16
  Attachment: (props: import("../../components/Attachment/Attachment").AttachmentProps) => React.JSX.Element | null;
9
17
  AttachButton: {
10
18
  (props: import("../../components/MessageInput/components/InputButtons/AttachButton").AttachButtonProps): React.JSX.Element;
@@ -279,20 +287,40 @@ export declare const DEFAULT_COMPONENTS: {
279
287
  };
280
288
  ImageGalleryVideoControls: React.MemoExoticComponent<(props: import("../..").ImageGalleryVideoControlProps) => React.JSX.Element>;
281
289
  MessageOverlayBackground: () => React.JSX.Element;
282
- ImageComponent: React.ComponentType<any>;
283
- AttachmentPickerIOSSelectMorePhotos: React.ComponentType<any> | undefined;
284
- ChatLoadingIndicator: React.ComponentType<any> | null | undefined;
285
- CreatePollContent: React.ComponentType<any> | undefined;
286
- MessageActions: React.ComponentType<MessageActionsProps> | undefined;
287
- Input: React.ComponentType<any> | undefined;
288
- ListHeaderComponent: React.ComponentType<any> | undefined;
289
- MessageContentBottomView: React.ComponentType<any> | undefined;
290
- MessageContentLeadingView: React.ComponentType<any> | undefined;
291
- MessageContentTopView: React.ComponentType<any> | undefined;
292
- MessageContentTrailingView: React.ComponentType<any> | undefined;
293
- MessageLocation: React.ComponentType<any> | undefined;
294
- MessageSpacer: React.ComponentType<any> | undefined;
295
- MessageText: React.ComponentType<any> | undefined;
296
- PollContent: React.ComponentType<any> | undefined;
290
+ ImageComponent: React.ComponentType<ImageProps>;
297
291
  };
292
+ /**
293
+ * Optional component slots that have no default implementation.
294
+ * These are `undefined` unless the integrator provides them via WithComponents.
295
+ */
296
+ export interface OptionalComponentOverrides {
297
+ AttachmentPickerIOSSelectMorePhotos?: React.ComponentType;
298
+ ChatLoadingIndicator?: React.ComponentType | null;
299
+ CreatePollContent?: React.ComponentType;
300
+ Input?: React.ComponentType<{
301
+ additionalTextInputProps?: TextInputProps;
302
+ getUsers: () => UserResponse[];
303
+ }>;
304
+ ListHeaderComponent?: React.ComponentType;
305
+ MessageActions?: React.ComponentType<MessageActionsProps>;
306
+ MessageContentBottomView?: React.ComponentType;
307
+ MessageContentLeadingView?: React.ComponentType;
308
+ MessageContentTopView?: React.ComponentType;
309
+ MessageContentTrailingView?: React.ComponentType;
310
+ MessageLocation?: React.ComponentType<{
311
+ message: LocalMessage;
312
+ }>;
313
+ MessageSpacer?: React.ComponentType;
314
+ MessageText?: React.ComponentType<MessageTextProps>;
315
+ PollContent?: React.ComponentType;
316
+ }
317
+ /**
318
+ * All default component implementations used across the SDK.
319
+ * These are the components used when no overrides are provided via WithComponents.
320
+ *
321
+ * The `NormalizeComponents` cast ensures that internal details like
322
+ * `displayName: string` don't leak into the public override types.
323
+ */
324
+ export declare const DEFAULT_COMPONENTS: NormalizeComponents<typeof components>;
325
+ export {};
298
326
  //# sourceMappingURL=defaultComponents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaultComponents.d.ts","sourceRoot":"","sources":["../../../../src/contexts/componentsContext/defaultComponents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAkJ1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAC;AAEjG;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAuJJ,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC;yCAIC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;0BAEnD,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS;uBAE9C,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;oBACvC,KAAK,CAAC,aAAa,CAAC,mBAAmB,CAAC,GAAG,SAAS;WAE7D,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;yBAEtB,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;8BAE/B,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;+BAEnC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;2BAExC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;gCAE/B,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;qBAE/C,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;mBAEtC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;iBAEtC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;iBAEpC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS;CAC/D,CAAC"}
1
+ {"version":3,"file":"defaultComponents.d.ts","sourceRoot":"","sources":["../../../../src/contexts/componentsContext/defaultComponents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAS,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiE9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+DAA+D,CAAC;AAiFtG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAC;AAEjG;;;;;GAKG;AACH,KAAK,mBAAmB,CAAC,CAAC,IAAI;KAC3B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC1F,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAsJW,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC;CACzD,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC,mCAAmC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC1D,oBAAoB,CAAC,EAAE,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;IAClD,iBAAiB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACxC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAC1B,wBAAwB,CAAC,EAAE,cAAc,CAAC;QAC1C,QAAQ,EAAE,MAAM,YAAY,EAAE,CAAC;KAChC,CAAC,CAAC;IACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC1C,cAAc,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC;IAC1D,wBAAwB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC/C,yBAAyB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAChD,qBAAqB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5C,0BAA0B,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACjD,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACjE,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACpC,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACpD,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CACnC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,mBAAmB,CAAC,OAAO,UAAU,CAAc,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stream-chat-react-native-core",
3
3
  "description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
4
- "version": "9.0.0-beta.37",
4
+ "version": "9.0.0-beta.38",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
@@ -9,7 +9,8 @@ import React, { PropsWithChildren, useContext, useMemo } from 'react';
9
9
  */
10
10
  export type ComponentOverrides = Partial<
11
11
  (typeof import('./defaultComponents'))['DEFAULT_COMPONENTS']
12
- >;
12
+ > &
13
+ import('./defaultComponents').OptionalComponentOverrides;
13
14
 
14
15
  const ComponentsContext = React.createContext<ComponentOverrides>({});
15
16
 
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
- import { Image } from 'react-native';
2
+ import { Image, ImageProps, TextInputProps } from 'react-native';
3
+
4
+ import type { LocalMessage, UserResponse } from 'stream-chat';
3
5
 
4
6
  import { Attachment } from '../../components/Attachment/Attachment';
5
7
  import { AudioAttachment } from '../../components/Attachment/Audio';
@@ -64,6 +66,7 @@ import { MessageReplies } from '../../components/Message/MessageItemView/Message
64
66
  import { MessageRepliesAvatars } from '../../components/Message/MessageItemView/MessageRepliesAvatars';
65
67
  import { MessageStatus } from '../../components/Message/MessageItemView/MessageStatus';
66
68
  import { MessageSwipeContent } from '../../components/Message/MessageItemView/MessageSwipeContent';
69
+ import type { MessageTextProps } from '../../components/Message/MessageItemView/MessageTextContainer';
67
70
  import { MessageTimestamp } from '../../components/Message/MessageItemView/MessageTimestamp';
68
71
  import { ReactionListBottom } from '../../components/Message/MessageItemView/ReactionList/ReactionListBottom';
69
72
  import { ReactionListClustered } from '../../components/Message/MessageItemView/ReactionList/ReactionListClustered';
@@ -147,10 +150,16 @@ import { DefaultMessageOverlayBackground } from '../../contexts/overlayContext/M
147
150
  import type { MessageActionsProps } from '../../contexts/overlayContext/MessageOverlayHostLayer';
148
151
 
149
152
  /**
150
- * All default component implementations used across the SDK.
151
- * These are the components used when no overrides are provided via WithComponents.
153
+ * Normalizes each component entry to React.ComponentType<P>, stripping
154
+ * extra inferred properties (like `displayName: string` from runtime
155
+ * assignments) that would otherwise leak into the override types and
156
+ * force integrators to match them.
152
157
  */
153
- export const DEFAULT_COMPONENTS = {
158
+ type NormalizeComponents<T> = {
159
+ [K in keyof T]: T[K] extends React.ComponentType<infer P> ? React.ComponentType<P> : T[K];
160
+ };
161
+
162
+ const components = {
154
163
  Attachment,
155
164
  AttachButton,
156
165
  AttachmentPickerContent,
@@ -300,35 +309,38 @@ export const DEFAULT_COMPONENTS = {
300
309
  MessageOverlayBackground: DefaultMessageOverlayBackground,
301
310
 
302
311
  // Image
303
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
304
- ImageComponent: Image as React.ComponentType<any>,
305
-
306
- // Optional overrides (no defaults — undefined unless user provides via WithComponents)
307
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
308
- AttachmentPickerIOSSelectMorePhotos: undefined as React.ComponentType<any> | undefined,
309
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
310
- ChatLoadingIndicator: undefined as React.ComponentType<any> | null | undefined,
311
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
312
- CreatePollContent: undefined as React.ComponentType<any> | undefined,
313
- MessageActions: undefined as React.ComponentType<MessageActionsProps> | undefined,
314
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
315
- Input: undefined as React.ComponentType<any> | undefined,
316
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
317
- ListHeaderComponent: undefined as React.ComponentType<any> | undefined,
318
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
319
- MessageContentBottomView: undefined as React.ComponentType<any> | undefined,
320
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
321
- MessageContentLeadingView: undefined as React.ComponentType<any> | undefined,
322
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
323
- MessageContentTopView: undefined as React.ComponentType<any> | undefined,
324
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
325
- MessageContentTrailingView: undefined as React.ComponentType<any> | undefined,
326
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
327
- MessageLocation: undefined as React.ComponentType<any> | undefined,
328
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
329
- MessageSpacer: undefined as React.ComponentType<any> | undefined,
330
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
331
- MessageText: undefined as React.ComponentType<any> | undefined,
332
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
333
- PollContent: undefined as React.ComponentType<any> | undefined,
312
+ ImageComponent: Image as React.ComponentType<ImageProps>,
334
313
  };
314
+
315
+ /**
316
+ * Optional component slots that have no default implementation.
317
+ * These are `undefined` unless the integrator provides them via WithComponents.
318
+ */
319
+ export interface OptionalComponentOverrides {
320
+ AttachmentPickerIOSSelectMorePhotos?: React.ComponentType;
321
+ ChatLoadingIndicator?: React.ComponentType | null;
322
+ CreatePollContent?: React.ComponentType;
323
+ Input?: React.ComponentType<{
324
+ additionalTextInputProps?: TextInputProps;
325
+ getUsers: () => UserResponse[];
326
+ }>;
327
+ ListHeaderComponent?: React.ComponentType;
328
+ MessageActions?: React.ComponentType<MessageActionsProps>;
329
+ MessageContentBottomView?: React.ComponentType;
330
+ MessageContentLeadingView?: React.ComponentType;
331
+ MessageContentTopView?: React.ComponentType;
332
+ MessageContentTrailingView?: React.ComponentType;
333
+ MessageLocation?: React.ComponentType<{ message: LocalMessage }>;
334
+ MessageSpacer?: React.ComponentType;
335
+ MessageText?: React.ComponentType<MessageTextProps>;
336
+ PollContent?: React.ComponentType;
337
+ }
338
+
339
+ /**
340
+ * All default component implementations used across the SDK.
341
+ * These are the components used when no overrides are provided via WithComponents.
342
+ *
343
+ * The `NormalizeComponents` cast ensures that internal details like
344
+ * `displayName: string` don't leak into the public override types.
345
+ */
346
+ export const DEFAULT_COMPONENTS: NormalizeComponents<typeof components> = components;
package/src/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "9.0.0-beta.37"
2
+ "version": "9.0.0-beta.38"
3
3
  }
@@ -1,148 +0,0 @@
1
- # WithComponents — Component Override System
2
-
3
- ## Design Principle
4
-
5
- **All components are read from `useComponentsContext()`. All other contexts only provide data + APIs — never components.**
6
-
7
- ## Current State (Completed)
8
-
9
- ### What was done
10
-
11
- 1. **Created `ComponentsContext`** — `WithComponents` provider, `useComponentsContext()` hook, `ComponentOverrides` type
12
- 2. **Created `defaultComponents.ts`** — centralized map of all ~130 default components
13
- 3. **Stripped component keys** from all existing context types: `MessagesContextValue`, `InputMessageInputContextValue`, `ChannelContextValue`, `ChannelsContextValue`, `AttachmentPickerContextValue`, `ThreadsContextValue`, `ImageGalleryContextValue`
14
- 4. **Simplified `useCreate*Context` hooks** — no longer receive or forward component params
15
- 5. **Simplified `Channel.tsx`** — removed ~90 component imports, prop defaults, forwarding lines
16
- 6. **Simplified `ChannelList.tsx`** — removed ~19 component props
17
- 7. **Updated ~80 consumer files** — switched from old context hooks to `useComponentsContext()`
18
- 8. **Removed component override props** from ALL individual components
19
- 9. **Updated all 3 example apps** (SampleApp, ExpoMessaging, TypeScriptMessaging)
20
- 10. **Updated ~45 documentation pages** across docs-content repo
21
- 11. **Merged with develop** and resolved conflicts
22
-
23
- ### Architecture
24
-
25
- ```
26
- User: <WithComponents overrides={{ Message: Custom }}>
27
-
28
- ComponentsContext (merges parent + overrides, inner wins)
29
-
30
- useComponentsContext() → { ...DEFAULT_COMPONENTS, ...overrides }
31
-
32
- Consumer: const { Message } = useComponentsContext()
33
- ```
34
-
35
- ### Key Files
36
-
37
- | File | Purpose |
38
- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
39
- | `ComponentsContext.tsx` | ~60 lines. `ComponentOverrides` type (derived from `typeof DEFAULT_COMPONENTS`), `WithComponents` provider, `useComponentsContext()` hook |
40
- | `defaultComponents.ts` | ~300 lines. Single source of truth for all default component mappings. Adding a new component here auto-extends `ComponentOverrides` |
41
-
42
- ### Type System
43
-
44
- `ComponentOverrides` is derived automatically:
45
-
46
- ```ts
47
- export type ComponentOverrides = Partial<(typeof import('./defaultComponents'))['DEFAULT_COMPONENTS']>;
48
- ```
49
-
50
- No manual type maintenance — add a component to `DEFAULT_COMPONENTS` and the type updates.
51
-
52
- ### Circular Dependency Handling
53
-
54
- `defaultComponents.ts` → imports components → components import `useComponentsContext` from `ComponentsContext.tsx`.
55
-
56
- Broken by lazy-loading defaults in the hook:
57
-
58
- ```ts
59
- let cachedDefaults: ComponentOverrides | undefined;
60
- const getDefaults = () => {
61
- if (!cachedDefaults) {
62
- cachedDefaults = require('./defaultComponents').DEFAULT_COMPONENTS;
63
- }
64
- return cachedDefaults;
65
- };
66
- ```
67
-
68
- ### Naming Conventions
69
-
70
- Some component keys differ from their default component names to avoid collisions:
71
-
72
- | Override Key | Default Component | Why renamed |
73
- | --------------------------------------- | --------------------------------------- | ---------------------------------------------------------- |
74
- | `FileAttachmentIcon` | `FileIcon` | Clarity |
75
- | `ChannelListLoadingIndicator` | `ChannelListLoadingIndicator` | Split from shared `LoadingIndicator` — renders skeleton UI |
76
- | `MessageListLoadingIndicator` | `LoadingIndicator` | Split from shared `LoadingIndicator` — renders text |
77
- | `ChatLoadingIndicator` | `undefined` | Optional, no default |
78
- | `ThreadMessageComposer` | `MessageComposer` | Avoid collision with `MessageComposer` component name |
79
- | `ThreadListComponent` | `DefaultThreadListComponent` | Avoid collision with exported `ThreadList` |
80
- | `StartAudioRecordingButton` | `AudioRecordingButton` | Historical naming |
81
- | `ChannelPreview` | `ChannelPreviewView` | ChannelList preview item |
82
- | `ChannelPreviewAvatar` | `ChannelAvatar` | ChannelList preview avatar |
83
- | `ChannelListFooterLoadingIndicator` | `ChannelListFooterLoadingIndicator` | ChannelList footer |
84
- | `ChannelListHeaderErrorIndicator` | `ChannelListHeaderErrorIndicator` | ChannelList header |
85
- | `ChannelListHeaderNetworkDownIndicator` | `ChannelListHeaderNetworkDownIndicator` | ChannelList header |
86
-
87
- ### Optional Components (no default)
88
-
89
- These exist in `DEFAULT_COMPONENTS` as `undefined` with `React.ComponentType<any> | undefined` type assertions:
90
-
91
- `AttachmentPickerIOSSelectMorePhotos`, `ChatLoadingIndicator`, `CreatePollContent`, `ImageComponent`, `Input`, `ListHeaderComponent`, `MessageActions`, `MessageContentBottomView`, `MessageContentLeadingView`, `MessageContentTopView`, `MessageContentTrailingView`, `MessageLocation`, `MessageSpacer`, `MessageText`, `PollContent`
92
-
93
- ### Shared Component Keys (audited)
94
-
95
- Some keys were used in multiple contexts before the refactor. Audit results:
96
-
97
- | Key | Used By | Same Default? | Resolution |
98
- | ----------------------- | --------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------- |
99
- | `EmptyStateIndicator` | Channel + ChannelList | Yes (differentiates via `listType` prop) | Single key ✅ |
100
- | `LoadingErrorIndicator` | Channel + ChannelList | Yes (differentiates via `listType` prop) | Single key ✅ |
101
- | `LoadingIndicator` | Channel + ChannelList | **No** — Channel used text-based, ChannelList used skeleton | Split into `MessageListLoadingIndicator` + `ChannelListLoadingIndicator` ✅ |
102
-
103
- ### API Alignment with stream-chat-react
104
-
105
- | Aspect | React Native | React Web |
106
- | --------- | ----------------------------------- | -------------------------------------- |
107
- | Provider | `WithComponents` | `WithComponents` |
108
- | Prop name | `overrides` | `overrides` |
109
- | Hook | `useComponentsContext()` | `useComponentContext()` |
110
- | Type | `ComponentOverrides` (auto-derived) | `ComponentContextValue` (hand-written) |
111
- | Defaults | Lazy-loaded via `require()` | Set at `Channel` level |
112
- | Merge | `useMemo` | Plain spread (no memo) |
113
-
114
- ## Known Issues / Future Work
115
-
116
- ### Pre-existing Test Failures (not caused by this work)
117
-
118
- These test suites fail on `develop` too:
119
-
120
- - `offline-support/index.test.ts` — timeout
121
- - `ChannelList.test.js` — filter race condition (`channel.countUnread` mock missing)
122
- - `isAttachmentEqualHandler.test.js`, `MessageContent.test.js`, `MessageTextContainer.test.tsx`, `MessageUserReactions.test.tsx`, `ChannelPreview.test.tsx` — various pre-existing issues
123
-
124
- ### Linter Interaction
125
-
126
- `@typescript-eslint/no-unused-vars` (warn, max-warnings 0) aggressively strips unused type keys. When adding new keys to `ComponentOverrides`, the type and its consumer must land in the same edit — otherwise the linter removes the key between saves.
127
-
128
- Since `ComponentOverrides` is now auto-derived from `DEFAULT_COMPONENTS`, this is no longer an issue for the type itself. But be aware when adding optional components (`undefined as React.ComponentType<any> | undefined`).
129
-
130
- ### `contexts/index.ts` Barrel Export
131
-
132
- The `export * from './componentsContext/ComponentsContext'` line in `contexts/index.ts` was stripped by the linter multiple times during development. If `WithComponents` becomes unexportable from the package, check this barrel file first.
133
-
134
- ### Documentation
135
-
136
- Docs PR: https://github.com/GetStream/docs-content/pull/1169
137
-
138
- Updated ~45 pages across:
139
-
140
- - Core teaching pages (custom_components, message-customization, etc.)
141
- - Component reference pages (channel-list, message-list, message-composer, etc.)
142
- - Context docs (stripped component keys from 7 context pages)
143
- - Migration guide (upgrading-from-v8.md — comprehensive WithComponents section)
144
- - Advanced guides (audio, AI, image-picker, etc.)
145
-
146
- ### SDK PR
147
-
148
- https://github.com/GetStream/stream-chat-react-native/pull/3542
@@ -1,148 +0,0 @@
1
- # WithComponents — Component Override System
2
-
3
- ## Design Principle
4
-
5
- **All components are read from `useComponentsContext()`. All other contexts only provide data + APIs — never components.**
6
-
7
- ## Current State (Completed)
8
-
9
- ### What was done
10
-
11
- 1. **Created `ComponentsContext`** — `WithComponents` provider, `useComponentsContext()` hook, `ComponentOverrides` type
12
- 2. **Created `defaultComponents.ts`** — centralized map of all ~130 default components
13
- 3. **Stripped component keys** from all existing context types: `MessagesContextValue`, `InputMessageInputContextValue`, `ChannelContextValue`, `ChannelsContextValue`, `AttachmentPickerContextValue`, `ThreadsContextValue`, `ImageGalleryContextValue`
14
- 4. **Simplified `useCreate*Context` hooks** — no longer receive or forward component params
15
- 5. **Simplified `Channel.tsx`** — removed ~90 component imports, prop defaults, forwarding lines
16
- 6. **Simplified `ChannelList.tsx`** — removed ~19 component props
17
- 7. **Updated ~80 consumer files** — switched from old context hooks to `useComponentsContext()`
18
- 8. **Removed component override props** from ALL individual components
19
- 9. **Updated all 3 example apps** (SampleApp, ExpoMessaging, TypeScriptMessaging)
20
- 10. **Updated ~45 documentation pages** across docs-content repo
21
- 11. **Merged with develop** and resolved conflicts
22
-
23
- ### Architecture
24
-
25
- ```
26
- User: <WithComponents overrides={{ Message: Custom }}>
27
-
28
- ComponentsContext (merges parent + overrides, inner wins)
29
-
30
- useComponentsContext() → { ...DEFAULT_COMPONENTS, ...overrides }
31
-
32
- Consumer: const { Message } = useComponentsContext()
33
- ```
34
-
35
- ### Key Files
36
-
37
- | File | Purpose |
38
- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
39
- | `ComponentsContext.tsx` | ~60 lines. `ComponentOverrides` type (derived from `typeof DEFAULT_COMPONENTS`), `WithComponents` provider, `useComponentsContext()` hook |
40
- | `defaultComponents.ts` | ~300 lines. Single source of truth for all default component mappings. Adding a new component here auto-extends `ComponentOverrides` |
41
-
42
- ### Type System
43
-
44
- `ComponentOverrides` is derived automatically:
45
-
46
- ```ts
47
- export type ComponentOverrides = Partial<(typeof import('./defaultComponents'))['DEFAULT_COMPONENTS']>;
48
- ```
49
-
50
- No manual type maintenance — add a component to `DEFAULT_COMPONENTS` and the type updates.
51
-
52
- ### Circular Dependency Handling
53
-
54
- `defaultComponents.ts` → imports components → components import `useComponentsContext` from `ComponentsContext.tsx`.
55
-
56
- Broken by lazy-loading defaults in the hook:
57
-
58
- ```ts
59
- let cachedDefaults: ComponentOverrides | undefined;
60
- const getDefaults = () => {
61
- if (!cachedDefaults) {
62
- cachedDefaults = require('./defaultComponents').DEFAULT_COMPONENTS;
63
- }
64
- return cachedDefaults;
65
- };
66
- ```
67
-
68
- ### Naming Conventions
69
-
70
- Some component keys differ from their default component names to avoid collisions:
71
-
72
- | Override Key | Default Component | Why renamed |
73
- | --------------------------------------- | --------------------------------------- | ---------------------------------------------------------- |
74
- | `FileAttachmentIcon` | `FileIcon` | Clarity |
75
- | `ChannelListLoadingIndicator` | `ChannelListLoadingIndicator` | Split from shared `LoadingIndicator` — renders skeleton UI |
76
- | `MessageListLoadingIndicator` | `LoadingIndicator` | Split from shared `LoadingIndicator` — renders text |
77
- | `ChatLoadingIndicator` | `undefined` | Optional, no default |
78
- | `ThreadMessageComposer` | `MessageComposer` | Avoid collision with `MessageComposer` component name |
79
- | `ThreadListComponent` | `DefaultThreadListComponent` | Avoid collision with exported `ThreadList` |
80
- | `StartAudioRecordingButton` | `AudioRecordingButton` | Historical naming |
81
- | `ChannelPreview` | `ChannelPreviewView` | ChannelList preview item |
82
- | `ChannelPreviewAvatar` | `ChannelAvatar` | ChannelList preview avatar |
83
- | `ChannelListFooterLoadingIndicator` | `ChannelListFooterLoadingIndicator` | ChannelList footer |
84
- | `ChannelListHeaderErrorIndicator` | `ChannelListHeaderErrorIndicator` | ChannelList header |
85
- | `ChannelListHeaderNetworkDownIndicator` | `ChannelListHeaderNetworkDownIndicator` | ChannelList header |
86
-
87
- ### Optional Components (no default)
88
-
89
- These exist in `DEFAULT_COMPONENTS` as `undefined` with `React.ComponentType<any> | undefined` type assertions:
90
-
91
- `AttachmentPickerIOSSelectMorePhotos`, `ChatLoadingIndicator`, `CreatePollContent`, `ImageComponent`, `Input`, `ListHeaderComponent`, `MessageActions`, `MessageContentBottomView`, `MessageContentLeadingView`, `MessageContentTopView`, `MessageContentTrailingView`, `MessageLocation`, `MessageSpacer`, `MessageText`, `PollContent`
92
-
93
- ### Shared Component Keys (audited)
94
-
95
- Some keys were used in multiple contexts before the refactor. Audit results:
96
-
97
- | Key | Used By | Same Default? | Resolution |
98
- | ----------------------- | --------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------- |
99
- | `EmptyStateIndicator` | Channel + ChannelList | Yes (differentiates via `listType` prop) | Single key ✅ |
100
- | `LoadingErrorIndicator` | Channel + ChannelList | Yes (differentiates via `listType` prop) | Single key ✅ |
101
- | `LoadingIndicator` | Channel + ChannelList | **No** — Channel used text-based, ChannelList used skeleton | Split into `MessageListLoadingIndicator` + `ChannelListLoadingIndicator` ✅ |
102
-
103
- ### API Alignment with stream-chat-react
104
-
105
- | Aspect | React Native | React Web |
106
- | --------- | ----------------------------------- | -------------------------------------- |
107
- | Provider | `WithComponents` | `WithComponents` |
108
- | Prop name | `overrides` | `overrides` |
109
- | Hook | `useComponentsContext()` | `useComponentContext()` |
110
- | Type | `ComponentOverrides` (auto-derived) | `ComponentContextValue` (hand-written) |
111
- | Defaults | Lazy-loaded via `require()` | Set at `Channel` level |
112
- | Merge | `useMemo` | Plain spread (no memo) |
113
-
114
- ## Known Issues / Future Work
115
-
116
- ### Pre-existing Test Failures (not caused by this work)
117
-
118
- These test suites fail on `develop` too:
119
-
120
- - `offline-support/index.test.ts` — timeout
121
- - `ChannelList.test.js` — filter race condition (`channel.countUnread` mock missing)
122
- - `isAttachmentEqualHandler.test.js`, `MessageContent.test.js`, `MessageTextContainer.test.tsx`, `MessageUserReactions.test.tsx`, `ChannelPreview.test.tsx` — various pre-existing issues
123
-
124
- ### Linter Interaction
125
-
126
- `@typescript-eslint/no-unused-vars` (warn, max-warnings 0) aggressively strips unused type keys. When adding new keys to `ComponentOverrides`, the type and its consumer must land in the same edit — otherwise the linter removes the key between saves.
127
-
128
- Since `ComponentOverrides` is now auto-derived from `DEFAULT_COMPONENTS`, this is no longer an issue for the type itself. But be aware when adding optional components (`undefined as React.ComponentType<any> | undefined`).
129
-
130
- ### `contexts/index.ts` Barrel Export
131
-
132
- The `export * from './componentsContext/ComponentsContext'` line in `contexts/index.ts` was stripped by the linter multiple times during development. If `WithComponents` becomes unexportable from the package, check this barrel file first.
133
-
134
- ### Documentation
135
-
136
- Docs PR: https://github.com/GetStream/docs-content/pull/1169
137
-
138
- Updated ~45 pages across:
139
-
140
- - Core teaching pages (custom_components, message-customization, etc.)
141
- - Component reference pages (channel-list, message-list, message-composer, etc.)
142
- - Context docs (stripped component keys from 7 context pages)
143
- - Migration guide (upgrading-from-v8.md — comprehensive WithComponents section)
144
- - Advanced guides (audio, AI, image-picker, etc.)
145
-
146
- ### SDK PR
147
-
148
- https://github.com/GetStream/stream-chat-react-native/pull/3542