stream-chat-react-native-core 5.1.0-beta.1 → 5.2.0-beta.1

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 (66) hide show
  1. package/lib/commonjs/components/ChannelList/ChannelListMessenger.js +32 -15
  2. package/lib/commonjs/components/ChannelList/ChannelListMessenger.js.map +1 -1
  3. package/lib/commonjs/components/Chat/Chat.js +17 -5
  4. package/lib/commonjs/components/Chat/Chat.js.map +1 -1
  5. package/lib/commonjs/components/ImageGallery/ImageGallery.js +106 -100
  6. package/lib/commonjs/components/ImageGallery/ImageGallery.js.map +1 -1
  7. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryVideo.js +14 -16
  8. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryVideo.js.map +1 -1
  9. package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js +13 -16
  10. package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
  11. package/lib/commonjs/components/ImageGallery/components/ImageGalleryVideoControl.js +6 -6
  12. package/lib/commonjs/components/ImageGallery/components/ImageGalleryVideoControl.js.map +1 -1
  13. package/lib/commonjs/components/MessageList/MessageList.js +39 -26
  14. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  15. package/lib/commonjs/components/ProgressControl/ProgressControl.js +6 -6
  16. package/lib/commonjs/components/ProgressControl/ProgressControl.js.map +1 -1
  17. package/lib/commonjs/contexts/debugContext/DebugContext.js +63 -0
  18. package/lib/commonjs/contexts/debugContext/DebugContext.js.map +1 -0
  19. package/lib/commonjs/contexts/index.js +13 -0
  20. package/lib/commonjs/contexts/index.js.map +1 -1
  21. package/lib/commonjs/native.js.map +1 -1
  22. package/lib/commonjs/version.json +1 -1
  23. package/lib/module/components/ChannelList/ChannelListMessenger.js +32 -15
  24. package/lib/module/components/ChannelList/ChannelListMessenger.js.map +1 -1
  25. package/lib/module/components/Chat/Chat.js +17 -5
  26. package/lib/module/components/Chat/Chat.js.map +1 -1
  27. package/lib/module/components/ImageGallery/ImageGallery.js +106 -100
  28. package/lib/module/components/ImageGallery/ImageGallery.js.map +1 -1
  29. package/lib/module/components/ImageGallery/components/AnimatedGalleryVideo.js +14 -16
  30. package/lib/module/components/ImageGallery/components/AnimatedGalleryVideo.js.map +1 -1
  31. package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js +13 -16
  32. package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
  33. package/lib/module/components/ImageGallery/components/ImageGalleryVideoControl.js +6 -6
  34. package/lib/module/components/ImageGallery/components/ImageGalleryVideoControl.js.map +1 -1
  35. package/lib/module/components/MessageList/MessageList.js +39 -26
  36. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  37. package/lib/module/components/ProgressControl/ProgressControl.js +6 -6
  38. package/lib/module/components/ProgressControl/ProgressControl.js.map +1 -1
  39. package/lib/module/contexts/debugContext/DebugContext.js +63 -0
  40. package/lib/module/contexts/debugContext/DebugContext.js.map +1 -0
  41. package/lib/module/contexts/index.js +13 -0
  42. package/lib/module/contexts/index.js.map +1 -1
  43. package/lib/module/native.js.map +1 -1
  44. package/lib/module/version.json +1 -1
  45. package/lib/typescript/components/ImageGallery/ImageGallery.d.ts +3 -0
  46. package/lib/typescript/components/ImageGallery/components/AnimatedGalleryVideo.d.ts +5 -3
  47. package/lib/typescript/components/ImageGallery/components/ImageGalleryFooter.d.ts +1 -3
  48. package/lib/typescript/components/ProgressControl/ProgressControl.d.ts +1 -1
  49. package/lib/typescript/contexts/debugContext/DebugContext.d.ts +27 -0
  50. package/lib/typescript/contexts/index.d.ts +1 -0
  51. package/lib/typescript/native.d.ts +3 -2
  52. package/package.json +2 -2
  53. package/src/components/ChannelList/ChannelListMessenger.tsx +17 -0
  54. package/src/components/Chat/Chat.tsx +13 -0
  55. package/src/components/ImageGallery/ImageGallery.tsx +87 -65
  56. package/src/components/ImageGallery/__tests__/AnimatedVideoGallery.test.tsx +2 -2
  57. package/src/components/ImageGallery/__tests__/ImageGallery.test.tsx +40 -28
  58. package/src/components/ImageGallery/components/AnimatedGalleryVideo.tsx +16 -9
  59. package/src/components/ImageGallery/components/ImageGalleryFooter.tsx +1 -6
  60. package/src/components/ImageGallery/components/ImageGalleryVideoControl.tsx +5 -6
  61. package/src/components/MessageList/MessageList.tsx +15 -0
  62. package/src/components/ProgressControl/ProgressControl.tsx +8 -4
  63. package/src/contexts/debugContext/DebugContext.tsx +77 -0
  64. package/src/contexts/index.ts +1 -0
  65. package/src/native.ts +3 -2
  66. package/src/version.json +1 -1
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "5.1.0-beta.1"
2
+ "version": "5.2.0-beta.1"
3
3
  }
@@ -70,10 +70,13 @@ export declare type Photo<StreamChatGenerics extends DefaultStreamChatGenerics =
70
70
  uri: string;
71
71
  channelId?: string;
72
72
  created_at?: string | Date;
73
+ duration?: number;
73
74
  messageId?: string;
74
75
  mime_type?: string;
75
76
  original_height?: number;
76
77
  original_width?: number;
78
+ paused?: boolean;
79
+ progress?: number;
77
80
  type?: string;
78
81
  user?: UserResponse<StreamChatGenerics> | null;
79
82
  user_id?: string;
@@ -2,11 +2,12 @@ import React from 'react';
2
2
  import { ViewStyle } from 'react-native';
3
3
  import type { StyleProp } from 'react-native';
4
4
  import Animated from 'react-native-reanimated';
5
- import { VideoPayloadData, VideoProgressData, VideoType } from '../../../native';
5
+ import { VideoType } from '../../../native';
6
6
  export declare type AnimatedGalleryVideoType = {
7
+ attachmentId: string;
7
8
  handleEnd: () => void;
8
- handleLoad: (payload: VideoPayloadData) => void;
9
- handleProgress: (data: VideoProgressData) => void;
9
+ handleLoad: (index: string, duration: number) => void;
10
+ handleProgress: (index: string, progress: number, hasEnd?: boolean) => void;
10
11
  index: number;
11
12
  offsetScale: Animated.SharedValue<number>;
12
13
  paused: boolean;
@@ -21,6 +22,7 @@ export declare type AnimatedGalleryVideoType = {
21
22
  translateX: Animated.SharedValue<number>;
22
23
  translateY: Animated.SharedValue<number>;
23
24
  videoRef: React.RefObject<VideoType>;
25
+ repeat?: boolean;
24
26
  style?: StyleProp<ViewStyle>;
25
27
  };
26
28
  export declare const AnimatedGalleryVideo: React.FC<AnimatedGalleryVideoType>;
@@ -11,11 +11,10 @@ export declare type ImageGalleryFooterCustomComponent<StreamChatGenerics extends
11
11
  export declare type ImageGalleryFooterVideoControlProps = {
12
12
  duration: number;
13
13
  onPlayPause: (status?: boolean) => void;
14
- onProgressDrag: (progress: number) => void;
15
14
  paused: boolean;
16
15
  progress: number;
17
16
  };
18
- export declare type ImageGalleryFooterVideoControlComponent = ({ duration, onPlayPause, onProgressDrag, paused, progress, }: ImageGalleryFooterVideoControlProps) => React.ReactElement | null;
17
+ export declare type ImageGalleryFooterVideoControlComponent = ({ duration, onPlayPause, paused, progress, }: ImageGalleryFooterVideoControlProps) => React.ReactElement | null;
19
18
  export declare type ImageGalleryFooterCustomComponentProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
20
19
  centerElement?: ImageGalleryFooterCustomComponent<StreamChatGenerics>;
21
20
  GridIcon?: React.ReactElement;
@@ -28,7 +27,6 @@ declare type ImageGalleryFooterPropsWithContext<StreamChatGenerics extends Defau
28
27
  accessibilityLabel: string;
29
28
  duration: number;
30
29
  onPlayPause: () => void;
31
- onProgressDrag: (progress: number) => void;
32
30
  opacity: Animated.SharedValue<number>;
33
31
  openGridView: () => void;
34
32
  paused: boolean;
@@ -3,9 +3,9 @@ export declare type ProgressControlProps = {
3
3
  duration: number;
4
4
  filledColor: string;
5
5
  onPlayPause: (status?: boolean) => void;
6
- onProgressDrag: (progress: number) => void;
7
6
  progress: number;
8
7
  testID: string;
9
8
  width: number;
9
+ onProgressDrag?: (progress: number) => void;
10
10
  };
11
11
  export declare const ProgressControl: React.FC<ProgressControlProps>;
@@ -0,0 +1,27 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import type { Channel, ChannelState, StreamChat } from 'stream-chat';
3
+ import type { MessageType } from '../../components/MessageList/hooks/useMessageList';
4
+ import type { DefaultStreamChatGenerics } from '../../types/types';
5
+ export declare type DebugDataType<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = StreamChat<StreamChatGenerics>['user'] | {
6
+ data: Channel<StreamChatGenerics>['data'];
7
+ members: ChannelState<StreamChatGenerics>['members'];
8
+ }[] | MessageType<StreamChatGenerics>[];
9
+ export declare type DebugContextValue<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
10
+ eventType?: string;
11
+ sendEventParams?: {
12
+ action: string;
13
+ data: DebugDataType<StreamChatGenerics>;
14
+ };
15
+ setEventType?: (data: string) => void;
16
+ setSendEventParams?: (data: {
17
+ action: string;
18
+ data: DebugDataType<StreamChatGenerics>;
19
+ }) => void;
20
+ };
21
+ export declare const DebugContext: React.Context<React.MutableRefObject<DebugContextValue<DefaultStreamChatGenerics>>>;
22
+ export declare const DebugContextProvider: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ children, useFlipper, }: React.PropsWithChildren<{
23
+ useFlipper: () => {
24
+ updateData: (ref: React.RefObject<DebugContextValue<StreamChatGenerics>>) => void;
25
+ };
26
+ }>) => JSX.Element;
27
+ export declare const useDebugContext: () => React.MutableRefObject<DebugContextValue<DefaultStreamChatGenerics>>;
@@ -2,6 +2,7 @@ export * from './attachmentPickerContext/AttachmentPickerContext';
2
2
  export * from './channelContext/ChannelContext';
3
3
  export * from './channelsContext/ChannelsContext';
4
4
  export * from './chatContext/ChatContext';
5
+ export * from './debugContext/DebugContext';
5
6
  export * from './imageGalleryContext/ImageGalleryContext';
6
7
  export * from './keyboardContext/KeyboardContext';
7
8
  export * from './messageContext/MessageContext';
@@ -130,9 +130,9 @@ export declare type SoundType = {
130
130
  };
131
131
  export declare let Sound: SoundType;
132
132
  export declare type VideoProgressData = {
133
- currentTime?: number;
133
+ currentTime: number;
134
+ seekableDuration: number;
134
135
  playableDuration?: number;
135
- seekableDuration?: number;
136
136
  };
137
137
  export declare type VideoPayloadData = {
138
138
  duration: number;
@@ -176,6 +176,7 @@ export declare type VideoType = {
176
176
  onPlaybackStatusUpdate?: (playbackStatus: PlaybackStatus) => void;
177
177
  onProgress?: (data: VideoProgressData) => void;
178
178
  onReadyForDisplay?: () => void;
179
+ repeat?: boolean;
179
180
  replayAsync?: () => void;
180
181
  resizeMode?: string;
181
182
  seek?: (progress: number) => void;
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.1.0-beta.1",
4
+ "version": "5.2.0-beta.1",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
@@ -94,13 +94,13 @@
94
94
  "@babel/preset-env": "7.13.12",
95
95
  "@babel/preset-typescript": "7.13.0",
96
96
  "@babel/runtime": "^7.12.5",
97
- "@types/better-sqlite3": "^7.6.0",
98
97
  "@react-native-community/eslint-config": "2.0.0",
99
98
  "@react-native-community/eslint-plugin": "1.1.0",
100
99
  "@react-native-community/netinfo": "6.0.0",
101
100
  "@testing-library/jest-native": "4.0.4",
102
101
  "@testing-library/react-hooks": "7.0.2",
103
102
  "@testing-library/react-native": "9.0.0",
103
+ "@types/better-sqlite3": "^7.6.0",
104
104
  "@types/eslint": "7.2.10",
105
105
  "@types/jest": "26.0.23",
106
106
  "@types/lodash": "4.14.169",
@@ -10,6 +10,7 @@ import {
10
10
  useChannelsContext,
11
11
  } from '../../contexts/channelsContext/ChannelsContext';
12
12
  import { useChatContext } from '../../contexts/chatContext/ChatContext';
13
+ import { useDebugContext } from '../../contexts/debugContext/DebugContext';
13
14
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
14
15
 
15
16
  import type { DefaultStreamChatGenerics } from '../../types/types';
@@ -116,12 +117,28 @@ const ChannelListMessengerWithContext = <
116
117
  * change to loadingChannels is registered.
117
118
  */
118
119
  const [loading, setLoading] = useState(true);
120
+ const debugRef = useDebugContext();
121
+
119
122
  useEffect(() => {
120
123
  if (!!loadingChannels !== loading) {
121
124
  setLoading(!!loadingChannels);
122
125
  }
123
126
  }, [loading, loadingChannels]);
124
127
 
128
+ const isDebugModeEnabled = __DEV__ && debugRef && debugRef.current;
129
+
130
+ if (isDebugModeEnabled) {
131
+ if (debugRef.current.setEventType) debugRef.current.setEventType('send');
132
+ if (debugRef.current.setSendEventParams)
133
+ debugRef.current.setSendEventParams({
134
+ action: 'Channels',
135
+ data: channels.map((channel) => ({
136
+ data: channel.data,
137
+ members: channel.state.members,
138
+ })),
139
+ });
140
+ }
141
+
125
142
  if (error && !refreshing && !loadingChannels && !channels?.length) {
126
143
  return (
127
144
  <LoadingErrorIndicator
@@ -15,6 +15,7 @@ import { useSyncDatabase } from './hooks/useSyncDatabase';
15
15
 
16
16
  import { ChannelsStateProvider } from '../../contexts/channelsStateContext/ChannelsStateContext';
17
17
  import { ChatContextValue, ChatProvider } from '../../contexts/chatContext/ChatContext';
18
+ import { useDebugContext } from '../../contexts/debugContext/DebugContext';
18
19
  import { useOverlayContext } from '../../contexts/overlayContext/OverlayContext';
19
20
  import { DeepPartial, ThemeProvider } from '../../contexts/themeContext/ThemeContext';
20
21
  import type { Theme } from '../../contexts/themeContext/utils/theme';
@@ -170,6 +171,9 @@ const ChatWithContext = <
170
171
  */
171
172
  const mutedUsers = useMutedUsers<StreamChatGenerics>(client);
172
173
 
174
+ const debugRef = useDebugContext();
175
+ const isDebugModeEnabled = __DEV__ && debugRef && debugRef.current;
176
+
173
177
  useEffect(() => {
174
178
  if (client) {
175
179
  client.setUserAgent(`${SDK}-${Platform.OS}-${version}`);
@@ -177,6 +181,15 @@ const ChatWithContext = <
177
181
  client.recoverStateOnReconnect = false;
178
182
  client.persistUserOnConnectionFailure = enableOfflineSupport;
179
183
  }
184
+
185
+ if (isDebugModeEnabled) {
186
+ if (debugRef.current.setEventType) debugRef.current.setEventType('send');
187
+ if (debugRef.current.setSendEventParams)
188
+ debugRef.current.setSendEventParams({
189
+ action: 'Client',
190
+ data: client.user,
191
+ });
192
+ }
180
193
  }, [client]);
181
194
 
182
195
  const setActiveChannel = (newChannel?: Channel<StreamChatGenerics>) => setChannel(newChannel);
@@ -55,12 +55,7 @@ import {
55
55
  useOverlayContext,
56
56
  } from '../../contexts/overlayContext/OverlayContext';
57
57
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
58
- import {
59
- isVideoPackageAvailable,
60
- VideoPayloadData,
61
- VideoProgressData,
62
- VideoType,
63
- } from '../../native';
58
+ import { isVideoPackageAvailable, VideoType } from '../../native';
64
59
  import type { DefaultStreamChatGenerics } from '../../types/types';
65
60
  import { getResizedImageUrl } from '../../utils/getResizedImageUrl';
66
61
  import { getUrlOfImageAttachment } from '../../utils/getUrlOfImageAttachment';
@@ -151,9 +146,9 @@ export const ImageGallery = <
151
146
  numberOfImageGalleryGridColumns,
152
147
  overlayOpacity,
153
148
  } = props;
154
- const [paused, setPaused] = useState<boolean>(false);
155
- const [progress, setProgress] = useState<number>(0);
156
- const [duration, setDuration] = useState<number>(0);
149
+ const [imageGalleryAttachments, setImageGalleryAttachments] = useState<
150
+ Photo<StreamChatGenerics>[]
151
+ >([]);
157
152
  const {
158
153
  theme: {
159
154
  colors: { white_snow },
@@ -282,11 +277,14 @@ export const ImageGallery = <
282
277
  return {
283
278
  channelId: cur.cid,
284
279
  created_at: cur.created_at,
280
+ duration: 0,
285
281
  id: `photoId-${cur.id}-${imageUrl}`,
286
282
  messageId: cur.id,
287
283
  mime_type: a.type === 'giphy' ? getGiphyMimeType(giphyURL ?? '') : a.mime_type,
288
284
  original_height: a.original_height,
289
285
  original_width: a.original_width,
286
+ paused: true,
287
+ progress: 0,
290
288
  type: a.type,
291
289
  uri:
292
290
  a.type === 'giphy'
@@ -312,6 +310,10 @@ export const ImageGallery = <
312
310
  */
313
311
  const photoLength = photos.length;
314
312
 
313
+ useEffect(() => {
314
+ setImageGalleryAttachments(photos);
315
+ }, []);
316
+
315
317
  /**
316
318
  * The URL for the images may differ because of dimensions passed as
317
319
  * part of the query.
@@ -338,10 +340,6 @@ export const ImageGallery = <
338
340
  stripQueryFromUrl(photo.uri) === stripQueryFromUrl(selectedMessage?.url || ''),
339
341
  );
340
342
 
341
- if (photoLength > 1) {
342
- setPaused(true);
343
- }
344
-
345
343
  runOnUI(updatePosition)(newIndex);
346
344
  }, [selectedMessage, photoLength]);
347
345
 
@@ -351,10 +349,11 @@ export const ImageGallery = <
351
349
  * to the proper scaled height based on the width being restricted to the
352
350
  * screen width when the dimensions are received.
353
351
  */
354
- const uriForCurrentImage = photos[selectedIndex]?.uri;
352
+ const uriForCurrentImage = imageGalleryAttachments[selectedIndex]?.uri;
353
+
355
354
  useEffect(() => {
356
355
  setCurrentImageHeight(screenHeight);
357
- const photo = photos[index.value];
356
+ const photo = imageGalleryAttachments[index.value];
358
357
  const height = photo?.original_height;
359
358
  const width = photo?.original_width;
360
359
 
@@ -463,53 +462,69 @@ export const ImageGallery = <
463
462
  const openGridView = () => {
464
463
  if (bottomSheetModalRef.current?.present) {
465
464
  bottomSheetModalRef.current.present();
466
- setGridPhotos(photos);
465
+ setGridPhotos(imageGalleryAttachments);
467
466
  }
468
467
  };
469
468
 
469
+ const handleEnd = () => {
470
+ handlePlayPause(imageGalleryAttachments[selectedIndex].id, true);
471
+ handleProgress(imageGalleryAttachments[selectedIndex].id, 1, true);
472
+ };
473
+
470
474
  const videoRef = useRef<VideoType>(null);
471
475
 
472
- const handleEnd = () => {
473
- setPaused(true);
474
- setProgress(1);
476
+ const handleLoad = (index: string, duration: number) => {
477
+ setImageGalleryAttachments((prevImageGalleryAttachment) =>
478
+ prevImageGalleryAttachment.map((imageGalleryAttachment) => ({
479
+ ...imageGalleryAttachment,
480
+ duration: imageGalleryAttachment.id === index ? duration : imageGalleryAttachment.duration,
481
+ })),
482
+ );
475
483
  };
476
484
 
477
- const handleLoad = (payload: VideoPayloadData) => {
478
- if (payload.duration) setDuration(payload.duration);
485
+ const handleProgress = (index: string, progress: number, hasEnd?: boolean) => {
486
+ setImageGalleryAttachments((prevImageGalleryAttachments) =>
487
+ prevImageGalleryAttachments.map((imageGalleryAttachment) => ({
488
+ ...imageGalleryAttachment,
489
+ progress:
490
+ imageGalleryAttachment.id === index
491
+ ? hasEnd
492
+ ? 1
493
+ : progress
494
+ : imageGalleryAttachment.progress,
495
+ })),
496
+ );
479
497
  };
480
498
 
481
- const handleProgress = (data: VideoProgressData) => {
482
- if (data.currentTime && data.seekableDuration) {
483
- setProgress(data.currentTime / data.seekableDuration);
499
+ const handlePlayPause = (index: string, pausedStatus?: boolean) => {
500
+ if (pausedStatus === false) {
501
+ // If the status is false we set the audio with the index as playing and the others as paused.
502
+ setImageGalleryAttachments((prevImageGalleryAttachment) =>
503
+ prevImageGalleryAttachment.map((imageGalleryAttachment) => ({
504
+ ...imageGalleryAttachment,
505
+ paused: imageGalleryAttachment.id === index ? false : true,
506
+ })),
507
+ );
508
+ } else {
509
+ // If the status is true we simply set all the audio's paused state as true.
510
+ setImageGalleryAttachments((prevImageGalleryAttachment) =>
511
+ prevImageGalleryAttachment.map((imageGalleryAttachment) => ({
512
+ ...imageGalleryAttachment,
513
+ paused: true,
514
+ })),
515
+ );
484
516
  }
485
517
  };
486
518
 
487
- const handlePlayPause = (status?: boolean) => {
519
+ const onPlayPause = (status?: boolean) => {
488
520
  if (status === undefined) {
489
- // React Native Video for RN CLI has seek as an API to move to a particular location in the video
490
- if (progress === 1 && videoRef.current && videoRef.current.seek) {
491
- videoRef.current.seek(0);
492
- }
493
- // Expo AV for Expo has replayAsync as an API to move to a starting of the video
494
- if (progress === 1 && videoRef.current && videoRef.current.replayAsync) {
495
- videoRef.current.replayAsync();
521
+ if (imageGalleryAttachments[selectedIndex].paused) {
522
+ handlePlayPause(imageGalleryAttachments[selectedIndex].id, false);
523
+ } else {
524
+ handlePlayPause(imageGalleryAttachments[selectedIndex].id, true);
496
525
  }
497
-
498
- setPaused((state) => !state);
499
526
  } else {
500
- setPaused(status);
501
- }
502
- };
503
-
504
- const onProgressDrag = (progress: number) => {
505
- // React Native Video for RN CLI has seek as an API to move to a particular location in the video
506
- if (videoRef.current && videoRef.current.seek) {
507
- videoRef.current.seek(progress);
508
- }
509
-
510
- // Expo AV for Expo has setPositionAsync as an API to move to a particular location of the video
511
- if (videoRef.current && videoRef.current.setPositionAsync) {
512
- videoRef.current.setPositionAsync(progress * 1000);
527
+ handlePlayPause(imageGalleryAttachments[selectedIndex].id, status);
513
528
  }
514
529
  };
515
530
 
@@ -568,17 +583,19 @@ export const ImageGallery = <
568
583
  },
569
584
  ]}
570
585
  >
571
- {photos.map((photo, i) =>
586
+ {imageGalleryAttachments.map((photo, i) =>
572
587
  photo.type === 'video' ? (
573
588
  <AnimatedGalleryVideo
589
+ attachmentId={photo.id}
574
590
  handleEnd={handleEnd}
575
591
  handleLoad={handleLoad}
576
592
  handleProgress={handleProgress}
577
593
  index={i}
578
594
  key={`${photo.uri}-${i}`}
579
595
  offsetScale={offsetScale}
580
- paused={paused}
596
+ paused={photo.paused || false}
581
597
  previous={selectedIndex > i}
598
+ repeat={true}
582
599
  scale={scale}
583
600
  screenHeight={screenHeight}
584
601
  selected={selectedIndex === i}
@@ -632,25 +649,27 @@ export const ImageGallery = <
632
649
  </TapGestureHandler>
633
650
  <ImageGalleryHeader<StreamChatGenerics>
634
651
  opacity={headerFooterOpacity}
635
- photo={photos[selectedIndex]}
652
+ photo={imageGalleryAttachments[selectedIndex]}
636
653
  visible={headerFooterVisible}
637
654
  {...imageGalleryCustomComponents?.header}
638
655
  />
639
- <ImageGalleryFooter<StreamChatGenerics>
640
- accessibilityLabel={'Image Gallery Footer'}
641
- duration={duration}
642
- onPlayPause={handlePlayPause}
643
- onProgressDrag={onProgressDrag}
644
- opacity={headerFooterOpacity}
645
- openGridView={openGridView}
646
- paused={paused}
647
- photo={photos[selectedIndex]}
648
- photoLength={photoLength}
649
- progress={progress}
650
- selectedIndex={selectedIndex}
651
- visible={headerFooterVisible}
652
- {...imageGalleryCustomComponents?.footer}
653
- />
656
+
657
+ {imageGalleryAttachments.length > 0 && (
658
+ <ImageGalleryFooter<StreamChatGenerics>
659
+ accessibilityLabel={'Image Gallery Footer'}
660
+ duration={imageGalleryAttachments[selectedIndex].duration || 0}
661
+ onPlayPause={onPlayPause}
662
+ opacity={headerFooterOpacity}
663
+ openGridView={openGridView}
664
+ paused={imageGalleryAttachments[selectedIndex].paused || false}
665
+ photo={imageGalleryAttachments[selectedIndex]}
666
+ photoLength={imageGalleryAttachments.length}
667
+ progress={imageGalleryAttachments[selectedIndex].progress || 0}
668
+ selectedIndex={selectedIndex}
669
+ visible={headerFooterVisible}
670
+ {...imageGalleryCustomComponents?.footer}
671
+ />
672
+ )}
654
673
 
655
674
  <ImageGalleryOverlay
656
675
  animatedBottomSheetIndex={animatedBottomSheetIndex}
@@ -708,10 +727,13 @@ export type Photo<
708
727
  uri: string;
709
728
  channelId?: string;
710
729
  created_at?: string | Date;
730
+ duration?: number;
711
731
  messageId?: string;
712
732
  mime_type?: string;
713
733
  original_height?: number;
714
734
  original_width?: number;
735
+ paused?: boolean;
736
+ progress?: number;
715
737
  type?: string;
716
738
  user?: UserResponse<StreamChatGenerics> | null;
717
739
  user_id?: string;
@@ -78,7 +78,7 @@ describe('ImageGallery', () => {
78
78
 
79
79
  act(() => {
80
80
  fireEvent(videoComponent, 'onEnd');
81
- fireEvent(videoComponent, 'onProgress');
81
+ fireEvent(videoComponent, 'onProgress', { currentTime: 10, seekableDuration: 60 });
82
82
  });
83
83
 
84
84
  expect(handleEndMock).toHaveBeenCalled();
@@ -127,7 +127,7 @@ describe('ImageGallery', () => {
127
127
  const spinnerComponent = queryByA11yLabel('Spinner');
128
128
 
129
129
  act(() => {
130
- fireEvent(videoComponent, 'onLoad');
130
+ fireEvent(videoComponent, 'onLoad', { duration: 10 });
131
131
  });
132
132
 
133
133
  expect(handleLoadMock).toHaveBeenCalled();
@@ -64,22 +64,25 @@ describe('ImageGallery', () => {
64
64
  });
65
65
 
66
66
  it('handle handleLoad function when video item present and payload duration is available', () => {
67
+ const attachment = generateVideoAttachment({ type: 'video' });
68
+ const message = generateMessage({
69
+ attachments: [attachment],
70
+ });
67
71
  const { getByA11yLabel } = render(
68
72
  getComponent({
69
- messages: [
70
- generateMessage({
71
- attachments: [generateVideoAttachment({ type: 'video' })],
72
- }),
73
- ] as unknown as MessageType<DefaultStreamChatGenerics>[],
73
+ messages: [message] as unknown as MessageType<DefaultStreamChatGenerics>[],
74
74
  }),
75
75
  );
76
76
 
77
77
  const videoItemComponent = getByA11yLabel('Image Gallery Video');
78
78
 
79
79
  act(() => {
80
- fireEvent(videoItemComponent, 'handleLoad', {
81
- duration: 10,
82
- });
80
+ fireEvent(
81
+ videoItemComponent,
82
+ 'handleLoad',
83
+ `photoId-${message.id}-${attachment.asset_url}`,
84
+ 10,
85
+ );
83
86
  });
84
87
 
85
88
  const videoDurationComponent = getByA11yLabel('Video Duration');
@@ -110,26 +113,32 @@ describe('ImageGallery', () => {
110
113
  });
111
114
 
112
115
  it('handle handleProgress function when video item present and payload is well defined', () => {
116
+ const attachment = generateVideoAttachment({ type: 'video' });
117
+ const message = generateMessage({
118
+ attachments: [attachment],
119
+ });
120
+
113
121
  const { getByA11yLabel } = render(
114
122
  getComponent({
115
- messages: [
116
- generateMessage({
117
- attachments: [generateVideoAttachment({ type: 'video' })],
118
- }),
119
- ] as unknown as MessageType<DefaultStreamChatGenerics>[],
123
+ messages: [message] as unknown as MessageType<DefaultStreamChatGenerics>[],
120
124
  }),
121
125
  );
122
126
 
123
127
  const videoItemComponent = getByA11yLabel('Image Gallery Video');
124
128
 
125
129
  act(() => {
126
- fireEvent(videoItemComponent, 'handleLoad', {
127
- duration: 10,
128
- });
129
- fireEvent(videoItemComponent, 'handleProgress', {
130
- currentTime: 3,
131
- seekableDuration: 10,
132
- });
130
+ fireEvent(
131
+ videoItemComponent,
132
+ 'handleLoad',
133
+ `photoId-${message.id}-${attachment.asset_url}`,
134
+ 10,
135
+ );
136
+ fireEvent(
137
+ videoItemComponent,
138
+ 'handleProgress',
139
+ `photoId-${message.id}-${attachment.asset_url}`,
140
+ 0.3,
141
+ );
133
142
  });
134
143
 
135
144
  const progressDurationComponent = getByA11yLabel('Progress Duration');
@@ -166,22 +175,25 @@ describe('ImageGallery', () => {
166
175
  });
167
176
 
168
177
  it('handle handleEnd function when video item present', () => {
178
+ const attachment = generateVideoAttachment({ type: 'video' });
179
+ const message = generateMessage({
180
+ attachments: [attachment],
181
+ });
169
182
  const { getByA11yLabel } = render(
170
183
  getComponent({
171
- messages: [
172
- generateMessage({
173
- attachments: [generateVideoAttachment({ type: 'video' })],
174
- }),
175
- ] as unknown as MessageType<DefaultStreamChatGenerics>[],
184
+ messages: [message] as unknown as MessageType<DefaultStreamChatGenerics>[],
176
185
  }),
177
186
  );
178
187
 
179
188
  const videoItemComponent = getByA11yLabel('Image Gallery Video');
180
189
 
181
190
  act(() => {
182
- fireEvent(videoItemComponent, 'handleLoad', {
183
- duration: 10,
184
- });
191
+ fireEvent(
192
+ videoItemComponent,
193
+ 'handleLoad',
194
+ `photoId-${message.id}-${attachment.asset_url}`,
195
+ 10,
196
+ );
185
197
  fireEvent(videoItemComponent, 'handleEnd');
186
198
  });
187
199