stream-chat-react-native-core 4.6.1 → 4.7.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 (200) hide show
  1. package/lib/commonjs/components/Attachment/Attachment.js +22 -13
  2. package/lib/commonjs/components/Attachment/Attachment.js.map +1 -1
  3. package/lib/commonjs/components/Attachment/Gallery.js +72 -37
  4. package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
  5. package/lib/commonjs/components/Attachment/VideoThumbnail.js +83 -0
  6. package/lib/commonjs/components/Attachment/VideoThumbnail.js.map +1 -0
  7. package/lib/commonjs/components/Attachment/hooks/useGoToURL.js +1 -1
  8. package/lib/commonjs/components/Attachment/hooks/useGoToURL.js.map +1 -1
  9. package/lib/commonjs/components/Attachment/utils/buildGallery/buildThumbnail.js +1 -0
  10. package/lib/commonjs/components/Attachment/utils/buildGallery/buildThumbnail.js.map +1 -1
  11. package/lib/commonjs/components/Attachment/utils/getAspectRatio.js +5 -5
  12. package/lib/commonjs/components/Attachment/utils/getAspectRatio.js.map +1 -1
  13. package/lib/commonjs/components/Channel/Channel.js +19 -14
  14. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  15. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js +4 -2
  16. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  17. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +43 -28
  18. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
  19. package/lib/commonjs/components/ImageGallery/ImageGallery.js +167 -345
  20. package/lib/commonjs/components/ImageGallery/ImageGallery.js.map +1 -1
  21. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryImage.js +4 -4
  22. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
  23. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryVideo.js +225 -0
  24. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryVideo.js.map +1 -0
  25. package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js +102 -40
  26. package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
  27. package/lib/commonjs/components/ImageGallery/components/ImageGalleryVideoControl.js +145 -0
  28. package/lib/commonjs/components/ImageGallery/components/ImageGalleryVideoControl.js.map +1 -0
  29. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js +31 -34
  30. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js.map +1 -1
  31. package/lib/commonjs/components/ImageGallery/components/ProgressControl.js +153 -0
  32. package/lib/commonjs/components/ImageGallery/components/ProgressControl.js.map +1 -0
  33. package/lib/commonjs/components/ImageGallery/hooks/useImageGalleryGestures.js +373 -0
  34. package/lib/commonjs/components/ImageGallery/hooks/useImageGalleryGestures.js.map +1 -0
  35. package/lib/commonjs/components/Message/Message.js +23 -13
  36. package/lib/commonjs/components/Message/Message.js.map +1 -1
  37. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js +4 -2
  38. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  39. package/lib/commonjs/components/MessageOverlay/MessageOverlay.js +25 -23
  40. package/lib/commonjs/components/MessageOverlay/MessageOverlay.js.map +1 -1
  41. package/lib/commonjs/components/Reply/Reply.js +32 -12
  42. package/lib/commonjs/components/Reply/Reply.js.map +1 -1
  43. package/lib/commonjs/components/Spinner/Spinner.js +12 -4
  44. package/lib/commonjs/components/Spinner/Spinner.js.map +1 -1
  45. package/lib/commonjs/components/index.js +26 -0
  46. package/lib/commonjs/components/index.js.map +1 -1
  47. package/lib/commonjs/contexts/messageContext/MessageContext.js +2 -2
  48. package/lib/commonjs/contexts/messageContext/MessageContext.js.map +1 -1
  49. package/lib/commonjs/contexts/messageOverlayContext/MessageOverlayContext.js +2 -2
  50. package/lib/commonjs/contexts/messageOverlayContext/MessageOverlayContext.js.map +1 -1
  51. package/lib/commonjs/contexts/messagesContext/MessagesContext.js +2 -2
  52. package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
  53. package/lib/commonjs/contexts/themeContext/utils/theme.js +10 -1
  54. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  55. package/lib/commonjs/icons/Pause.js +61 -0
  56. package/lib/commonjs/icons/Pause.js.map +1 -0
  57. package/lib/commonjs/icons/Play.js +42 -0
  58. package/lib/commonjs/icons/Play.js.map +1 -0
  59. package/lib/commonjs/icons/index.js +26 -0
  60. package/lib/commonjs/icons/index.js.map +1 -1
  61. package/lib/commonjs/mock-builders/api/queryMembers.js +98 -1
  62. package/lib/commonjs/mock-builders/api/queryMembers.js.map +1 -1
  63. package/lib/commonjs/native.js +7 -1
  64. package/lib/commonjs/native.js.map +1 -1
  65. package/lib/commonjs/utils/getUrlOfImageAttachment.js +1 -1
  66. package/lib/commonjs/utils/getUrlOfImageAttachment.js.map +1 -1
  67. package/lib/commonjs/version.json +1 -1
  68. package/lib/module/components/Attachment/Attachment.js +22 -13
  69. package/lib/module/components/Attachment/Attachment.js.map +1 -1
  70. package/lib/module/components/Attachment/Gallery.js +72 -37
  71. package/lib/module/components/Attachment/Gallery.js.map +1 -1
  72. package/lib/module/components/Attachment/VideoThumbnail.js +83 -0
  73. package/lib/module/components/Attachment/VideoThumbnail.js.map +1 -0
  74. package/lib/module/components/Attachment/hooks/useGoToURL.js +1 -1
  75. package/lib/module/components/Attachment/hooks/useGoToURL.js.map +1 -1
  76. package/lib/module/components/Attachment/utils/buildGallery/buildThumbnail.js +1 -0
  77. package/lib/module/components/Attachment/utils/buildGallery/buildThumbnail.js.map +1 -1
  78. package/lib/module/components/Attachment/utils/getAspectRatio.js +5 -5
  79. package/lib/module/components/Attachment/utils/getAspectRatio.js.map +1 -1
  80. package/lib/module/components/Channel/Channel.js +19 -14
  81. package/lib/module/components/Channel/Channel.js.map +1 -1
  82. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js +4 -2
  83. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  84. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +43 -28
  85. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
  86. package/lib/module/components/ImageGallery/ImageGallery.js +167 -345
  87. package/lib/module/components/ImageGallery/ImageGallery.js.map +1 -1
  88. package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js +4 -4
  89. package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
  90. package/lib/module/components/ImageGallery/components/AnimatedGalleryVideo.js +225 -0
  91. package/lib/module/components/ImageGallery/components/AnimatedGalleryVideo.js.map +1 -0
  92. package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js +102 -40
  93. package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
  94. package/lib/module/components/ImageGallery/components/ImageGalleryVideoControl.js +145 -0
  95. package/lib/module/components/ImageGallery/components/ImageGalleryVideoControl.js.map +1 -0
  96. package/lib/module/components/ImageGallery/components/ImageGrid.js +31 -34
  97. package/lib/module/components/ImageGallery/components/ImageGrid.js.map +1 -1
  98. package/lib/module/components/ImageGallery/components/ProgressControl.js +153 -0
  99. package/lib/module/components/ImageGallery/components/ProgressControl.js.map +1 -0
  100. package/lib/module/components/ImageGallery/hooks/useImageGalleryGestures.js +373 -0
  101. package/lib/module/components/ImageGallery/hooks/useImageGalleryGestures.js.map +1 -0
  102. package/lib/module/components/Message/Message.js +23 -13
  103. package/lib/module/components/Message/Message.js.map +1 -1
  104. package/lib/module/components/Message/hooks/useCreateMessageContext.js +4 -2
  105. package/lib/module/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  106. package/lib/module/components/MessageOverlay/MessageOverlay.js +25 -23
  107. package/lib/module/components/MessageOverlay/MessageOverlay.js.map +1 -1
  108. package/lib/module/components/Reply/Reply.js +32 -12
  109. package/lib/module/components/Reply/Reply.js.map +1 -1
  110. package/lib/module/components/Spinner/Spinner.js +12 -4
  111. package/lib/module/components/Spinner/Spinner.js.map +1 -1
  112. package/lib/module/components/index.js +26 -0
  113. package/lib/module/components/index.js.map +1 -1
  114. package/lib/module/contexts/messageContext/MessageContext.js +2 -2
  115. package/lib/module/contexts/messageContext/MessageContext.js.map +1 -1
  116. package/lib/module/contexts/messageOverlayContext/MessageOverlayContext.js +2 -2
  117. package/lib/module/contexts/messageOverlayContext/MessageOverlayContext.js.map +1 -1
  118. package/lib/module/contexts/messagesContext/MessagesContext.js +2 -2
  119. package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
  120. package/lib/module/contexts/themeContext/utils/theme.js +10 -1
  121. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  122. package/lib/module/icons/Pause.js +61 -0
  123. package/lib/module/icons/Pause.js.map +1 -0
  124. package/lib/module/icons/Play.js +42 -0
  125. package/lib/module/icons/Play.js.map +1 -0
  126. package/lib/module/icons/index.js +26 -0
  127. package/lib/module/icons/index.js.map +1 -1
  128. package/lib/module/mock-builders/api/queryMembers.js +98 -1
  129. package/lib/module/mock-builders/api/queryMembers.js.map +1 -1
  130. package/lib/module/native.js +7 -1
  131. package/lib/module/native.js.map +1 -1
  132. package/lib/module/utils/getUrlOfImageAttachment.js +1 -1
  133. package/lib/module/utils/getUrlOfImageAttachment.js.map +1 -1
  134. package/lib/module/version.json +1 -1
  135. package/lib/typescript/components/Attachment/Gallery.d.ts +1 -1
  136. package/lib/typescript/components/Attachment/VideoThumbnail.d.ts +4 -0
  137. package/lib/typescript/components/Attachment/utils/buildGallery/types.d.ts +1 -0
  138. package/lib/typescript/components/Attachment/utils/getAspectRatio.d.ts +1 -1
  139. package/lib/typescript/components/Channel/Channel.d.ts +1 -1
  140. package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts +1 -1
  141. package/lib/typescript/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayName.test.d.ts +1 -0
  142. package/lib/typescript/components/ChannelPreview/hooks/useChannelPreviewDisplayName.d.ts +4 -4
  143. package/lib/typescript/components/ImageGallery/ImageGallery.d.ts +2 -0
  144. package/lib/typescript/components/ImageGallery/components/AnimatedGalleryVideo.d.ts +27 -0
  145. package/lib/typescript/components/ImageGallery/components/ImageGalleryFooter.d.ts +18 -2
  146. package/lib/typescript/components/ImageGallery/components/ImageGalleryVideoControl.d.ts +3 -0
  147. package/lib/typescript/components/ImageGallery/components/ProgressControl.d.ts +5 -0
  148. package/lib/typescript/components/ImageGallery/hooks/useImageGalleryGestures.d.ts +36 -0
  149. package/lib/typescript/components/Message/hooks/useCreateMessageContext.d.ts +1 -1
  150. package/lib/typescript/components/Spinner/Spinner.d.ts +7 -1
  151. package/lib/typescript/components/index.d.ts +2 -0
  152. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +3 -1
  153. package/lib/typescript/contexts/messageOverlayContext/MessageOverlayContext.d.ts +1 -0
  154. package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +2 -0
  155. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +9 -0
  156. package/lib/typescript/icons/Pause.d.ts +3 -0
  157. package/lib/typescript/icons/Play.d.ts +3 -0
  158. package/lib/typescript/icons/index.d.ts +2 -0
  159. package/lib/typescript/mock-builders/api/queryMembers.d.ts +161 -0
  160. package/lib/typescript/mock-builders/api/utils.d.ts +7 -0
  161. package/lib/typescript/mock-builders/generator/user.d.ts +30 -0
  162. package/lib/typescript/mock-builders/mock.d.ts +3 -0
  163. package/lib/typescript/native.d.ts +66 -1
  164. package/package.json +1 -1
  165. package/src/components/Attachment/Attachment.tsx +11 -7
  166. package/src/components/Attachment/Gallery.tsx +75 -36
  167. package/src/components/Attachment/VideoThumbnail.tsx +45 -0
  168. package/src/components/Attachment/hooks/useGoToURL.ts +1 -1
  169. package/src/components/Attachment/utils/buildGallery/buildThumbnail.ts +1 -0
  170. package/src/components/Attachment/utils/buildGallery/types.ts +1 -0
  171. package/src/components/Attachment/utils/getAspectRatio.ts +7 -5
  172. package/src/components/Channel/Channel.tsx +4 -0
  173. package/src/components/Channel/hooks/useCreateMessagesContext.ts +2 -0
  174. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayName.test.tsx +83 -0
  175. package/src/components/ChannelPreview/hooks/useChannelPreviewDisplayName.ts +43 -26
  176. package/src/components/ImageGallery/ImageGallery.tsx +145 -667
  177. package/src/components/ImageGallery/components/AnimatedGalleryImage.tsx +4 -4
  178. package/src/components/ImageGallery/components/AnimatedGalleryVideo.tsx +224 -0
  179. package/src/components/ImageGallery/components/ImageGalleryFooter.tsx +114 -19
  180. package/src/components/ImageGallery/components/ImageGalleryVideoControl.tsx +103 -0
  181. package/src/components/ImageGallery/components/ImageGrid.tsx +12 -25
  182. package/src/components/ImageGallery/components/ProgressControl.tsx +107 -0
  183. package/src/components/ImageGallery/hooks/useImageGalleryGestures.tsx +722 -0
  184. package/src/components/Message/Message.tsx +10 -3
  185. package/src/components/Message/hooks/useCreateMessageContext.ts +2 -0
  186. package/src/components/MessageOverlay/MessageOverlay.tsx +2 -0
  187. package/src/components/Reply/Reply.tsx +36 -23
  188. package/src/components/Spinner/Spinner.tsx +11 -4
  189. package/src/components/index.ts +2 -0
  190. package/src/contexts/messageContext/MessageContext.tsx +2 -0
  191. package/src/contexts/messageOverlayContext/MessageOverlayContext.tsx +1 -0
  192. package/src/contexts/messagesContext/MessagesContext.tsx +2 -0
  193. package/src/contexts/themeContext/utils/theme.ts +18 -0
  194. package/src/icons/Pause.tsx +14 -0
  195. package/src/icons/Play.tsx +17 -0
  196. package/src/icons/index.ts +2 -0
  197. package/src/mock-builders/api/queryMembers.js +98 -0
  198. package/src/native.ts +59 -1
  199. package/src/utils/getUrlOfImageAttachment.ts +1 -1
  200. package/src/version.json +1 -1
@@ -7,7 +7,7 @@ import { vw } from '../../../utils/utils';
7
7
 
8
8
  const screenWidth = vw(100);
9
9
  const halfScreenWidth = vw(50);
10
- const oneEight = 1 / 8;
10
+ const oneEighth = 1 / 8;
11
11
 
12
12
  type Props = {
13
13
  index: number;
@@ -65,7 +65,7 @@ export const AnimatedGalleryImage: React.FC<Props> = React.memo(
65
65
  translateY: selected ? translateY.value + yScaleOffset : yScaleOffset,
66
66
  },
67
67
  {
68
- scale: selected ? scale.value / 8 : oneEight,
68
+ scale: selected ? scale.value / 8 : oneEighth,
69
69
  },
70
70
  { scaleX: -1 },
71
71
  ],
@@ -78,7 +78,7 @@ export const AnimatedGalleryImage: React.FC<Props> = React.memo(
78
78
  * load on memory.
79
79
  */
80
80
  if (!shouldRender) {
81
- return <View style={[style, { transform: [{ scale: oneEight }] }]} />;
81
+ return <View style={[style, { transform: [{ scale: oneEighth }] }]} />;
82
82
  }
83
83
 
84
84
  return (
@@ -95,7 +95,7 @@ export const AnimatedGalleryImage: React.FC<Props> = React.memo(
95
95
  {
96
96
  translateX: -translateX.value + 7 * screenWidth * (0.5 + index),
97
97
  },
98
- { scale: oneEight },
98
+ { scale: oneEighth },
99
99
  ],
100
100
  },
101
101
  ]}
@@ -0,0 +1,224 @@
1
+ import React, { useState } from 'react';
2
+ import { StyleSheet, View, ViewStyle } from 'react-native';
3
+ import type { StyleProp } from 'react-native';
4
+ import Animated, { useAnimatedStyle } from 'react-native-reanimated';
5
+
6
+ import {
7
+ PlaybackStatus,
8
+ Video,
9
+ VideoPayloadData,
10
+ VideoProgressData,
11
+ VideoType,
12
+ } from '../../../native';
13
+
14
+ import { vw } from '../../../utils/utils';
15
+ import { Spinner } from '../../Spinner/Spinner';
16
+
17
+ const screenWidth = vw(100);
18
+ const halfScreenWidth = vw(50);
19
+ const oneEighth = 1 / 8;
20
+
21
+ type Props = {
22
+ handleEnd: () => void;
23
+ handleLoad: (payload: VideoPayloadData) => void;
24
+ handleProgress: (data: VideoProgressData) => void;
25
+ index: number;
26
+ offsetScale: Animated.SharedValue<number>;
27
+ paused: boolean;
28
+ previous: boolean;
29
+ scale: Animated.SharedValue<number>;
30
+ screenHeight: number;
31
+ selected: boolean;
32
+ shouldRender: boolean;
33
+ source: { uri: string };
34
+ translateX: Animated.SharedValue<number>;
35
+ translateY: Animated.SharedValue<number>;
36
+ videoRef: React.RefObject<VideoType>;
37
+ style?: StyleProp<ViewStyle>;
38
+ };
39
+
40
+ const styles = StyleSheet.create({
41
+ activityIndicator: {
42
+ alignSelf: 'center',
43
+ },
44
+ videoPlayer: {
45
+ height: '100%',
46
+ width: '100%',
47
+ },
48
+ });
49
+
50
+ export const AnimatedGalleryVideo: React.FC<Props> = React.memo(
51
+ (props) => {
52
+ const [opacity, setOpacity] = useState<number>(1);
53
+ const {
54
+ handleEnd,
55
+ handleLoad,
56
+ handleProgress,
57
+ index,
58
+ offsetScale,
59
+ paused,
60
+ previous,
61
+ scale,
62
+ screenHeight,
63
+ selected,
64
+ shouldRender,
65
+ source,
66
+ style,
67
+ translateX,
68
+ translateY,
69
+ videoRef,
70
+ } = props;
71
+
72
+ const onLoadStart = () => {
73
+ setOpacity(1);
74
+ };
75
+
76
+ const onLoad = (payload: VideoPayloadData) => {
77
+ setOpacity(0);
78
+ handleLoad(payload);
79
+ };
80
+
81
+ const onEnd = () => {
82
+ handleEnd();
83
+ };
84
+
85
+ const onProgress = (data: VideoProgressData) => {
86
+ handleProgress(data);
87
+ };
88
+
89
+ const onBuffer = ({ isBuffering }: { isBuffering: boolean }) => {
90
+ if (isBuffering) setOpacity(1);
91
+ else setOpacity(0);
92
+ };
93
+
94
+ const onPlayBackStatusUpdate = (playbackStatus: PlaybackStatus) => {
95
+ if (!playbackStatus.isLoaded) {
96
+ // Update your UI for the unloaded state
97
+ setOpacity(1);
98
+ if (playbackStatus.error) {
99
+ console.error(`Encountered a fatal error during playback: ${playbackStatus.error}`);
100
+ }
101
+ } else {
102
+ // Update your UI for the loaded state
103
+ setOpacity(0);
104
+ handleLoad({ duration: playbackStatus.durationMillis / 1000 });
105
+ if (playbackStatus.isPlaying) {
106
+ // Update your UI for the playing state
107
+ handleProgress({
108
+ currentTime: playbackStatus.positionMillis / 1000,
109
+ seekableDuration: playbackStatus.durationMillis / 1000,
110
+ });
111
+ }
112
+
113
+ if (playbackStatus.isBuffering) {
114
+ // Update your UI for the buffering state
115
+ setOpacity(1);
116
+ }
117
+
118
+ if (playbackStatus.didJustFinish && !playbackStatus.isLooping) {
119
+ // The player has just finished playing and will stop. Maybe you want to play something else?
120
+ handleEnd();
121
+ }
122
+ }
123
+ };
124
+
125
+ const animatedViewStyles = useAnimatedStyle<ViewStyle>(() => {
126
+ const xScaleOffset = -7 * screenWidth * (0.5 + index);
127
+ const yScaleOffset = -screenHeight * 3.5;
128
+ return {
129
+ transform: [
130
+ {
131
+ translateX: selected
132
+ ? translateX.value + xScaleOffset
133
+ : scale.value < 1 || scale.value !== offsetScale.value
134
+ ? xScaleOffset
135
+ : previous
136
+ ? translateX.value - halfScreenWidth * (scale.value - 1) + xScaleOffset
137
+ : translateX.value + halfScreenWidth * (scale.value - 1) + xScaleOffset,
138
+ },
139
+ {
140
+ translateY: selected ? translateY.value + yScaleOffset : yScaleOffset,
141
+ },
142
+ {
143
+ scale: selected ? scale.value / 8 : oneEighth,
144
+ },
145
+ { scaleX: -1 },
146
+ ],
147
+ };
148
+ }, [previous, selected]);
149
+
150
+ /**
151
+ * An empty view is rendered for images not close to the currently
152
+ * selected in order to maintain spacing while reducing the image
153
+ * load on memory.
154
+ */
155
+ if (!shouldRender) {
156
+ return <View style={[style, { transform: [{ scale: oneEighth }] }]} />;
157
+ }
158
+
159
+ return (
160
+ <Animated.View
161
+ style={[
162
+ style,
163
+ animatedViewStyles,
164
+ {
165
+ transform: [
166
+ { scaleX: -1 },
167
+ { translateY: -screenHeight * 3.5 },
168
+ {
169
+ translateX: -translateX.value + 7 * screenWidth * (0.5 + index),
170
+ },
171
+ { scale: oneEighth },
172
+ ],
173
+ },
174
+ ]}
175
+ >
176
+ <Video
177
+ onBuffer={onBuffer}
178
+ onEnd={onEnd}
179
+ onLoad={onLoad}
180
+ onLoadStart={onLoadStart}
181
+ onPlaybackStatusUpdate={onPlayBackStatusUpdate}
182
+ onProgress={onProgress}
183
+ paused={paused}
184
+ resizeMode='cover'
185
+ style={style}
186
+ uri={source.uri}
187
+ videoRef={videoRef}
188
+ />
189
+ <Animated.View
190
+ style={[
191
+ styles.activityIndicator,
192
+ {
193
+ opacity,
194
+ transform: [
195
+ { scaleX: -1 },
196
+ { translateY: -screenHeight * 4 },
197
+ { scale: 1 / oneEighth },
198
+ ],
199
+ },
200
+ ]}
201
+ >
202
+ <Spinner height={40} width={40} />
203
+ </Animated.View>
204
+ </Animated.View>
205
+ );
206
+ },
207
+
208
+ (prevProps, nextProps) => {
209
+ if (
210
+ prevProps.paused === nextProps.paused &&
211
+ prevProps.shouldRender === nextProps.shouldRender &&
212
+ prevProps.source.uri === nextProps.source.uri &&
213
+ prevProps.screenHeight === nextProps.screenHeight &&
214
+ prevProps.selected === nextProps.selected &&
215
+ prevProps.previous === nextProps.previous &&
216
+ prevProps.index === nextProps.index
217
+ ) {
218
+ return true;
219
+ }
220
+ return false;
221
+ },
222
+ );
223
+
224
+ AnimatedGalleryVideo.displayName = 'AnimatedGalleryVideo';
@@ -2,6 +2,8 @@ import React, { useState } from 'react';
2
2
  import { SafeAreaView, StyleSheet, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
3
3
  import Animated, { Extrapolate, interpolate, useAnimatedStyle } from 'react-native-reanimated';
4
4
 
5
+ import { ImageGalleryVideoControl } from './ImageGalleryVideoControl';
6
+
5
7
  import { useTheme } from '../../../contexts/themeContext/ThemeContext';
6
8
  import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
7
9
  import { Grid as GridIconDefault, Share as ShareIconDefault } from '../../../icons';
@@ -60,6 +62,22 @@ export type ImageGalleryFooterCustomComponent<
60
62
  photo?: Photo<StreamChatGenerics>;
61
63
  }) => React.ReactElement | null;
62
64
 
65
+ export type ImageGalleryFooterVideoControlProps = {
66
+ duration: number;
67
+ onPlayPause: () => void;
68
+ onProgressDrag: (progress: number) => void;
69
+ paused: boolean;
70
+ progress: number;
71
+ };
72
+
73
+ export type ImageGalleryFooterVideoControlComponent = ({
74
+ duration,
75
+ onPlayPause,
76
+ onProgressDrag,
77
+ paused,
78
+ progress,
79
+ }: ImageGalleryFooterVideoControlProps) => React.ReactElement | null;
80
+
63
81
  export type ImageGalleryFooterCustomComponentProps<
64
82
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
65
83
  > = {
@@ -68,41 +86,55 @@ export type ImageGalleryFooterCustomComponentProps<
68
86
  leftElement?: ImageGalleryFooterCustomComponent<StreamChatGenerics>;
69
87
  rightElement?: ImageGalleryFooterCustomComponent<StreamChatGenerics>;
70
88
  ShareIcon?: React.ReactElement;
89
+ videoControlElement?: ImageGalleryFooterVideoControlComponent;
71
90
  };
72
91
 
73
- type Props<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> =
74
- ImageGalleryFooterCustomComponentProps<StreamChatGenerics> & {
75
- opacity: Animated.SharedValue<number>;
76
- openGridView: () => void;
77
- photo: Photo<StreamChatGenerics>;
78
- photoLength: number;
79
- selectedIndex: number;
80
- visible: Animated.SharedValue<number>;
81
- };
92
+ type ImageGalleryFooterPropsWithContext<
93
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
94
+ > = ImageGalleryFooterCustomComponentProps<StreamChatGenerics> & {
95
+ duration: number;
96
+ onPlayPause: () => void;
97
+ onProgressDrag: (progress: number) => void;
98
+ opacity: Animated.SharedValue<number>;
99
+ openGridView: () => void;
100
+ paused: boolean;
101
+ photo: Photo<StreamChatGenerics>;
102
+ photoLength: number;
103
+ progress: number;
104
+ selectedIndex: number;
105
+ visible: Animated.SharedValue<number>;
106
+ };
82
107
 
83
- export const ImageGalleryFooter = <
108
+ export const ImageGalleryFooterWithContext = <
84
109
  StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
85
110
  >(
86
- props: Props<StreamChatGenerics>,
111
+ props: ImageGalleryFooterPropsWithContext<StreamChatGenerics>,
87
112
  ) => {
88
113
  const {
89
114
  centerElement,
115
+ duration,
90
116
  GridIcon,
91
117
  leftElement,
118
+ onPlayPause,
119
+ onProgressDrag,
92
120
  opacity,
93
121
  openGridView,
122
+ paused,
94
123
  photo,
95
124
  photoLength,
125
+ progress,
96
126
  rightElement,
97
127
  selectedIndex,
98
128
  ShareIcon,
129
+ videoControlElement,
99
130
  visible,
100
131
  } = props;
132
+
101
133
  const [height, setHeight] = useState(200);
102
134
  const [shareMenuOpen, setShareMenuOpen] = useState(false);
103
135
  const {
104
136
  theme: {
105
- colors: { black, white },
137
+ colors: { black },
106
138
  imageGallery: {
107
139
  footer: {
108
140
  centerContainer,
@@ -126,18 +158,21 @@ export const ImageGalleryFooter = <
126
158
  },
127
159
  ],
128
160
  }),
129
- [],
161
+ [height],
130
162
  );
131
163
 
132
164
  const share = async () => {
133
165
  setShareMenuOpen(true);
134
166
  try {
135
- const localImage = await saveFile({
136
- fileName: `${photo.user?.id || 'ChatPhoto'}-${photo.messageId}-${selectedIndex}.jpg`,
167
+ const extension = photo.mime_type?.split('/')[1] || 'jpg';
168
+ const localFile = await saveFile({
169
+ fileName: `${photo.user?.id || 'ChatPhoto'}-${
170
+ photo.messageId
171
+ }-${selectedIndex}.${extension}`,
137
172
  fromUrl: photo.uri,
138
173
  });
139
- await shareImage({ type: 'image/jpeg', url: localImage });
140
- await deleteFile({ uri: localImage });
174
+ await shareImage({ type: photo.mime_type, url: localFile });
175
+ await deleteFile({ uri: localFile });
141
176
  } catch (error) {
142
177
  console.log(error);
143
178
  }
@@ -150,8 +185,21 @@ export const ImageGalleryFooter = <
150
185
  pointerEvents={'box-none'}
151
186
  style={styles.wrapper}
152
187
  >
153
- <ReanimatedSafeAreaView style={[{ backgroundColor: white }, container, footerStyle]}>
154
- <View style={[styles.innerContainer, innerContainer]}>
188
+ <ReanimatedSafeAreaView style={[container, footerStyle]}>
189
+ {photo.type === 'video' ? (
190
+ videoControlElement ? (
191
+ videoControlElement({ duration, onPlayPause, onProgressDrag, paused, progress })
192
+ ) : (
193
+ <ImageGalleryVideoControl
194
+ duration={duration}
195
+ onPlayPause={onPlayPause}
196
+ onProgressDrag={onProgressDrag}
197
+ paused={paused}
198
+ progress={progress}
199
+ />
200
+ )
201
+ ) : null}
202
+ <View style={[styles.innerContainer, innerContainer, { backgroundColor: 'white' }]}>
155
203
  {leftElement ? (
156
204
  leftElement({ openGridView, photo, share, shareMenuOpen })
157
205
  ) : (
@@ -188,4 +236,51 @@ export const ImageGalleryFooter = <
188
236
  );
189
237
  };
190
238
 
239
+ const areEqual = <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(
240
+ prevProps: ImageGalleryFooterPropsWithContext<StreamChatGenerics>,
241
+ nextProps: ImageGalleryFooterPropsWithContext<StreamChatGenerics>,
242
+ ) => {
243
+ const {
244
+ duration: prevDuration,
245
+ paused: prevPaused,
246
+ progress: prevProgress,
247
+ selectedIndex: prevSelectedIndex,
248
+ } = prevProps;
249
+ const {
250
+ duration: nextDuration,
251
+ paused: nextPaused,
252
+ progress: nextProgress,
253
+ selectedIndex: nextSelectedIndex,
254
+ } = nextProps;
255
+
256
+ const isDurationEqual = prevDuration === nextDuration;
257
+ if (!isDurationEqual) return false;
258
+
259
+ const isPausedEqual = prevPaused === nextPaused;
260
+ if (!isPausedEqual) return false;
261
+
262
+ const isProgressEqual = prevProgress === nextProgress;
263
+ if (!isProgressEqual) return false;
264
+
265
+ const isSelectedIndexEqual = prevSelectedIndex === nextSelectedIndex;
266
+ if (!isSelectedIndexEqual) return false;
267
+
268
+ return true;
269
+ };
270
+
271
+ const MemoizedImageGalleryFooter = React.memo(
272
+ ImageGalleryFooterWithContext,
273
+ areEqual,
274
+ ) as typeof ImageGalleryFooterWithContext;
275
+
276
+ export type ImageGalleryFooterProps<
277
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
278
+ > = ImageGalleryFooterPropsWithContext<StreamChatGenerics>;
279
+
280
+ export const ImageGalleryFooter = <
281
+ StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
282
+ >(
283
+ props: ImageGalleryFooterProps<StreamChatGenerics>,
284
+ ) => <MemoizedImageGalleryFooter {...props} />;
285
+
191
286
  ImageGalleryFooter.displayName = 'ImageGalleryFooter{imageGallery{footer}}';
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3
+
4
+ import dayjs from 'dayjs';
5
+
6
+ import type { ImageGalleryFooterVideoControlProps } from './ImageGalleryFooter';
7
+ import { ProgressControl } from './ProgressControl';
8
+
9
+ import { useTheme } from '../../../contexts/themeContext/ThemeContext';
10
+
11
+ import { Pause, Play } from '../../../icons';
12
+
13
+ const styles = StyleSheet.create({
14
+ durationTextStyle: {
15
+ color: '#fff',
16
+ fontWeight: 'bold',
17
+ },
18
+ roundedView: {
19
+ alignItems: 'center',
20
+ backgroundColor: '#fff',
21
+ borderRadius: 50,
22
+ display: 'flex',
23
+ elevation: 2,
24
+ height: 36,
25
+ justifyContent: 'center',
26
+ width: 36,
27
+ },
28
+ videoContainer: {
29
+ alignItems: 'center',
30
+ backgroundColor: 'rgba(52, 52, 52, 0.1)',
31
+ display: 'flex',
32
+ flexDirection: 'row',
33
+ justifyContent: 'space-between',
34
+ padding: 10,
35
+ },
36
+ });
37
+
38
+ export const ImageGalleryVideoControl: React.FC<ImageGalleryFooterVideoControlProps> = React.memo(
39
+ (props) => {
40
+ const { duration, onPlayPause, onProgressDrag, paused, progress } = props;
41
+
42
+ const videoDuration = duration
43
+ ? duration / 3600 >= 1
44
+ ? dayjs.duration(duration, 'second').format('HH:mm:ss')
45
+ : dayjs.duration(duration, 'second').format('mm:ss')
46
+ : null;
47
+
48
+ const progressValueInSeconds = progress * duration;
49
+
50
+ const progressDuration = progressValueInSeconds
51
+ ? progressValueInSeconds / 3600 >= 1
52
+ ? dayjs.duration(progressValueInSeconds, 'second').format('HH:mm:ss')
53
+ : dayjs.duration(progressValueInSeconds, 'second').format('mm:ss')
54
+ : null;
55
+
56
+ const {
57
+ theme: {
58
+ imageGallery: {
59
+ videoControl: { durationTextStyle, roundedView, videoContainer },
60
+ },
61
+ },
62
+ } = useTheme();
63
+
64
+ return (
65
+ <View style={[styles.videoContainer, videoContainer]}>
66
+ <TouchableOpacity onPress={onPlayPause}>
67
+ <View style={[styles.roundedView, roundedView]}>
68
+ {paused ? (
69
+ <Play height={24} pathFill={'#000'} width={24} />
70
+ ) : (
71
+ <Pause height={24} width={24} />
72
+ )}
73
+ </View>
74
+ </TouchableOpacity>
75
+ <Text style={[styles.durationTextStyle, durationTextStyle]}>
76
+ {progressDuration ? progressDuration : '00:00'}
77
+ </Text>
78
+ <ProgressControl
79
+ duration={duration}
80
+ onPlayPause={onPlayPause}
81
+ onProgressDrag={onProgressDrag}
82
+ progress={progress}
83
+ />
84
+ <Text style={[styles.durationTextStyle, durationTextStyle]}>
85
+ {videoDuration ? videoDuration : '00:00'}
86
+ </Text>
87
+ </View>
88
+ );
89
+ },
90
+ (prevProps, nextProps) => {
91
+ if (
92
+ prevProps.duration === nextProps.duration &&
93
+ prevProps.paused === nextProps.paused &&
94
+ prevProps.progress === nextProps.progress
95
+ ) {
96
+ return true;
97
+ } else {
98
+ return false;
99
+ }
100
+ },
101
+ );
102
+
103
+ ImageGalleryVideoControl.displayName = 'ImageGalleryVideoControl{imageGallery{videoControl}}';
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
- import { ImageBackground, StyleSheet } from 'react-native';
2
+ import { Image, StyleSheet, View } from 'react-native';
3
3
 
4
4
  import { BottomSheetFlatList, TouchableOpacity } from '@gorhom/bottom-sheet';
5
5
 
6
+ import { VideoThumbnail } from '../../../components/Attachment/VideoThumbnail';
6
7
  import { useTheme } from '../../../contexts/themeContext/ThemeContext';
7
8
  import type { DefaultStreamChatGenerics } from '../../../types/types';
8
9
  import { vw } from '../../../utils/utils';
9
- import { Avatar } from '../../Avatar/Avatar';
10
10
 
11
11
  import type { Photo } from '../ImageGallery';
12
12
 
@@ -66,15 +66,14 @@ const GridImage = <
66
66
  }) => {
67
67
  const {
68
68
  theme: {
69
- colors: { white },
70
69
  imageGallery: {
71
- grid: { gridAvatar, gridAvatarWrapper, gridImage },
70
+ grid: { gridImage },
72
71
  },
73
72
  },
74
73
  } = useTheme();
75
- const { avatarComponent, imageComponent, ...restItem } = item;
74
+ const { imageComponent, ...restItem } = item;
76
75
 
77
- const { numberOfImageGalleryGridColumns, selectAndClose, uri, user } = restItem;
76
+ const { numberOfImageGalleryGridColumns, selectAndClose, type, uri } = restItem;
78
77
 
79
78
  const size = vw(100) / (numberOfImageGalleryGridColumns || 3) - 2;
80
79
 
@@ -84,25 +83,13 @@ const GridImage = <
84
83
 
85
84
  return (
86
85
  <TouchableOpacity onPress={selectAndClose}>
87
- <ImageBackground
88
- source={{ uri }}
89
- style={[styles.image, { height: size, width: size }, gridImage]}
90
- >
91
- {avatarComponent
92
- ? avatarComponent({ item: restItem })
93
- : !!user?.image && (
94
- <Avatar
95
- containerStyle={[
96
- styles.avatarImageWrapper,
97
- { backgroundColor: white },
98
- gridAvatarWrapper,
99
- ]}
100
- image={user.image}
101
- imageStyle={gridAvatar}
102
- size={22}
103
- />
104
- )}
105
- </ImageBackground>
86
+ {type === 'video' ? (
87
+ <View style={[styles.image, { height: size, width: size }, gridImage]}>
88
+ <VideoThumbnail />
89
+ </View>
90
+ ) : (
91
+ <Image source={{ uri }} style={[styles.image, { height: size, width: size }]} />
92
+ )}
106
93
  </TouchableOpacity>
107
94
  );
108
95
  };