stream-chat-react-native-core 5.6.0-beta.1 → 5.6.0-beta.3

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 (119) hide show
  1. package/lib/commonjs/components/Channel/Channel.js +23 -17
  2. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  3. package/lib/commonjs/components/Chat/Chat.js +1 -1
  4. package/lib/commonjs/components/Chat/Chat.js.map +1 -1
  5. package/lib/commonjs/components/Chat/hooks/useAppSettings.js +51 -22
  6. package/lib/commonjs/components/Chat/hooks/useAppSettings.js.map +1 -1
  7. package/lib/commonjs/components/Chat/hooks/useIsOnline.js +1 -1
  8. package/lib/commonjs/components/Chat/hooks/useIsOnline.js.map +1 -1
  9. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js +4 -1
  10. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js.map +1 -1
  11. package/lib/commonjs/components/ImageGallery/components/ImageGridHandle.js +2 -2
  12. package/lib/commonjs/components/ImageGallery/components/ImageGridHandle.js.map +1 -1
  13. package/lib/commonjs/contexts/chatContext/ChatContext.js.map +1 -1
  14. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +16 -7
  15. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  16. package/lib/commonjs/contexts/themeContext/utils/theme.js +1 -0
  17. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  18. package/lib/commonjs/native.js +1 -1
  19. package/lib/commonjs/native.js.map +1 -1
  20. package/lib/commonjs/store/QuickSqliteClient.js +46 -1
  21. package/lib/commonjs/store/QuickSqliteClient.js.map +1 -1
  22. package/lib/commonjs/store/QuickSqliteClient_v4.js +112 -0
  23. package/lib/commonjs/store/QuickSqliteClient_v4.js.map +1 -0
  24. package/lib/commonjs/store/apis/getAppSettings.js +23 -0
  25. package/lib/commonjs/store/apis/getAppSettings.js.map +1 -0
  26. package/lib/commonjs/store/apis/getPendingTasks.js.map +1 -1
  27. package/lib/commonjs/store/apis/index.js +26 -0
  28. package/lib/commonjs/store/apis/index.js.map +1 -1
  29. package/lib/commonjs/store/apis/queries/selectChannels.js.map +1 -1
  30. package/lib/commonjs/store/apis/queries/selectMembersForChannels.js.map +1 -1
  31. package/lib/commonjs/store/apis/queries/selectMessagesForChannels.js.map +1 -1
  32. package/lib/commonjs/store/apis/queries/selectReactionsForMessages.js.map +1 -1
  33. package/lib/commonjs/store/apis/queries/selectReadsForChannels.js.map +1 -1
  34. package/lib/commonjs/store/apis/upsertAppSettings.js +26 -0
  35. package/lib/commonjs/store/apis/upsertAppSettings.js.map +1 -0
  36. package/lib/commonjs/store/schema.js +1 -0
  37. package/lib/commonjs/store/schema.js.map +1 -1
  38. package/lib/commonjs/version.json +1 -1
  39. package/lib/module/components/Channel/Channel.js +23 -17
  40. package/lib/module/components/Channel/Channel.js.map +1 -1
  41. package/lib/module/components/Chat/Chat.js +1 -1
  42. package/lib/module/components/Chat/Chat.js.map +1 -1
  43. package/lib/module/components/Chat/hooks/useAppSettings.js +51 -22
  44. package/lib/module/components/Chat/hooks/useAppSettings.js.map +1 -1
  45. package/lib/module/components/Chat/hooks/useIsOnline.js +1 -1
  46. package/lib/module/components/Chat/hooks/useIsOnline.js.map +1 -1
  47. package/lib/module/components/ImageGallery/components/ImageGrid.js +4 -1
  48. package/lib/module/components/ImageGallery/components/ImageGrid.js.map +1 -1
  49. package/lib/module/components/ImageGallery/components/ImageGridHandle.js +2 -2
  50. package/lib/module/components/ImageGallery/components/ImageGridHandle.js.map +1 -1
  51. package/lib/module/contexts/chatContext/ChatContext.js.map +1 -1
  52. package/lib/module/contexts/messageInputContext/MessageInputContext.js +16 -7
  53. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  54. package/lib/module/contexts/themeContext/utils/theme.js +1 -0
  55. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  56. package/lib/module/native.js +1 -1
  57. package/lib/module/native.js.map +1 -1
  58. package/lib/module/store/QuickSqliteClient.js +46 -1
  59. package/lib/module/store/QuickSqliteClient.js.map +1 -1
  60. package/lib/module/store/QuickSqliteClient_v4.js +112 -0
  61. package/lib/module/store/QuickSqliteClient_v4.js.map +1 -0
  62. package/lib/module/store/apis/getAppSettings.js +23 -0
  63. package/lib/module/store/apis/getAppSettings.js.map +1 -0
  64. package/lib/module/store/apis/getPendingTasks.js.map +1 -1
  65. package/lib/module/store/apis/index.js +26 -0
  66. package/lib/module/store/apis/index.js.map +1 -1
  67. package/lib/module/store/apis/queries/selectChannels.js.map +1 -1
  68. package/lib/module/store/apis/queries/selectMembersForChannels.js.map +1 -1
  69. package/lib/module/store/apis/queries/selectMessagesForChannels.js.map +1 -1
  70. package/lib/module/store/apis/queries/selectReactionsForMessages.js.map +1 -1
  71. package/lib/module/store/apis/queries/selectReadsForChannels.js.map +1 -1
  72. package/lib/module/store/apis/upsertAppSettings.js +26 -0
  73. package/lib/module/store/apis/upsertAppSettings.js.map +1 -0
  74. package/lib/module/store/schema.js +1 -0
  75. package/lib/module/store/schema.js.map +1 -1
  76. package/lib/module/version.json +1 -1
  77. package/lib/typescript/components/Chat/hooks/useAppSettings.d.ts +2 -2
  78. package/lib/typescript/components/Chat/hooks/useIsOnline.d.ts +1 -1
  79. package/lib/typescript/contexts/chatContext/ChatContext.d.ts +1 -1
  80. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +1 -0
  81. package/lib/typescript/store/QuickSqliteClient.d.ts +4 -1
  82. package/lib/typescript/store/QuickSqliteClient_v4.d.ts +12 -0
  83. package/lib/typescript/store/apis/getAppSettings.d.ts +4 -0
  84. package/lib/typescript/store/apis/getPendingTasks.d.ts +1 -1
  85. package/lib/typescript/store/apis/index.d.ts +2 -0
  86. package/lib/typescript/store/apis/upsertAppSettings.d.ts +6 -0
  87. package/lib/typescript/store/schema.d.ts +1 -0
  88. package/package.json +2 -2
  89. package/src/components/Attachment/__tests__/Gallery.test.js +9 -2
  90. package/src/components/Attachment/__tests__/Giphy.test.js +9 -5
  91. package/src/components/Channel/Channel.tsx +11 -3
  92. package/src/components/Chat/Chat.tsx +1 -1
  93. package/src/components/Chat/hooks/__tests__/useAppSettings.test.tsx +1 -0
  94. package/src/components/Chat/hooks/useAppSettings.ts +19 -6
  95. package/src/components/Chat/hooks/useIsOnline.ts +1 -1
  96. package/src/components/ImageGallery/__tests__/ImageGallery.test.tsx +29 -15
  97. package/src/components/ImageGallery/__tests__/ImageGalleryFooter.test.tsx +12 -6
  98. package/src/components/ImageGallery/__tests__/ImageGalleryHeader.test.tsx +9 -4
  99. package/src/components/ImageGallery/components/ImageGrid.tsx +2 -1
  100. package/src/components/ImageGallery/components/ImageGridHandle.tsx +2 -2
  101. package/src/contexts/chatContext/ChatContext.tsx +1 -1
  102. package/src/contexts/messageInputContext/MessageInputContext.tsx +7 -0
  103. package/src/contexts/messageInputContext/__tests__/MessageInputContext.test.tsx +8 -3
  104. package/src/contexts/messageInputContext/__tests__/uploadFile.test.tsx +16 -6
  105. package/src/contexts/themeContext/utils/theme.ts +2 -0
  106. package/src/native.ts +1 -1
  107. package/src/store/QuickSqliteClient.ts +49 -5
  108. package/src/store/QuickSqliteClient_v4.ts +98 -0
  109. package/src/store/apis/getAppSettings.ts +19 -0
  110. package/src/store/apis/getPendingTasks.ts +2 -1
  111. package/src/store/apis/index.ts +2 -0
  112. package/src/store/apis/queries/selectChannels.ts +4 -1
  113. package/src/store/apis/queries/selectMembersForChannels.ts +1 -1
  114. package/src/store/apis/queries/selectMessagesForChannels.ts +1 -1
  115. package/src/store/apis/queries/selectReactionsForMessages.ts +1 -1
  116. package/src/store/apis/queries/selectReadsForChannels.ts +1 -1
  117. package/src/store/apis/upsertAppSettings.ts +23 -0
  118. package/src/store/schema.ts +2 -0
  119. package/src/version.json +1 -1
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "5.6.0-beta.1"
2
+ "version": "5.6.0-beta.3"
3
3
  }
@@ -1,3 +1,3 @@
1
1
  import type { AppSettingsAPIResponse, StreamChat } from 'stream-chat';
2
- import type { DefaultStreamChatGenerics } from 'stream-chat-react-native';
3
- export declare const useAppSettings: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(client: StreamChat<StreamChatGenerics>, isOnline: boolean) => AppSettingsAPIResponse | null;
2
+ import type { DefaultStreamChatGenerics } from '../../../types/types';
3
+ export declare const useAppSettings: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(client: StreamChat<StreamChatGenerics>, isOnline: boolean | null, enableOfflineSupport: boolean) => AppSettingsAPIResponse | null;
@@ -8,5 +8,5 @@ import type { DefaultStreamChatGenerics } from '../../../types/types';
8
8
  */
9
9
  export declare const useIsOnline: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(client: StreamChat<StreamChatGenerics>, closeConnectionOnBackground?: boolean) => {
10
10
  connectionRecovering: boolean;
11
- isOnline: boolean;
11
+ isOnline: boolean | null;
12
12
  };
@@ -27,7 +27,7 @@ export declare type ChatContextValue<StreamChatGenerics extends DefaultStreamCha
27
27
  connectionRecovering: boolean;
28
28
  enableOfflineSupport: boolean;
29
29
  ImageComponent: React.ComponentType<ImageProps>;
30
- isOnline: boolean;
30
+ isOnline: boolean | null;
31
31
  mutedUsers: Mute<StreamChatGenerics>[];
32
32
  /**
33
33
  * @param newChannel Channel to set as active.
@@ -178,6 +178,7 @@ export declare type Theme = {
178
178
  rightContainer: ViewStyle;
179
179
  };
180
180
  grid: {
181
+ container: ViewStyle;
181
182
  contentContainer: ViewStyle;
182
183
  gridAvatar: ImageStyle;
183
184
  gridAvatarWrapper: ViewStyle;
@@ -2,6 +2,8 @@ import type { PreparedQueries } from './types';
2
2
  /**
3
3
  * QuickSqliteClient takes care of any direct interaction with sqlite.
4
4
  * This way usage react-native-quick-sqlite package is scoped to a single class/file.
5
+ *
6
+ * TODO: Drop the support for v4 of react-native-quick-sqlite in the next major release.
5
7
  */
6
8
  export declare class QuickSqliteClient {
7
9
  static dbVersion: number;
@@ -9,10 +11,11 @@ export declare class QuickSqliteClient {
9
11
  static dbLocation: string;
10
12
  static getDbVersion: () => number;
11
13
  static setDbVersion: (version: number) => number;
14
+ static isQuickSqliteV4: boolean;
12
15
  static openDB: () => void;
13
16
  static closeDB: () => void;
14
17
  static executeSqlBatch: (queries: PreparedQueries[]) => void;
15
- static executeSql: (query: string, params?: string[] | undefined) => any[];
18
+ static executeSql: (query: string, params?: string[] | undefined) => any;
16
19
  static dropTables: () => void;
17
20
  static deleteDatabase: () => boolean;
18
21
  static initializeDatabase: () => void;
@@ -0,0 +1,12 @@
1
+ import type { PreparedQueries } from './types';
2
+ export declare class QuickSqliteClient_v4 {
3
+ static dbName: string;
4
+ static dbLocation: string;
5
+ static openDB: () => void;
6
+ static closeDB: () => void;
7
+ static executeSqlBatch: (queries: PreparedQueries[]) => void;
8
+ static executeSql: (query: string, params?: string[] | undefined) => any;
9
+ static deleteDatabase: () => boolean;
10
+ static updateUserPragmaVersion: (version: number) => void;
11
+ static getUserPragmaVersion: () => number;
12
+ }
@@ -0,0 +1,4 @@
1
+ import type { AppSettingsAPIResponse } from 'stream-chat';
2
+ export declare const getAppSettings: ({ currentUserId, }: {
3
+ currentUserId: string;
4
+ }) => AppSettingsAPIResponse;
@@ -1,3 +1,3 @@
1
1
  export declare const getPendingTasks: (conditions?: {
2
2
  messageId?: string;
3
- }) => import("../types").PendingTask[];
3
+ }) => any;
@@ -4,6 +4,7 @@ export * from './deleteMessage';
4
4
  export * from './deleteMessagesForChannel';
5
5
  export * from './deleteReactions';
6
6
  export * from './getAllChannelIds';
7
+ export * from './getAppSettings';
7
8
  export * from './getChannelMessages';
8
9
  export * from './getChannels';
9
10
  export * from './getChannelsForFilterSort';
@@ -12,6 +13,7 @@ export * from './getMembers';
12
13
  export * from './getReads';
13
14
  export * from './updateMessage';
14
15
  export * from './updateReaction';
16
+ export * from './upsertAppSettings';
15
17
  export * from './upsertChannelData';
16
18
  export * from './upsertChannels';
17
19
  export * from './upsertCidsForQuery';
@@ -0,0 +1,6 @@
1
+ import type { AppSettingsAPIResponse } from 'stream-chat';
2
+ export declare const upsertAppSettings: ({ appSettings, currentUserId, flush, }: {
3
+ appSettings: AppSettingsAPIResponse;
4
+ currentUserId: string;
5
+ flush?: boolean | undefined;
6
+ }) => void;
@@ -114,6 +114,7 @@ export declare type Schema = {
114
114
  updatedAt?: string;
115
115
  };
116
116
  userSyncStatus: {
117
+ appSettings: string;
117
118
  lastSyncedAt: string;
118
119
  userId: string;
119
120
  };
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": "5.6.0-beta.1",
4
+ "version": "5.6.0-beta.3",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
@@ -79,7 +79,7 @@
79
79
  "path": "0.12.7",
80
80
  "react-native-markdown-package": "1.8.2",
81
81
  "react-native-url-polyfill": "^1.3.0",
82
- "stream-chat": "7.0.0"
82
+ "stream-chat": "8.1.2"
83
83
  },
84
84
  "peerDependencies": {
85
85
  "react-native-svg": "^12.1.0"
@@ -265,7 +265,10 @@ describe('Gallery', () => {
265
265
  });
266
266
 
267
267
  const component = await getComponent([image1]);
268
- const { getByA11yLabel, getByAccessibilityHint } = render(component);
268
+ const { getByA11yLabel, getByAccessibilityHint, queryAllByTestId } = render(component);
269
+ await waitFor(() => {
270
+ expect(queryAllByTestId('gallery-container').length).toBe(1);
271
+ });
269
272
 
270
273
  fireEvent(getByA11yLabel('Gallery Image'), 'error');
271
274
  expect(getByAccessibilityHint('image-loading-error')).toBeTruthy();
@@ -278,7 +281,11 @@ describe('Gallery', () => {
278
281
  });
279
282
 
280
283
  const component = await getComponent([image1]);
281
- const { getByA11yLabel, getByAccessibilityHint } = render(component);
284
+ const { getByA11yLabel, getByAccessibilityHint, queryAllByTestId } = render(component);
285
+
286
+ await waitFor(() => {
287
+ expect(queryAllByTestId('gallery-container').length).toBe(1);
288
+ });
282
289
 
283
290
  fireEvent(getByA11yLabel('Gallery Image'), 'onLoadStart');
284
291
  expect(getByAccessibilityHint('image-loading')).toBeTruthy();
@@ -274,8 +274,8 @@ describe('Giphy', () => {
274
274
  });
275
275
  });
276
276
 
277
- it('should render a error indicator in giphy image', () => {
278
- const { getByA11yLabel, getByAccessibilityHint } = render(
277
+ it('should render a error indicator in giphy image', async () => {
278
+ const { getByA11yLabel, getByAccessibilityHint, queryByTestId } = render(
279
279
  <OverlayProvider>
280
280
  <Chat client={chatClient}>
281
281
  <Channel channel={channel}>
@@ -284,12 +284,15 @@ describe('Giphy', () => {
284
284
  </Chat>
285
285
  </OverlayProvider>,
286
286
  );
287
+ await waitFor(() => {
288
+ expect(queryByTestId('giphy-attachment')).toBeTruthy();
289
+ });
287
290
 
288
291
  fireEvent(getByA11yLabel('Giphy Attachment Image'), 'error');
289
292
  expect(getByAccessibilityHint('image-loading-error')).toBeTruthy();
290
293
  });
291
294
 
292
- it('should render a loading indicator in giphy image and when successful render the image', () => {
295
+ it('should render a loading indicator in giphy image and when successful render the image', async () => {
293
296
  const { getByA11yLabel, getByAccessibilityHint } = render(
294
297
  <OverlayProvider>
295
298
  <Chat client={chatClient}>
@@ -299,8 +302,9 @@ describe('Giphy', () => {
299
302
  </Chat>
300
303
  </OverlayProvider>,
301
304
  );
302
-
303
- expect(getByAccessibilityHint('image-loading')).toBeTruthy();
305
+ await waitFor(() => {
306
+ expect(getByAccessibilityHint('image-loading')).toBeTruthy();
307
+ });
304
308
 
305
309
  fireEvent(getByA11yLabel('Giphy Attachment Image'), 'onLoadStart');
306
310
 
@@ -74,7 +74,7 @@ import {
74
74
  ThumbsUpReaction,
75
75
  WutReaction,
76
76
  } from '../../icons';
77
- import { FlatList as FlatListDefault } from '../../native';
77
+ import { FlatList as FlatListDefault, pickDocument } from '../../native';
78
78
  import * as dbApi from '../../store/apis';
79
79
  import type { DefaultStreamChatGenerics } from '../../types/types';
80
80
  import { addReactionToLocalState } from '../../utils/addReactionToLocalState';
@@ -455,7 +455,8 @@ const ChannelWithContext = <
455
455
  handleRetry,
456
456
  handleThreadReply,
457
457
  hasCommands = true,
458
- hasFilePicker = true,
458
+ // If pickDocument isn't available, default to hiding the file picker
459
+ hasFilePicker = pickDocument !== null,
459
460
  hasImagePicker = true,
460
461
  hideDateSeparators = false,
461
462
  hideStickyDateHeader = false,
@@ -1192,6 +1193,13 @@ const ChannelWithContext = <
1192
1193
  text,
1193
1194
  ...extraFields
1194
1195
  }: Partial<StreamMessage<StreamChatGenerics>>) => {
1196
+ // Exclude following properties from message.user within message preview,
1197
+ // since they could be long arrays and have no meaning as sender of message.
1198
+ // Storing such large value within user's table may cause sqlite queries to crash.
1199
+ // @ts-ignore
1200
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1201
+ const { channel_mutes, devices, mutes, ...messageUser } = client.user;
1202
+
1195
1203
  const preview = {
1196
1204
  __html: text,
1197
1205
  attachments,
@@ -1209,7 +1217,7 @@ const ChannelWithContext = <
1209
1217
  type: 'regular',
1210
1218
  user: {
1211
1219
  id: client.userID,
1212
- ...client.user,
1220
+ ...messageUser,
1213
1221
  },
1214
1222
  ...extraFields,
1215
1223
  } as unknown as MessageResponse<StreamChatGenerics>;
@@ -196,7 +196,7 @@ const ChatWithContext = <
196
196
 
197
197
  const setActiveChannel = (newChannel?: Channel<StreamChatGenerics>) => setChannel(newChannel);
198
198
 
199
- const appSettings = useAppSettings(client, isOnline);
199
+ const appSettings = useAppSettings(client, isOnline, enableOfflineSupport);
200
200
 
201
201
  const chatContext = useCreateChatContext({
202
202
  appSettings,
@@ -21,6 +21,7 @@ describe('useAppSettings', () => {
21
21
  userID: 'some-user-id',
22
22
  } as unknown as StreamChat,
23
23
  isOnline,
24
+ false,
24
25
  );
25
26
 
26
27
  return <Text>{JSON.stringify(appSettings)}</Text>;
@@ -1,23 +1,38 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
 
3
3
  import type { AppSettingsAPIResponse, StreamChat } from 'stream-chat';
4
- import type { DefaultStreamChatGenerics } from 'stream-chat-react-native';
5
4
 
5
+ import * as dbApi from '../../../store/apis';
6
+ import type { DefaultStreamChatGenerics } from '../../../types/types';
6
7
  export const useAppSettings = <
7
8
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
8
9
  >(
9
10
  client: StreamChat<StreamChatGenerics>,
10
- isOnline: boolean,
11
+ isOnline: boolean | null,
12
+ enableOfflineSupport: boolean,
11
13
  ): AppSettingsAPIResponse | null => {
12
14
  const [appSettings, setAppSettings] = useState<AppSettingsAPIResponse | null>(null);
13
15
  const isMounted = useRef(true);
14
16
 
15
17
  useEffect(() => {
16
- async function getAppSettings() {
18
+ async function enforeAppSettings() {
19
+ if (!client.userID) return;
20
+
21
+ if (!isOnline && enableOfflineSupport) {
22
+ const appSettings = dbApi.getAppSettings({ currentUserId: client.userID });
23
+ setAppSettings(appSettings);
24
+ return;
25
+ }
26
+
17
27
  try {
18
28
  const appSettings = await client.getAppSettings();
19
29
  if (isMounted.current) {
20
30
  setAppSettings(appSettings);
31
+ enableOfflineSupport &&
32
+ dbApi.upsertAppSettings({
33
+ appSettings,
34
+ currentUserId: client.userID as string,
35
+ });
21
36
  }
22
37
  } catch (error: unknown) {
23
38
  if (error instanceof Error) {
@@ -26,9 +41,7 @@ export const useAppSettings = <
26
41
  }
27
42
  }
28
43
 
29
- if (isOnline && client.userID) {
30
- getAppSettings();
31
- }
44
+ enforeAppSettings();
32
45
 
33
46
  return () => {
34
47
  isMounted.current = false;
@@ -22,7 +22,7 @@ export const useIsOnline = <
22
22
  client: StreamChat<StreamChatGenerics>,
23
23
  closeConnectionOnBackground = true,
24
24
  ) => {
25
- const [isOnline, setIsOnline] = useState(true);
25
+ const [isOnline, setIsOnline] = useState<boolean | null>(null);
26
26
  const [connectionRecovering, setConnectionRecovering] = useState(false);
27
27
  const isMounted = useIsMountedRef();
28
28
  const clientExists = !!client;
@@ -4,7 +4,7 @@ import type { SharedValue } from 'react-native-reanimated';
4
4
 
5
5
  import { act } from 'react-test-renderer';
6
6
 
7
- import { fireEvent, render } from '@testing-library/react-native';
7
+ import { fireEvent, render, waitFor } from '@testing-library/react-native';
8
8
 
9
9
  import {
10
10
  ImageGalleryContext,
@@ -45,7 +45,7 @@ const getComponent = (props: Partial<ImageGalleryContextValue>) => (
45
45
  );
46
46
 
47
47
  describe('ImageGallery', () => {
48
- it('render image gallery component', () => {
48
+ it('render image gallery component', async () => {
49
49
  const { queryAllByA11yLabel } = render(
50
50
  getComponent({
51
51
  messages: [
@@ -59,11 +59,14 @@ describe('ImageGallery', () => {
59
59
  ] as unknown as MessageType<DefaultStreamChatGenerics>[],
60
60
  }),
61
61
  );
62
- expect(queryAllByA11yLabel('Image Item')).toHaveLength(2);
63
- expect(queryAllByA11yLabel('Image Gallery Video')).toHaveLength(1);
62
+
63
+ await waitFor(() => {
64
+ expect(queryAllByA11yLabel('Image Item')).toHaveLength(2);
65
+ expect(queryAllByA11yLabel('Image Gallery Video')).toHaveLength(1);
66
+ });
64
67
  });
65
68
 
66
- it('handle handleLoad function when video item present and payload duration is available', () => {
69
+ it('handle handleLoad function when video item present and payload duration is available', async () => {
67
70
  const attachment = generateVideoAttachment({ type: 'video' });
68
71
  const message = generateMessage({
69
72
  attachments: [attachment],
@@ -86,10 +89,13 @@ describe('ImageGallery', () => {
86
89
  });
87
90
 
88
91
  const videoDurationComponent = getByA11yLabel('Video Duration');
89
- expect(videoDurationComponent.children[0]).toBe('00:10');
92
+
93
+ await waitFor(() => {
94
+ expect(videoDurationComponent.children[0]).toBe('00:10');
95
+ });
90
96
  });
91
97
 
92
- it('handle handleLoad function when video item present and payload duration is undefined', () => {
98
+ it('handle handleLoad function when video item present and payload duration is undefined', async () => {
93
99
  const { getByA11yLabel } = render(
94
100
  getComponent({
95
101
  messages: [
@@ -109,10 +115,12 @@ describe('ImageGallery', () => {
109
115
  });
110
116
 
111
117
  const videoDurationComponent = getByA11yLabel('Video Duration');
112
- expect(videoDurationComponent.children[0]).toBe('00:00');
118
+ await waitFor(() => {
119
+ expect(videoDurationComponent.children[0]).toBe('00:00');
120
+ });
113
121
  });
114
122
 
115
- it('handle handleProgress function when video item present and payload is well defined', () => {
123
+ it('handle handleProgress function when video item present and payload is well defined', async () => {
116
124
  const attachment = generateVideoAttachment({ type: 'video' });
117
125
  const message = generateMessage({
118
126
  attachments: [attachment],
@@ -143,10 +151,12 @@ describe('ImageGallery', () => {
143
151
 
144
152
  const progressDurationComponent = getByA11yLabel('Progress Duration');
145
153
 
146
- expect(progressDurationComponent.children[0]).toBe('00:03');
154
+ await waitFor(() => {
155
+ expect(progressDurationComponent.children[0]).toBe('00:03');
156
+ });
147
157
  });
148
158
 
149
- it('handle handleProgress function when video item present and payload is not defined', () => {
159
+ it('handle handleProgress function when video item present and payload is not defined', async () => {
150
160
  const { getByA11yLabel } = render(
151
161
  getComponent({
152
162
  messages: [
@@ -171,10 +181,12 @@ describe('ImageGallery', () => {
171
181
 
172
182
  const progressDurationComponent = getByA11yLabel('Progress Duration');
173
183
 
174
- expect(progressDurationComponent.children[0]).toBe('00:00');
184
+ await waitFor(() => {
185
+ expect(progressDurationComponent.children[0]).toBe('00:00');
186
+ });
175
187
  });
176
188
 
177
- it('handle handleEnd function when video item present', () => {
189
+ it('handle handleEnd function when video item present', async () => {
178
190
  const attachment = generateVideoAttachment({ type: 'video' });
179
191
  const message = generateMessage({
180
192
  attachments: [attachment],
@@ -198,7 +210,9 @@ describe('ImageGallery', () => {
198
210
  });
199
211
 
200
212
  const progressDurationComponent = getByA11yLabel('Progress Duration');
201
- expect(getByA11yLabel('Play Icon')).not.toBeUndefined();
202
- expect(progressDurationComponent.children[0]).toBe('00:10');
213
+ await waitFor(() => {
214
+ expect(getByA11yLabel('Play Icon')).not.toBeUndefined();
215
+ expect(progressDurationComponent.children[0]).toBe('00:10');
216
+ });
203
217
  });
204
218
  });
@@ -97,10 +97,13 @@ describe('ImageGalleryFooter', () => {
97
97
  </ImageGalleryContext.Provider>
98
98
  </OverlayProvider>,
99
99
  );
100
- expect(queryAllByText('Left element')).toHaveLength(1);
101
- expect(queryAllByText('Right element')).toHaveLength(1);
102
- expect(queryAllByText('Center element')).toHaveLength(1);
103
- expect(queryAllByText('Video Control element')).toHaveLength(1);
100
+
101
+ await waitFor(() => {
102
+ expect(queryAllByText('Left element')).toHaveLength(1);
103
+ expect(queryAllByText('Right element')).toHaveLength(1);
104
+ expect(queryAllByText('Center element')).toHaveLength(1);
105
+ expect(queryAllByText('Video Control element')).toHaveLength(1);
106
+ });
104
107
  });
105
108
 
106
109
  it('render image gallery footer component with custom component footer Grid Icon and Share Icon component', async () => {
@@ -147,8 +150,11 @@ describe('ImageGalleryFooter', () => {
147
150
  </ImageGalleryContext.Provider>
148
151
  </OverlayProvider>,
149
152
  );
150
- expect(queryAllByText('Share Icon element')).toHaveLength(1);
151
- expect(queryAllByText('Grid Icon element')).toHaveLength(1);
153
+
154
+ await waitFor(() => {
155
+ expect(queryAllByText('Share Icon element')).toHaveLength(1);
156
+ expect(queryAllByText('Grid Icon element')).toHaveLength(1);
157
+ });
152
158
  });
153
159
 
154
160
  it('should trigger the share button onPress Handler', async () => {
@@ -90,9 +90,12 @@ describe('ImageGalleryHeader', () => {
90
90
  </ImageGalleryContext.Provider>
91
91
  </OverlayProvider>,
92
92
  );
93
- expect(queryAllByText('Left element')).toHaveLength(1);
94
- expect(queryAllByText('Right element')).toHaveLength(1);
95
- expect(queryAllByText('Center element')).toHaveLength(1);
93
+
94
+ await waitFor(() => {
95
+ expect(queryAllByText('Left element')).toHaveLength(1);
96
+ expect(queryAllByText('Right element')).toHaveLength(1);
97
+ expect(queryAllByText('Center element')).toHaveLength(1);
98
+ });
96
99
  });
97
100
 
98
101
  it('render image gallery header component with custom Close Icon component', async () => {
@@ -132,7 +135,9 @@ describe('ImageGalleryHeader', () => {
132
135
  </ImageGalleryContext.Provider>
133
136
  </OverlayProvider>,
134
137
  );
135
- expect(queryAllByText('Close Icon element')).toHaveLength(1);
138
+ await waitFor(() => {
139
+ expect(queryAllByText('Close Icon element')).toHaveLength(1);
140
+ });
136
141
  });
137
142
 
138
143
  it('should trigger the hideOverlay function on button onPress', async () => {
@@ -137,7 +137,7 @@ export const ImageGrid = <
137
137
  theme: {
138
138
  colors: { white },
139
139
  imageGallery: {
140
- grid: { contentContainer },
140
+ grid: { container, contentContainer },
141
141
  },
142
142
  },
143
143
  } = useTheme();
@@ -165,6 +165,7 @@ export const ImageGrid = <
165
165
  keyExtractor={(item, index) => `${item.uri}-${index}`}
166
166
  numColumns={numberOfImageGalleryGridColumns || 3}
167
167
  renderItem={renderItem}
168
+ style={container}
168
169
  />
169
170
  );
170
171
  };
@@ -13,8 +13,8 @@ const styles = StyleSheet.create({
13
13
  },
14
14
  handle: {
15
15
  alignItems: 'center',
16
- borderTopLeftRadius: 16,
17
- borderTopRightRadius: 16,
16
+ borderTopLeftRadius: 12,
17
+ borderTopRightRadius: 12,
18
18
  flexDirection: 'row',
19
19
  height: 40,
20
20
  justifyContent: 'center',
@@ -36,7 +36,7 @@ export type ChatContextValue<
36
36
  connectionRecovering: boolean;
37
37
  enableOfflineSupport: boolean;
38
38
  ImageComponent: React.ComponentType<ImageProps>;
39
- isOnline: boolean;
39
+ isOnline: boolean | null;
40
40
  mutedUsers: Mute<StreamChatGenerics>[];
41
41
  /**
42
42
  * @param newChannel Channel to set as active.
@@ -575,6 +575,13 @@ export const MessageInputProvider = <
575
575
  };
576
576
 
577
577
  const pickFile = async () => {
578
+ if (pickDocument === null) {
579
+ console.log(
580
+ 'The file picker is not installed. Check our Getting Started documentation to install it.',
581
+ );
582
+ return;
583
+ }
584
+
578
585
  if (numberOfUploads >= value.maxNumberOfFiles) {
579
586
  Alert.alert('Maximum number of files reached');
580
587
  return;
@@ -4,6 +4,7 @@ import type { TextInput } from 'react-native';
4
4
 
5
5
  import { act, renderHook } from '@testing-library/react-hooks';
6
6
 
7
+ import { waitFor } from '@testing-library/react-native';
7
8
  import type { AppSettingsAPIResponse, StreamChat } from 'stream-chat';
8
9
 
9
10
  import { ChatContextValue, ChatProvider } from '../../../contexts/chatContext/ChatContext';
@@ -167,7 +168,7 @@ describe('MessageInputContext', () => {
167
168
  expect(result.current.text).toBe('/');
168
169
  });
169
170
 
170
- it('openMentionPicker works', () => {
171
+ it('openMentionPicker works', async () => {
171
172
  const { result } = renderHook(() => useMessageInputContext(), {
172
173
  initialProps: {
173
174
  editing: true,
@@ -180,7 +181,9 @@ describe('MessageInputContext', () => {
180
181
  result.current.openMentionsPicker();
181
182
  });
182
183
 
183
- expect(result.current.text).toBe('@');
184
+ await waitFor(() => {
185
+ expect(result.current.text).toBe('@');
186
+ });
184
187
  });
185
188
 
186
189
  it('openAttachmentPicker works', async () => {
@@ -203,6 +206,8 @@ describe('MessageInputContext', () => {
203
206
  result.current.openAttachmentPicker();
204
207
  });
205
208
 
206
- expect(await result.current.pickFile()).toBe(undefined);
209
+ await waitFor(async () => {
210
+ expect(await result.current.pickFile()).toBe(undefined);
211
+ });
207
212
  });
208
213
  });
@@ -3,6 +3,8 @@ import { act } from 'react-test-renderer';
3
3
 
4
4
  import { renderHook } from '@testing-library/react-hooks';
5
5
 
6
+ import { waitFor } from '@testing-library/react-native';
7
+
6
8
  import { generateFileUploadPreview } from '../../../mock-builders/generator/attachment';
7
9
  import type { DefaultStreamChatGenerics } from '../../../types/types';
8
10
  import {
@@ -31,7 +33,7 @@ const Wrapper = <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultS
31
33
  );
32
34
 
33
35
  describe("MessageInputContext's uploadFile", () => {
34
- it('uploadFile works', () => {
36
+ it('uploadFile works', async () => {
35
37
  const doDocUploadRequestMock = jest.fn().mockResolvedValue({
36
38
  file: {
37
39
  url: '',
@@ -46,16 +48,20 @@ describe("MessageInputContext's uploadFile", () => {
46
48
  wrapper: Wrapper,
47
49
  });
48
50
 
49
- expect(result.current.fileUploads).toHaveLength(0);
51
+ await waitFor(() => {
52
+ expect(result.current.fileUploads).toHaveLength(0);
53
+ });
50
54
 
51
55
  act(() => {
52
56
  result.current.uploadFile({ newFile: generateFileUploadPreview({ state: '' }) });
53
57
  });
54
58
 
55
- expect(doDocUploadRequestMock).toHaveBeenCalled();
59
+ await waitFor(() => {
60
+ expect(doDocUploadRequestMock).toHaveBeenCalled();
61
+ });
56
62
  });
57
63
 
58
- it('uploadFile catch block gets executed', () => {
64
+ it('uploadFile catch block gets executed', async () => {
59
65
  const doDocUploadRequestMock = jest.fn().mockResolvedValue(new Error('This is an error'));
60
66
  const { result } = renderHook(() => useMessageInputContext(), {
61
67
  initialProps: {
@@ -65,12 +71,16 @@ describe("MessageInputContext's uploadFile", () => {
65
71
  wrapper: Wrapper,
66
72
  });
67
73
 
68
- expect(result.current.fileUploads).toHaveLength(0);
74
+ await waitFor(() => {
75
+ expect(result.current.fileUploads).toHaveLength(0);
76
+ });
69
77
 
70
78
  act(() => {
71
79
  result.current.uploadFile({ newFile: generateFileUploadPreview({ state: '' }) });
72
80
  });
73
81
 
74
- expect(result.current.fileUploads.length).toBe(0);
82
+ await waitFor(() => {
83
+ expect(result.current.fileUploads.length).toBe(0);
84
+ });
75
85
  });
76
86
  });