stream-chat-react 13.13.0 → 13.13.2

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.
Files changed (43) hide show
  1. package/INTEGRATION_GUIDE.md +423 -0
  2. package/dist/components/Attachment/AttachmentActions.js +7 -2
  3. package/dist/components/Chat/hooks/useChat.js +1 -1
  4. package/dist/components/Message/ReminderNotification.js +2 -2
  5. package/dist/components/Message/hooks/useMuteHandler.js +2 -2
  6. package/dist/components/MessageInput/AttachmentPreviewList/AttachmentPreviewList.js +1 -1
  7. package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.d.ts +2 -1
  8. package/dist/components/MessageInput/AttachmentPreviewList/FileAttachmentPreview.js +3 -2
  9. package/dist/components/MessageInput/AttachmentPreviewList/ImageAttachmentPreview.js +1 -1
  10. package/dist/components/MessageInput/AttachmentPreviewList/VoiceRecordingPreview.js +1 -1
  11. package/dist/components/MessageInput/SendButton.js +3 -1
  12. package/dist/components/Poll/PollCreationDialog/MultipleAnswersField.js +6 -2
  13. package/dist/components/Poll/PollCreationDialog/NameField.js +5 -2
  14. package/dist/components/Poll/PollCreationDialog/OptionFieldSet.js +6 -2
  15. package/dist/components/TextareaComposer/SuggestionList/CommandItem.js +21 -3
  16. package/dist/css/v2/index.css +1 -1
  17. package/dist/css/v2/index.layout.css +1 -1
  18. package/dist/experimental/Search/SearchResults/SearchResultsHeader.js +7 -2
  19. package/dist/experimental/Search/SearchResults/SearchSourceResultsLoadingIndicator.js +3 -1
  20. package/dist/experimental/index.browser.cjs +18 -2
  21. package/dist/experimental/index.browser.cjs.map +2 -2
  22. package/dist/experimental/index.node.cjs +18 -2
  23. package/dist/experimental/index.node.cjs.map +2 -2
  24. package/dist/i18n/Streami18n.d.ts +90 -78
  25. package/dist/i18n/de.json +91 -89
  26. package/dist/i18n/en.json +91 -79
  27. package/dist/i18n/es.json +99 -97
  28. package/dist/i18n/fr.json +99 -97
  29. package/dist/i18n/hi.json +91 -89
  30. package/dist/i18n/it.json +99 -97
  31. package/dist/i18n/ja.json +88 -86
  32. package/dist/i18n/ko.json +88 -86
  33. package/dist/i18n/nl.json +91 -89
  34. package/dist/i18n/pt.json +99 -97
  35. package/dist/i18n/ru.json +104 -102
  36. package/dist/i18n/tr.json +91 -89
  37. package/dist/index.browser.cjs +1607 -1518
  38. package/dist/index.browser.cjs.map +2 -2
  39. package/dist/index.node.cjs +1607 -1518
  40. package/dist/index.node.cjs.map +2 -2
  41. package/dist/scss/v2/Message/Message-layout.scss +1 -0
  42. package/dist/scss/v2/Poll/Poll-layout.scss +4 -0
  43. package/package.json +9 -8
@@ -0,0 +1,423 @@
1
+ # Stream Chat React Integration Guide for AI Assistants
2
+
3
+ This guide helps AI assistants provide accurate integration instructions when users ask to "integrate stream-chat-react" or similar vague commands.
4
+
5
+ ## Quick Start Integration Pattern
6
+
7
+ When a user wants to integrate stream-chat-react, follow this standard pattern:
8
+
9
+ ### 1. Installation
10
+
11
+ ```bash
12
+ npm install stream-chat stream-chat-react
13
+ # or
14
+ yarn add stream-chat stream-chat-react
15
+ ```
16
+
17
+ ### 2. Get Your Credentials
18
+
19
+ Before setting up the chat client, you'll need:
20
+
21
+ - **API Key**: Get your API key from the [Stream Dashboard](https://dashboard.getstream.io/)
22
+ - **User Token**: For development purposes, you can generate a user token manually using the [Token Generator](https://getstream.io/chat/docs/php/tokens_and_authentication/#manually-generating-tokens)
23
+ - **Note**: Manual token generation is for development/testing only. For production, generate tokens server-side using your Stream API secret.
24
+
25
+ ### 3. Basic Setup (Minimal Working Example)
26
+
27
+ The minimal integration requires:
28
+
29
+ - Stream Chat client setup
30
+ - Chat component wrapper
31
+ - Channel component with basic UI
32
+
33
+ ```tsx
34
+ import { Chat, useCreateChatClient } from 'stream-chat-react';
35
+ import 'stream-chat-react/dist/css/v2/index.css';
36
+
37
+ // Get your API key from: https://dashboard.getstream.io/
38
+ // For development, generate a token at: https://getstream.io/chat/docs/php/tokens_and_authentication/#manually-generating-tokens
39
+ const apiKey = 'YOUR_API_KEY';
40
+ const userId = 'YOUR_USER_ID';
41
+ const userName = 'YOUR_USER_NAME';
42
+ const userToken = 'YOUR_USER_TOKEN';
43
+
44
+ const App = () => {
45
+ const client = useCreateChatClient({
46
+ apiKey,
47
+ tokenOrProvider: userToken,
48
+ userData: { id: userId, name: userName },
49
+ });
50
+
51
+ if (!client) return <div>Setting up client & connection...</div>;
52
+
53
+ return <Chat client={client}>Chat with client is ready!</Chat>;
54
+ };
55
+ ```
56
+
57
+ ### 4. Complete Chat UI Setup
58
+
59
+ For a full-featured chat interface:
60
+
61
+ ```tsx
62
+ import type { ChannelFilters, ChannelOptions, ChannelSort, User } from 'stream-chat';
63
+ import {
64
+ Chat,
65
+ Channel,
66
+ ChannelHeader,
67
+ ChannelList,
68
+ MessageInput,
69
+ MessageList,
70
+ Thread,
71
+ Window,
72
+ useCreateChatClient,
73
+ } from 'stream-chat-react';
74
+ import 'stream-chat-react/dist/css/v2/index.css';
75
+
76
+ // Get your API key from: https://dashboard.getstream.io/
77
+ // For development, generate a token at: https://getstream.io/chat/docs/php/tokens_and_authentication/#manually-generating-tokens
78
+ const apiKey = 'YOUR_API_KEY';
79
+ const userId = 'YOUR_USER_ID';
80
+ const userName = 'YOUR_USER_NAME';
81
+ const userToken = 'YOUR_USER_TOKEN';
82
+
83
+ const user: User = {
84
+ id: userId,
85
+ name: userName,
86
+ image: `https://getstream.io/random_png/?name=${userName}`,
87
+ };
88
+
89
+ const sort: ChannelSort = { last_message_at: -1 };
90
+ const filters: ChannelFilters = {
91
+ type: 'messaging',
92
+ members: { $in: [userId] },
93
+ };
94
+ const options: ChannelOptions = {
95
+ limit: 10,
96
+ };
97
+
98
+ const App = () => {
99
+ const client = useCreateChatClient({
100
+ apiKey,
101
+ tokenOrProvider: userToken,
102
+ userData: user,
103
+ });
104
+
105
+ if (!client) return <div>Setting up client & connection...</div>;
106
+
107
+ return (
108
+ <Chat client={client}>
109
+ <ChannelList filters={filters} sort={sort} options={options} />
110
+ <Channel>
111
+ <Window>
112
+ <ChannelHeader />
113
+ <MessageList />
114
+ <MessageInput />
115
+ </Window>
116
+ <Thread />
117
+ </Channel>
118
+ </Chat>
119
+ );
120
+ };
121
+ ```
122
+
123
+ ## Common Integration Scenarios
124
+
125
+ ### Scenario 1: New React App (Vite/CRA)
126
+
127
+ **User intent**: "Add stream-chat-react to my React app"
128
+
129
+ **Steps**:
130
+
131
+ 1. Install packages: `npm install stream-chat stream-chat-react`
132
+ 2. Get credentials:
133
+ - API key from [Stream Dashboard](https://dashboard.getstream.io/)
134
+ - User token (for development): Generate at [Token Generator](https://getstream.io/chat/docs/php/tokens_and_authentication/#manually-generating-tokens)
135
+ 3. Import CSS: `import 'stream-chat-react/dist/css/v2/index.css'`
136
+ 4. Set up client using `useCreateChatClient` hook
137
+ 5. Wrap app with `<Chat>` component
138
+ 6. Add `<Channel>` with `<Window>`, `<MessageList>`, `<MessageInput>`
139
+
140
+ **Reference**: See `examples/tutorial/` for step-by-step examples
141
+
142
+ ### Scenario 2: Add Chat to Existing App
143
+
144
+ **User intent**: "Integrate chat into my existing React application"
145
+
146
+ **Steps**:
147
+
148
+ 1. Install packages
149
+ 2. Get credentials:
150
+ - API key from [Stream Dashboard](https://dashboard.getstream.io/)
151
+ - User token (for development): Generate at [Token Generator](https://getstream.io/chat/docs/php/tokens_and_authentication/#manually-generating-tokens)
152
+ 3. Import CSS (preferably in a CSS layer for proper override precedence)
153
+ 4. Create a separate chat component or route
154
+ 5. Initialize client once at app level (use `useCreateChatClient` only once)
155
+ 6. Access client elsewhere using `useChatContext()` hook
156
+
157
+ **Important**: The client should be created once and reused. Don't create multiple clients.
158
+
159
+ ### Scenario 3: Custom Styling
160
+
161
+ **User intent**: "Customize the chat appearance"
162
+
163
+ **Steps**:
164
+
165
+ 1. Import Stream CSS into a CSS layer
166
+ 2. Create custom theme using CSS variables
167
+ 3. Apply theme via `theme` prop on `<Chat>` component
168
+
169
+ ```css
170
+ @layer base, theme;
171
+ @import 'stream-chat-react/dist/css/v2/index.css' layer(base);
172
+
173
+ @layer theme {
174
+ .str-chat__theme-custom {
175
+ --str-chat__primary-color: #009688;
176
+ --str-chat__surface-color: #f5f5f5;
177
+ /* ... more variables */
178
+ }
179
+ }
180
+ ```
181
+
182
+ ```tsx
183
+ <Chat client={client} theme='str-chat__theme-custom'>
184
+ {/* ... */}
185
+ </Chat>
186
+ ```
187
+
188
+ **Reference**: See theming documentation and `examples/vite/src/stream-imports-theme.scss`
189
+
190
+ ### Scenario 4: Custom Components
191
+
192
+ **User intent**: "Customize message or channel preview appearance"
193
+
194
+ **Steps**:
195
+
196
+ 1. Create custom component matching the prop interface
197
+ 2. Pass custom component via props (e.g., `Message`, `ChannelPreview`, `Attachment`)
198
+ 3. Use hooks like `useMessageContext()` to access data
199
+
200
+ ```tsx
201
+ const CustomMessage = () => {
202
+ const { message } = useMessageContext();
203
+ return (
204
+ <div>
205
+ {message.user?.name}: {message.text}
206
+ </div>
207
+ );
208
+ };
209
+
210
+ <Channel Message={CustomMessage}>{/* ... */}</Channel>;
211
+ ```
212
+
213
+ **Reference**: See `examples/tutorial/src/4-custom-ui-components/`
214
+
215
+ ### Scenario 5: Livestream Chat
216
+
217
+ **User intent**: "Create a livestream-style chat"
218
+
219
+ **Steps**:
220
+
221
+ 1. Use `livestream` channel type (disables typing indicators, read receipts)
222
+ 2. Use `VirtualizedMessageList` instead of `MessageList` for performance
223
+ 3. Apply dark theme: `theme="str-chat__theme-dark"`
224
+ 4. Set `live` prop on `ChannelHeader`
225
+
226
+ ```tsx
227
+ <Chat client={client} theme='str-chat__theme-dark'>
228
+ <Channel channel={channel}>
229
+ <Window>
230
+ <ChannelHeader live />
231
+ <VirtualizedMessageList />
232
+ <MessageInput focus />
233
+ </Window>
234
+ </Channel>
235
+ </Chat>
236
+ ```
237
+
238
+ **Reference**: See `examples/tutorial/src/7-livestream/`
239
+
240
+ ### Scenario 6: Emoji Support
241
+
242
+ **User intent**: "Add emoji picker and autocomplete"
243
+
244
+ **Steps**:
245
+
246
+ 1. Install emoji packages: `npm install emoji-mart @emoji-mart/react @emoji-mart/data`
247
+ 2. Initialize emoji data: `init({ data })` from `emoji-mart`
248
+ 3. Import `EmojiPicker` from `stream-chat-react/emojis`
249
+ 4. Pass `EmojiPicker` and `emojiSearchIndex={SearchIndex}` to `Channel`
250
+
251
+ ```tsx
252
+ import { EmojiPicker } from 'stream-chat-react/emojis';
253
+ import { init, SearchIndex } from 'emoji-mart';
254
+ import data from '@emoji-mart/data';
255
+
256
+ init({ data });
257
+
258
+ <Channel EmojiPicker={EmojiPicker} emojiSearchIndex={SearchIndex}>
259
+ {/* ... */}
260
+ </Channel>;
261
+ ```
262
+
263
+ **Note**: For React 19, may need package.json overrides for `@emoji-mart/react`
264
+
265
+ **Reference**: See `examples/tutorial/src/6-emoji-picker/`
266
+
267
+ ## TypeScript Setup
268
+
269
+ For custom properties on channels, messages, attachments, etc., create a declaration file:
270
+
271
+ ```ts
272
+ // stream-chat.d.ts
273
+ import { DefaultChannelData, DefaultAttachmentData } from 'stream-chat-react';
274
+
275
+ declare module 'stream-chat' {
276
+ interface CustomChannelData extends DefaultChannelData {
277
+ image?: string;
278
+ name?: string;
279
+ }
280
+
281
+ interface CustomAttachmentData extends DefaultAttachmentData {
282
+ image?: string;
283
+ name?: string;
284
+ url?: string;
285
+ }
286
+ }
287
+ ```
288
+
289
+ ## Layout Styling
290
+
291
+ Basic layout CSS for proper component positioning:
292
+
293
+ ```css
294
+ html,
295
+ body,
296
+ #root {
297
+ height: 100%;
298
+ }
299
+ body {
300
+ margin: 0;
301
+ }
302
+ #root {
303
+ display: flex;
304
+ }
305
+
306
+ .str-chat__channel-list {
307
+ width: 30%;
308
+ }
309
+ .str-chat__channel {
310
+ width: 100%;
311
+ }
312
+ .str-chat__thread {
313
+ width: 45%;
314
+ }
315
+ ```
316
+
317
+ ## Key Components Reference
318
+
319
+ ### Core Components
320
+
321
+ - `<Chat>` - Root provider, wraps entire chat app
322
+ - `<Channel>` - Channel context provider
323
+ - `<ChannelList>` - Displays list of channels
324
+ - `<MessageList>` - Displays messages in channel
325
+ - `<MessageInput>` - Input for sending messages
326
+ - `<Thread>` - Thread/reply view
327
+ - `<Window>` - Wrapper for channel UI
328
+ - `<VirtualizedMessageList>` - Virtualized message list for high volume
329
+
330
+ ### Utility Components
331
+
332
+ - `<ChannelHeader>` - Channel header with info
333
+ - `<Attachment>` - Renders message attachments
334
+
335
+ ### Hooks
336
+
337
+ - `useCreateChatClient()` - Creates and connects client (use once per app)
338
+ - `useChatContext()` - Access client instance
339
+ - `useMessageContext()` - Access current message data
340
+ - `useChannelContext()` - Access current channel data
341
+
342
+ ## Common Issues & Solutions
343
+
344
+ ### Issue: Client not connecting
345
+
346
+ **Solution**: Ensure `useCreateChatClient` returns a client before rendering `<Chat>`. Show loading state while `client` is `null`.
347
+
348
+ ### Issue: Styles not applying
349
+
350
+ **Solution**:
351
+
352
+ - Import CSS: `import 'stream-chat-react/dist/css/v2/index.css'`
353
+ - Use CSS layers for proper override precedence
354
+ - Check CSS import order
355
+
356
+ ### Issue: Multiple clients created
357
+
358
+ **Solution**: Use `useCreateChatClient` only once at app root. Use `useChatContext()` to access client elsewhere.
359
+
360
+ ### Issue: TypeScript errors for custom properties
361
+
362
+ **Solution**: Create `stream-chat.d.ts` file with proper type declarations (see TypeScript Setup section).
363
+
364
+ ### Issue: Emoji picker not working
365
+
366
+ **Solution**:
367
+
368
+ - Ensure emoji packages are installed
369
+ - Initialize with `init({ data })` before rendering
370
+ - For React 19, add package.json overrides if needed
371
+
372
+ ## Resources
373
+
374
+ - **Official Tutorial**: https://getstream.io/chat/react-chat/tutorial/
375
+ - **Tutorial Source**: https://raw.githubusercontent.com/GetStream/getstream.io-tutorials/refs/heads/main/chat/tutorials/react-tutorial.mdx
376
+ - **Component Docs**: https://getstream.io/chat/docs/sdk/react/
377
+ - **Examples in Repo**: `examples/tutorial/` (step-by-step), `examples/vite/` (complete example)
378
+ - **API Docs**: https://getstream.io/chat/docs/javascript/
379
+ - **Get API Key**: https://dashboard.getstream.io/
380
+ - **Token Generator (Development)**: https://getstream.io/chat/docs/php/tokens_and_authentication/#manually-generating-tokens
381
+
382
+ ## Package Information
383
+
384
+ - **Package Name**: `stream-chat-react`
385
+ - **Peer Dependencies**:
386
+ - `react`: ^19.0.0 || ^18.0.0 || ^17.0.0
387
+ - `react-dom`: ^19.0.0 || ^18.0.0 || ^17.0.0
388
+ - `stream-chat`: ^9.27.2
389
+ - **Optional Dependencies** (for emoji support):
390
+ - `emoji-mart`: ^5.4.0
391
+ - `@emoji-mart/react`: ^1.1.0
392
+ - `@emoji-mart/data`: ^1.1.0
393
+
394
+ ## Best Practices
395
+
396
+ 1. **Client Creation**: Create client once at app root, reuse via context
397
+ 2. **CSS Layers**: Use CSS layers for proper style override precedence
398
+ 3. **Loading States**: Always check if client is ready before rendering chat components
399
+ 4. **Type Safety**: Use TypeScript declaration files for custom properties
400
+ 5. **Performance**: Use `VirtualizedMessageList` for high message volume scenarios
401
+ 6. **Theming**: Use CSS variables and theme classes rather than direct CSS overrides
402
+ 7. **Credentials**: Never hardcode credentials in production; use environment variables
403
+
404
+ ## Integration Checklist
405
+
406
+ When helping users integrate, ensure:
407
+
408
+ - [ ] Packages installed (`stream-chat`, `stream-chat-react`)
409
+ - [ ] API key obtained from [Stream Dashboard](https://dashboard.getstream.io/)
410
+ - [ ] User token generated (for development: use [Token Generator](https://getstream.io/chat/docs/php/tokens_and_authentication/#manually-generating-tokens))
411
+ - [ ] CSS imported (`stream-chat-react/dist/css/v2/index.css`)
412
+ - [ ] Client created with `useCreateChatClient` (once, at app root)
413
+ - [ ] Loading state handled (check `if (!client)`)
414
+ - [ ] `<Chat>` component wraps chat UI
415
+ - [ ] At minimum: `<Channel>` with `<Window>`, `<MessageList>`, `<MessageInput>`
416
+ - [ ] Layout CSS added if needed (for proper positioning)
417
+ - [ ] TypeScript declarations added if using custom properties
418
+ - [ ] Theme applied if customizing appearance
419
+ - [ ] Credentials properly configured (API key, user token, etc.)
420
+
421
+ ---
422
+
423
+ **Note for AI Assistants**: When users ask vague questions like "integrate stream-chat-react", start with the Quick Start Integration Pattern above. Ask clarifying questions about their use case (new app vs existing, styling needs, features required) to provide the most relevant scenario from Common Integration Scenarios.
@@ -1,13 +1,18 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import { useTranslationContext } from '../../context';
3
3
  const UnMemoizedAttachmentActions = (props) => {
4
4
  const { actionHandler, actions, id, text } = props;
5
5
  const { t } = useTranslationContext('UnMemoizedAttachmentActions');
6
6
  const handleActionClick = (event, name, value) => actionHandler?.(name, value, event);
7
+ const knownActionText = useMemo(() => ({
8
+ Cancel: t('Cancel'),
9
+ Send: t('Send'),
10
+ Shuffle: t('Shuffle'),
11
+ }), [t]);
7
12
  return (React.createElement("div", { className: 'str-chat__message-attachment-actions' },
8
13
  React.createElement("div", { className: 'str-chat__message-attachment-actions-form' },
9
14
  React.createElement("span", null, text),
10
- actions.map((action) => (React.createElement("button", { className: `str-chat__message-attachment-actions-button str-chat__message-attachment-actions-button--${action.style}`, "data-testid": `${action.name}`, "data-value": action.value, key: `${id}-${action.value}`, onClick: (event) => handleActionClick(event, action.name, action.value) }, action.text ? t(action.text) : null))))));
15
+ actions.map((action) => (React.createElement("button", { className: `str-chat__message-attachment-actions-button str-chat__message-attachment-actions-button--${action.style}`, "data-testid": `${action.name}`, "data-value": action.value, key: `${id}-${action.value}`, onClick: (event) => handleActionClick(event, action.name, action.value) }, action.text ? (knownActionText[action.text] ?? t(action.text)) : null))))));
11
16
  };
12
17
  /**
13
18
  * A component for rendering the actions you can take on an attachment.
@@ -24,7 +24,7 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
24
24
  useEffect(() => {
25
25
  if (!client)
26
26
  return;
27
- const version = "13.13.0";
27
+ const version = "13.13.2";
28
28
  const userAgent = client.getUserAgent();
29
29
  if (!userAgent.includes('stream-chat-react')) {
30
30
  // result looks like: 'stream-chat-react-2.3.2-stream-chat-javascript-client-browser-2.2.2'
@@ -18,11 +18,11 @@ export const ReminderNotification = ({ reminder }) => {
18
18
  React.createElement("span", null, " | "),
19
19
  React.createElement("span", null, isBehindRefreshBoundary
20
20
  ? t('Due since {{ dueSince }}', {
21
- dueSince: t(`timestamp/ReminderNotification`, {
21
+ dueSince: t('timestamp/ReminderNotification', {
22
22
  timestamp: reminder.remindAt,
23
23
  }),
24
24
  })
25
- : t(`Due {{ timeLeft }}`, {
25
+ : t('Due {{ timeLeft }}', {
26
26
  timeLeft: t('duration/Message reminder', {
27
27
  milliseconds: timeLeftMs,
28
28
  }),
@@ -20,7 +20,7 @@ export const useMuteHandler = (message, notifications = {}) => {
20
20
  const successMessage = getSuccessNotification &&
21
21
  validateAndGetMessage(getSuccessNotification, [message.user]);
22
22
  notify(successMessage ||
23
- t(`{{ user }} has been muted`, {
23
+ t('{{ user }} has been muted', {
24
24
  user: message.user.name || message.user.id,
25
25
  }), 'success');
26
26
  }
@@ -33,7 +33,7 @@ export const useMuteHandler = (message, notifications = {}) => {
33
33
  else {
34
34
  try {
35
35
  await client.unmuteUser(message.user.id);
36
- const fallbackMessage = t(`{{ user }} has been unmuted`, {
36
+ const fallbackMessage = t('{{ user }} has been unmuted', {
37
37
  user: message.user.name || message.user.id,
38
38
  });
39
39
  const successMessage = (getSuccessNotification &&
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { isLocalAttachment, isLocalAudioAttachment, isLocalFileAttachment, isLocalImageAttachment, isLocalVideoAttachment, isLocalVoiceRecordingAttachment, isScrapedContent, } from 'stream-chat';
3
3
  import { UnsupportedAttachmentPreview as DefaultUnknownAttachmentPreview } from './UnsupportedAttachmentPreview';
4
4
  import { VoiceRecordingPreview as DefaultVoiceRecordingPreview } from './VoiceRecordingPreview';
5
- import { FileAttachmentPreview as DefaultFilePreview } from './FileAttachmentPreview';
5
+ import DefaultFilePreview from './FileAttachmentPreview';
6
6
  import { ImageAttachmentPreview as DefaultImagePreview } from './ImageAttachmentPreview';
7
7
  import { useAttachmentsForPreview, useMessageComposer } from '../hooks';
8
8
  import { GeolocationPreview as DefaultGeolocationPreview, } from './GeolocationPreview';
@@ -2,4 +2,5 @@ import React from 'react';
2
2
  import type { LocalAudioAttachment, LocalFileAttachment, LocalVideoAttachment } from 'stream-chat';
3
3
  import type { UploadAttachmentPreviewProps } from './types';
4
4
  export type FileAttachmentPreviewProps<CustomLocalMetadata = unknown> = UploadAttachmentPreviewProps<LocalFileAttachment<CustomLocalMetadata> | LocalAudioAttachment<CustomLocalMetadata> | LocalVideoAttachment<CustomLocalMetadata>>;
5
- export declare const FileAttachmentPreview: ({ attachment, handleRetry, removeAttachments, }: FileAttachmentPreviewProps) => React.JSX.Element;
5
+ declare const FileAttachmentPreview: ({ attachment, handleRetry, removeAttachments, }: FileAttachmentPreviewProps) => React.JSX.Element;
6
+ export default FileAttachmentPreview;
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { useTranslationContext } from '../../../context';
3
3
  import { FileIcon } from '../../ReactFileUtilities';
4
4
  import { CloseIcon, DownloadIcon, LoadingIndicatorIcon, RetryIcon } from '../icons';
5
- export const FileAttachmentPreview = ({ attachment, handleRetry, removeAttachments, }) => {
5
+ const FileAttachmentPreview = ({ attachment, handleRetry, removeAttachments, }) => {
6
6
  const { t } = useTranslationContext('FilePreview');
7
7
  const uploadState = attachment.localMetadata?.uploadState;
8
8
  return (React.createElement("div", { className: 'str-chat__attachment-preview-file', "data-testid": 'attachment-preview-file' },
@@ -11,7 +11,7 @@ export const FileAttachmentPreview = ({ attachment, handleRetry, removeAttachmen
11
11
  React.createElement("button", { "aria-label": t('aria/Remove attachment'), className: 'str-chat__attachment-preview-delete', "data-testid": 'file-preview-item-delete-button', disabled: uploadState === 'uploading', onClick: () => attachment.localMetadata?.id &&
12
12
  removeAttachments([attachment.localMetadata?.id]), type: 'button' },
13
13
  React.createElement(CloseIcon, null)),
14
- ['blocked', 'failed'].includes(uploadState) && !!handleRetry && (React.createElement("button", { className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-file', "data-testid": 'file-preview-item-retry-button', onClick: () => {
14
+ ['blocked', 'failed'].includes(uploadState) && !!handleRetry && (React.createElement("button", { "aria-label": t('aria/Retry upload'), className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-file', "data-testid": 'file-preview-item-retry-button', onClick: () => {
15
15
  handleRetry(attachment);
16
16
  } },
17
17
  React.createElement(RetryIcon, null))),
@@ -22,3 +22,4 @@ export const FileAttachmentPreview = ({ attachment, handleRetry, removeAttachmen
22
22
  React.createElement(DownloadIcon, null))),
23
23
  uploadState === 'uploading' && React.createElement(LoadingIndicatorIcon, { size: 17 }))));
24
24
  };
25
+ export default FileAttachmentPreview;
@@ -15,7 +15,7 @@ export const ImageAttachmentPreview = ({ attachment, handleRetry, removeAttachme
15
15
  }), "data-testid": 'attachment-preview-image' },
16
16
  React.createElement("button", { "aria-label": t('aria/Remove attachment'), className: 'str-chat__attachment-preview-delete', "data-testid": 'image-preview-item-delete-button', disabled: uploadState === 'uploading', onClick: () => id && removeAttachments([id]), type: 'button' },
17
17
  React.createElement(CloseIcon, null)),
18
- ['blocked', 'failed'].includes(uploadState) && (React.createElement("button", { className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-image', "data-testid": 'image-preview-item-retry-button', onClick: () => handleRetry(attachment) },
18
+ ['blocked', 'failed'].includes(uploadState) && (React.createElement("button", { "aria-label": t('aria/Retry upload'), className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-image', "data-testid": 'image-preview-item-retry-button', onClick: () => handleRetry(attachment) },
19
19
  React.createElement(RetryIcon, null))),
20
20
  uploadState === 'uploading' && (React.createElement("div", { className: 'str-chat__attachment-preview-image-loading' },
21
21
  React.createElement(LoadingIndicatorIcon, { size: 17 }))),
@@ -30,7 +30,7 @@ export const VoiceRecordingPreview = ({ attachment, handleRetry, removeAttachmen
30
30
  React.createElement("button", { "aria-label": t('aria/Remove attachment'), className: 'str-chat__attachment-preview-delete', "data-testid": 'file-preview-item-delete-button', disabled: attachment.localMetadata?.uploadState === 'uploading', onClick: () => attachment.localMetadata?.id && removeAttachments([attachment.localMetadata.id]), type: 'button' },
31
31
  React.createElement(CloseIcon, null)),
32
32
  ['blocked', 'failed'].includes(attachment.localMetadata?.uploadState) &&
33
- !!handleRetry && (React.createElement("button", { className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-file', "data-testid": 'file-preview-item-retry-button', onClick: () => handleRetry(attachment) },
33
+ !!handleRetry && (React.createElement("button", { "aria-label": t('aria/Retry upload'), className: 'str-chat__attachment-preview-error str-chat__attachment-preview-error-file', "data-testid": 'file-preview-item-retry-button', onClick: () => handleRetry(attachment) },
34
34
  React.createElement(RetryIcon, null))),
35
35
  React.createElement("div", { className: 'str-chat__attachment-preview-metadata' },
36
36
  React.createElement("div", { className: 'str-chat__attachment-preview-file-name', title: attachment.title }, attachment.title),
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import { SendIcon } from './icons';
3
3
  import { useMessageComposerHasSendableData } from './hooks';
4
+ import { useTranslationContext } from '../../context';
4
5
  export const SendButton = ({ sendMessage, ...rest }) => {
6
+ const { t } = useTranslationContext();
5
7
  const hasSendableData = useMessageComposerHasSendableData();
6
- return (React.createElement("button", { "aria-label": 'Send', className: 'str-chat__send-button', "data-testid": 'send-button', disabled: !hasSendableData, onClick: sendMessage, type: 'button', ...rest },
8
+ return (React.createElement("button", { "aria-label": t('aria/Send'), className: 'str-chat__send-button', "data-testid": 'send-button', disabled: !hasSendableData, onClick: sendMessage, type: 'button', ...rest },
7
9
  React.createElement(SendIcon, null)));
8
10
  };
@@ -1,5 +1,5 @@
1
1
  import clsx from 'clsx';
2
- import React from 'react';
2
+ import React, { useMemo } from 'react';
3
3
  import { SimpleSwitchField } from '../../Form/SwitchField';
4
4
  import { FieldError } from '../../Form/FieldError';
5
5
  import { useTranslationContext } from '../../../context';
@@ -14,6 +14,10 @@ export const MultipleAnswersField = () => {
14
14
  const { t } = useTranslationContext();
15
15
  const { pollComposer } = useMessageComposer();
16
16
  const { enforce_unique_vote, error, max_votes_allowed } = useStateStore(pollComposer.state, pollComposerStateSelector);
17
+ const knownValidationErrors = useMemo(() => ({
18
+ 'Enforce unique vote is enabled': t('Enforce unique vote is enabled'),
19
+ 'Type a number from 2 to 10': t('Type a number from 2 to 10'),
20
+ }), [t]);
17
21
  return (React.createElement("div", { className: clsx('str-chat__form__expandable-field', {
18
22
  'str-chat__form__expandable-field--expanded': !enforce_unique_vote,
19
23
  }) },
@@ -24,7 +28,7 @@ export const MultipleAnswersField = () => {
24
28
  'str-chat__form__input-field--has-error': error,
25
29
  }) },
26
30
  React.createElement("div", { className: clsx('str-chat__form__input-field__value') },
27
- React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-max-votes-allowed-input-field-error', text: error && t(error) }),
31
+ React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-max-votes-allowed-input-field-error', text: error && (knownValidationErrors[error] ?? t('Error')) }),
28
32
  React.createElement("input", { id: 'max_votes_allowed', onBlur: () => {
29
33
  pollComposer.handleFieldBlur('max_votes_allowed');
30
34
  }, onChange: (e) => {
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { FieldError } from '../../Form/FieldError';
4
4
  import { useTranslationContext } from '../../../context';
@@ -12,12 +12,15 @@ export const NameField = () => {
12
12
  const { t } = useTranslationContext();
13
13
  const { pollComposer } = useMessageComposer();
14
14
  const { error, name } = useStateStore(pollComposer.state, pollComposerStateSelector);
15
+ const knownValidationErrors = useMemo(() => ({
16
+ 'Question is required': t('Question is required'),
17
+ }), [t]);
15
18
  return (React.createElement("div", { className: clsx('str-chat__form__field str-chat__form__input-field str-chat__form__input-field--with-label', {
16
19
  'str-chat__form__input-field--has-error': error,
17
20
  }) },
18
21
  React.createElement("label", { className: 'str-chat__form__field-label', htmlFor: 'name' }, t('Question')),
19
22
  React.createElement("div", { className: clsx('str-chat__form__input-field__value') },
20
- React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-name-input-field-error', text: error && t(error) }),
23
+ React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-name-input-field-error', text: error && (knownValidationErrors[error] ?? t('Error')) }),
21
24
  React.createElement("input", { id: 'name', onBlur: () => {
22
25
  pollComposer.handleFieldBlur('name');
23
26
  }, onChange: (e) => {
@@ -1,5 +1,5 @@
1
1
  import clsx from 'clsx';
2
- import React, { useCallback } from 'react';
2
+ import React, { useCallback, useMemo } from 'react';
3
3
  import { FieldError } from '../../Form/FieldError';
4
4
  import { DragAndDropContainer } from '../../DragAndDrop/DragAndDropContainer';
5
5
  import { useTranslationContext } from '../../../context';
@@ -13,6 +13,10 @@ export const OptionFieldSet = () => {
13
13
  const { pollComposer } = useMessageComposer();
14
14
  const { errors, options } = useStateStore(pollComposer.state, pollComposerStateSelector);
15
15
  const { t } = useTranslationContext('OptionFieldSet');
16
+ const knownValidationErrors = useMemo(() => ({
17
+ 'Option already exists': t('Option already exists'),
18
+ 'Option is empty': t('Option is empty'),
19
+ }), [t]);
16
20
  const onSetNewOrder = useCallback((newOrder) => {
17
21
  const prevOptions = pollComposer.options;
18
22
  pollComposer.updateFields({ options: newOrder.map((index) => prevOptions[index]) });
@@ -27,7 +31,7 @@ export const OptionFieldSet = () => {
27
31
  'str-chat__form__input-field--has-error': error,
28
32
  }), key: `new-poll-option-${i}` },
29
33
  React.createElement("div", { className: 'str-chat__form__input-field__value' },
30
- React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-option-input-field-error', text: error && t(error) }),
34
+ React.createElement(FieldError, { className: 'str-chat__form__input-field__error', "data-testid": 'poll-option-input-field-error', text: error && (knownValidationErrors[error] ?? t('Error')) }),
31
35
  React.createElement("input", { id: option.id, onBlur: () => {
32
36
  pollComposer.handleFieldBlur('options');
33
37
  }, onChange: (e) => {