stream-chat-react-native-core 4.9.0-beta.4 → 4.10.0
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.
- package/lib/commonjs/components/Attachment/Gallery.js +246 -192
- package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelPreview.js +7 -3
- package/lib/commonjs/components/ChannelPreview/ChannelPreview.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useLatestMessagePreview.js +2 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useLatestMessagePreview.js.map +1 -1
- package/lib/commonjs/components/Chat/Chat.js +9 -5
- package/lib/commonjs/components/Chat/Chat.js.map +1 -1
- package/lib/commonjs/components/ImageGallery/ImageGallery.js +25 -22
- package/lib/commonjs/components/ImageGallery/ImageGallery.js.map +1 -1
- package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js +15 -15
- package/lib/commonjs/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
- package/lib/commonjs/components/Message/Message.js +12 -8
- package/lib/commonjs/components/Message/Message.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/MessageTextContainer.js +7 -4
- package/lib/commonjs/components/Message/MessageSimple/MessageTextContainer.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +16 -10
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/commonjs/contexts/messageOverlayContext/MessageOverlayContext.js +2 -2
- package/lib/commonjs/contexts/messageOverlayContext/MessageOverlayContext.js.map +1 -1
- package/lib/commonjs/contexts/overlayContext/OverlayProvider.js +15 -13
- package/lib/commonjs/contexts/overlayContext/OverlayProvider.js.map +1 -1
- package/lib/commonjs/contexts/translationContext/TranslationContext.js +8 -4
- package/lib/commonjs/contexts/translationContext/TranslationContext.js.map +1 -1
- package/lib/commonjs/hooks/useStreami18n.js.map +1 -1
- package/lib/commonjs/hooks/useTranslatedMessage.js +34 -0
- package/lib/commonjs/hooks/useTranslatedMessage.js.map +1 -0
- package/lib/commonjs/mock-builders/api/getOrCreateChannel.js.map +1 -1
- package/lib/commonjs/mock-builders/generator/channel.js +8 -4
- package/lib/commonjs/mock-builders/generator/channel.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/Gallery.js +246 -192
- package/lib/module/components/Attachment/Gallery.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelPreview.js +7 -3
- package/lib/module/components/ChannelPreview/ChannelPreview.js.map +1 -1
- package/lib/module/components/ChannelPreview/hooks/useLatestMessagePreview.js +2 -1
- package/lib/module/components/ChannelPreview/hooks/useLatestMessagePreview.js.map +1 -1
- package/lib/module/components/Chat/Chat.js +9 -5
- package/lib/module/components/Chat/Chat.js.map +1 -1
- package/lib/module/components/ImageGallery/ImageGallery.js +25 -22
- package/lib/module/components/ImageGallery/ImageGallery.js.map +1 -1
- package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js +15 -15
- package/lib/module/components/ImageGallery/components/ImageGalleryFooter.js.map +1 -1
- package/lib/module/components/Message/Message.js +12 -8
- package/lib/module/components/Message/Message.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/MessageTextContainer.js +7 -4
- package/lib/module/components/Message/MessageSimple/MessageTextContainer.js.map +1 -1
- package/lib/module/contexts/messageInputContext/MessageInputContext.js +16 -10
- package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/module/contexts/messageOverlayContext/MessageOverlayContext.js +2 -2
- package/lib/module/contexts/messageOverlayContext/MessageOverlayContext.js.map +1 -1
- package/lib/module/contexts/overlayContext/OverlayProvider.js +15 -13
- package/lib/module/contexts/overlayContext/OverlayProvider.js.map +1 -1
- package/lib/module/contexts/translationContext/TranslationContext.js +8 -4
- package/lib/module/contexts/translationContext/TranslationContext.js.map +1 -1
- package/lib/module/hooks/useStreami18n.js.map +1 -1
- package/lib/module/hooks/useTranslatedMessage.js +34 -0
- package/lib/module/hooks/useTranslatedMessage.js.map +1 -0
- package/lib/module/mock-builders/api/getOrCreateChannel.js.map +1 -1
- package/lib/module/mock-builders/generator/channel.js +8 -4
- package/lib/module/mock-builders/generator/channel.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/ChannelPreview/__tests__/ChannelPreview.test.d.ts +1 -0
- package/lib/typescript/components/Message/MessageSimple/__tests__/MessageTextContainer.test.d.ts +1 -0
- package/lib/typescript/contexts/messageOverlayContext/MessageOverlayContext.d.ts +2 -1
- package/lib/typescript/contexts/translationContext/TranslationContext.d.ts +7 -2
- package/lib/typescript/hooks/__tests__/useTranslatedMessage.test.d.ts +1 -0
- package/lib/typescript/hooks/useStreami18n.d.ts +2 -2
- package/lib/typescript/hooks/useTranslatedMessage.d.ts +3 -0
- package/lib/typescript/mock-builders/api/getOrCreateChannel.d.ts +13 -1
- package/lib/typescript/mock-builders/event/messageNew.d.ts +2 -0
- package/lib/typescript/mock-builders/event/messageRead.d.ts +8 -0
- package/lib/typescript/mock-builders/generator/channel.d.ts +19 -15
- package/package.json +3 -3
- package/src/components/Attachment/Gallery.tsx +231 -141
- package/src/components/ChannelPreview/ChannelPreview.tsx +7 -1
- package/src/components/ChannelPreview/__tests__/{ChannelPreview.test.js → ChannelPreview.test.tsx} +79 -41
- package/src/components/ChannelPreview/hooks/__tests__/useLatestMessagePreview.test.tsx +56 -21
- package/src/components/ChannelPreview/hooks/useLatestMessagePreview.ts +4 -1
- package/src/components/Chat/Chat.tsx +6 -3
- package/src/components/ImageGallery/ImageGallery.tsx +10 -6
- package/src/components/ImageGallery/components/ImageGalleryFooter.tsx +2 -1
- package/src/components/Message/Message.tsx +3 -0
- package/src/components/Message/MessageSimple/MessageTextContainer.tsx +7 -1
- package/src/components/Message/MessageSimple/__tests__/MessageTextContainer.test.tsx +108 -0
- package/src/components/Message/MessageSimple/__tests__/__snapshots__/{MessageTextContainer.test.js.snap → MessageTextContainer.test.tsx.snap} +0 -0
- package/src/contexts/messageInputContext/MessageInputContext.tsx +7 -0
- package/src/contexts/messageInputContext/__tests__/__snapshots__/sendMessage.test.tsx.snap +4 -0
- package/src/contexts/messageOverlayContext/MessageOverlayContext.tsx +2 -1
- package/src/contexts/overlayContext/OverlayProvider.tsx +4 -3
- package/src/contexts/translationContext/TranslationContext.tsx +10 -1
- package/src/hooks/__tests__/useTranslatedMessage.test.tsx +106 -0
- package/src/hooks/useStreami18n.ts +2 -2
- package/src/hooks/useTranslatedMessage.ts +30 -0
- package/src/mock-builders/api/{getOrCreateChannel.js → getOrCreateChannel.ts} +8 -1
- package/src/mock-builders/generator/{channel.js → channel.ts} +19 -10
- package/src/version.json +1 -1
- package/src/components/Message/MessageSimple/__tests__/MessageTextContainer.test.js +0 -65
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|
3
3
|
|
|
4
|
+
import type { Attachment } from 'stream-chat';
|
|
5
|
+
|
|
4
6
|
import { GalleryImage } from './GallaryImage';
|
|
5
7
|
import { useLoadingImage } from './hooks/useLoadingImage';
|
|
6
8
|
import { buildGallery } from './utils/buildGallery/buildGallery';
|
|
7
9
|
|
|
10
|
+
import type { Thumbnail } from './utils/buildGallery/types';
|
|
8
11
|
import { getGalleryImageBorderRadius } from './utils/getGalleryImageBorderRadius';
|
|
9
12
|
|
|
10
13
|
import { openUrlSafely } from './utils/openUrlSafely';
|
|
@@ -135,26 +138,18 @@ const GalleryWithContext = <
|
|
|
135
138
|
VideoThumbnail,
|
|
136
139
|
} = props;
|
|
137
140
|
|
|
138
|
-
const { isLoadingImage, isLoadingImageError, setLoadingImage, setLoadingImageError } =
|
|
139
|
-
useLoadingImage();
|
|
140
|
-
|
|
141
141
|
const {
|
|
142
142
|
theme: {
|
|
143
|
-
colors: { overlay },
|
|
144
143
|
messageSimple: {
|
|
145
144
|
gallery: {
|
|
146
145
|
galleryContainer,
|
|
147
146
|
galleryItemColumn,
|
|
148
147
|
gridHeight,
|
|
149
148
|
gridWidth,
|
|
150
|
-
image,
|
|
151
|
-
imageContainer,
|
|
152
149
|
maxHeight,
|
|
153
150
|
maxWidth,
|
|
154
151
|
minHeight,
|
|
155
152
|
minWidth,
|
|
156
|
-
moreImagesContainer,
|
|
157
|
-
moreImagesText,
|
|
158
153
|
},
|
|
159
154
|
},
|
|
160
155
|
},
|
|
@@ -180,8 +175,6 @@ const GalleryWithContext = <
|
|
|
180
175
|
);
|
|
181
176
|
|
|
182
177
|
if (!imagesAndVideos?.length) return null;
|
|
183
|
-
const messageText = message?.text;
|
|
184
|
-
const messageId = message?.id;
|
|
185
178
|
const numOfColumns = thumbnailGrid.length;
|
|
186
179
|
|
|
187
180
|
return (
|
|
@@ -212,31 +205,7 @@ const GalleryWithContext = <
|
|
|
212
205
|
]}
|
|
213
206
|
testID={`gallery-${invertedDirections ? 'row' : 'column'}-${colIndex}`}
|
|
214
207
|
>
|
|
215
|
-
{rows.map((
|
|
216
|
-
const openImageViewer = () => {
|
|
217
|
-
if (!legacyImageViewerSwipeBehaviour && message) {
|
|
218
|
-
// Added if-else to keep the logic readable, instead of DRY.
|
|
219
|
-
// if - legacyImageViewerSwipeBehaviour is disabled
|
|
220
|
-
// else - legacyImageViewerSwipeBehaviour is enabled
|
|
221
|
-
setImages([message]);
|
|
222
|
-
setImage({ messageId: message.id, url });
|
|
223
|
-
setOverlay('gallery');
|
|
224
|
-
} else if (legacyImageViewerSwipeBehaviour) {
|
|
225
|
-
setImage({ messageId: message?.id, url });
|
|
226
|
-
setOverlay('gallery');
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const defaultOnPress = () => {
|
|
231
|
-
if (type === 'video' && !isVideoPackageAvailable()) {
|
|
232
|
-
// This condition is kinda unreachable, since we render videos as file attachment if the video
|
|
233
|
-
// library is not installed. But doesn't hurt to have extra safeguard, in case of some customizations.
|
|
234
|
-
openUrlSafely(url);
|
|
235
|
-
} else {
|
|
236
|
-
openImageViewer();
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
|
|
208
|
+
{rows.map((thumbnail, rowIndex) => {
|
|
240
209
|
const borderRadius = getGalleryImageBorderRadius({
|
|
241
210
|
alignment,
|
|
242
211
|
colIndex,
|
|
@@ -244,7 +213,7 @@ const GalleryWithContext = <
|
|
|
244
213
|
hasThreadReplies,
|
|
245
214
|
height,
|
|
246
215
|
invertedDirections,
|
|
247
|
-
messageText,
|
|
216
|
+
messageText: message?.text,
|
|
248
217
|
numOfColumns,
|
|
249
218
|
numOfRows,
|
|
250
219
|
rowIndex,
|
|
@@ -253,112 +222,35 @@ const GalleryWithContext = <
|
|
|
253
222
|
width,
|
|
254
223
|
});
|
|
255
224
|
|
|
225
|
+
if (message === undefined) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
256
229
|
return (
|
|
257
|
-
<
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
defaultHandler: defaultOnPress,
|
|
282
|
-
emitter: 'gallery',
|
|
283
|
-
event,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
}}
|
|
287
|
-
style={[
|
|
288
|
-
styles.imageContainer,
|
|
289
|
-
{
|
|
290
|
-
height,
|
|
291
|
-
width,
|
|
292
|
-
},
|
|
293
|
-
imageContainer,
|
|
294
|
-
]}
|
|
295
|
-
testID={`gallery-${
|
|
296
|
-
invertedDirections ? 'row' : 'column'
|
|
297
|
-
}-${colIndex}-item-${rowIndex}`}
|
|
298
|
-
{...additionalTouchableProps}
|
|
299
|
-
>
|
|
300
|
-
{type === 'video' ? (
|
|
301
|
-
<VideoThumbnail
|
|
302
|
-
style={[
|
|
303
|
-
borderRadius,
|
|
304
|
-
image,
|
|
305
|
-
{
|
|
306
|
-
height: height - 1,
|
|
307
|
-
width: width - 1,
|
|
308
|
-
},
|
|
309
|
-
]}
|
|
310
|
-
thumb_url={thumb_url}
|
|
311
|
-
/>
|
|
312
|
-
) : (
|
|
313
|
-
<View style={styles.imageContainerStyle}>
|
|
314
|
-
<MemoizedGalleryImage
|
|
315
|
-
onError={(error) => {
|
|
316
|
-
console.warn(error);
|
|
317
|
-
setLoadingImage(false);
|
|
318
|
-
setLoadingImageError(true);
|
|
319
|
-
}}
|
|
320
|
-
onLoadEnd={() => setLoadingImage(false)}
|
|
321
|
-
onLoadStart={() => setLoadingImage(true)}
|
|
322
|
-
resizeMode={resizeMode}
|
|
323
|
-
style={[
|
|
324
|
-
borderRadius,
|
|
325
|
-
image,
|
|
326
|
-
{
|
|
327
|
-
height: height - 1,
|
|
328
|
-
width: width - 1,
|
|
329
|
-
},
|
|
330
|
-
]}
|
|
331
|
-
uri={url}
|
|
332
|
-
/>
|
|
333
|
-
{isLoadingImage && (
|
|
334
|
-
<View style={{ position: 'absolute' }}>
|
|
335
|
-
<ImageLoadingIndicator style={styles.imageLoadingIndicatorStyle} />
|
|
336
|
-
</View>
|
|
337
|
-
)}
|
|
338
|
-
{isLoadingImageError && (
|
|
339
|
-
<View style={{ position: 'absolute' }}>
|
|
340
|
-
<ImageLoadingFailedIndicator style={styles.imageLoadingIndicatorStyle} />
|
|
341
|
-
</View>
|
|
342
|
-
)}
|
|
343
|
-
</View>
|
|
344
|
-
)}
|
|
345
|
-
{colIndex === numOfColumns - 1 &&
|
|
346
|
-
rowIndex === numOfRows - 1 &&
|
|
347
|
-
imagesAndVideos.length > 4 ? (
|
|
348
|
-
<View
|
|
349
|
-
style={[
|
|
350
|
-
StyleSheet.absoluteFillObject,
|
|
351
|
-
styles.moreImagesContainer,
|
|
352
|
-
{ backgroundColor: overlay },
|
|
353
|
-
moreImagesContainer,
|
|
354
|
-
]}
|
|
355
|
-
>
|
|
356
|
-
<Text style={[styles.moreImagesText, moreImagesText]}>
|
|
357
|
-
{`+${imagesAndVideos.length - 4}`}
|
|
358
|
-
</Text>
|
|
359
|
-
</View>
|
|
360
|
-
) : null}
|
|
361
|
-
</TouchableOpacity>
|
|
230
|
+
<GalleryThumbnail
|
|
231
|
+
additionalTouchableProps={additionalTouchableProps}
|
|
232
|
+
borderRadius={borderRadius}
|
|
233
|
+
colIndex={colIndex}
|
|
234
|
+
ImageLoadingFailedIndicator={ImageLoadingFailedIndicator}
|
|
235
|
+
ImageLoadingIndicator={ImageLoadingIndicator}
|
|
236
|
+
imagesAndVideos={imagesAndVideos}
|
|
237
|
+
invertedDirections={invertedDirections || false}
|
|
238
|
+
key={rowIndex}
|
|
239
|
+
legacyImageViewerSwipeBehaviour={legacyImageViewerSwipeBehaviour}
|
|
240
|
+
message={message}
|
|
241
|
+
numOfColumns={numOfColumns}
|
|
242
|
+
numOfRows={numOfRows}
|
|
243
|
+
onLongPress={onLongPress}
|
|
244
|
+
onPress={onPress}
|
|
245
|
+
onPressIn={onPressIn}
|
|
246
|
+
preventPress={preventPress}
|
|
247
|
+
rowIndex={rowIndex}
|
|
248
|
+
setImage={setImage}
|
|
249
|
+
setImages={setImages}
|
|
250
|
+
setOverlay={setOverlay}
|
|
251
|
+
thumbnail={thumbnail}
|
|
252
|
+
VideoThumbnail={VideoThumbnail}
|
|
253
|
+
/>
|
|
362
254
|
);
|
|
363
255
|
})}
|
|
364
256
|
</View>
|
|
@@ -368,6 +260,204 @@ const GalleryWithContext = <
|
|
|
368
260
|
);
|
|
369
261
|
};
|
|
370
262
|
|
|
263
|
+
type GalleryThumbnailProps<
|
|
264
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
265
|
+
> = {
|
|
266
|
+
borderRadius: {
|
|
267
|
+
borderBottomLeftRadius: number;
|
|
268
|
+
borderBottomRightRadius: number;
|
|
269
|
+
borderTopLeftRadius: number;
|
|
270
|
+
borderTopRightRadius: number;
|
|
271
|
+
};
|
|
272
|
+
colIndex: number;
|
|
273
|
+
imagesAndVideos: Attachment<StreamChatGenerics>[];
|
|
274
|
+
invertedDirections: boolean;
|
|
275
|
+
message: MessageType<StreamChatGenerics>;
|
|
276
|
+
numOfColumns: number;
|
|
277
|
+
numOfRows: number;
|
|
278
|
+
rowIndex: number;
|
|
279
|
+
thumbnail: Thumbnail;
|
|
280
|
+
} & Pick<
|
|
281
|
+
MessagesContextValue<StreamChatGenerics>,
|
|
282
|
+
| 'additionalTouchableProps'
|
|
283
|
+
| 'legacyImageViewerSwipeBehaviour'
|
|
284
|
+
| 'VideoThumbnail'
|
|
285
|
+
| 'ImageLoadingIndicator'
|
|
286
|
+
| 'ImageLoadingFailedIndicator'
|
|
287
|
+
> &
|
|
288
|
+
Pick<ImageGalleryContextValue<StreamChatGenerics>, 'setImage' | 'setImages'> &
|
|
289
|
+
Pick<
|
|
290
|
+
MessageContextValue<StreamChatGenerics>,
|
|
291
|
+
'onLongPress' | 'onPress' | 'onPressIn' | 'preventPress'
|
|
292
|
+
> &
|
|
293
|
+
Pick<OverlayContextValue, 'setOverlay'>;
|
|
294
|
+
|
|
295
|
+
const GalleryThumbnail = <
|
|
296
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
297
|
+
>({
|
|
298
|
+
additionalTouchableProps,
|
|
299
|
+
borderRadius,
|
|
300
|
+
colIndex,
|
|
301
|
+
ImageLoadingFailedIndicator,
|
|
302
|
+
ImageLoadingIndicator,
|
|
303
|
+
imagesAndVideos,
|
|
304
|
+
invertedDirections,
|
|
305
|
+
legacyImageViewerSwipeBehaviour,
|
|
306
|
+
message,
|
|
307
|
+
numOfColumns,
|
|
308
|
+
numOfRows,
|
|
309
|
+
onLongPress,
|
|
310
|
+
onPress,
|
|
311
|
+
onPressIn,
|
|
312
|
+
preventPress,
|
|
313
|
+
rowIndex,
|
|
314
|
+
setImage,
|
|
315
|
+
setImages,
|
|
316
|
+
setOverlay,
|
|
317
|
+
thumbnail,
|
|
318
|
+
VideoThumbnail,
|
|
319
|
+
}: GalleryThumbnailProps<StreamChatGenerics>) => {
|
|
320
|
+
const { isLoadingImage, isLoadingImageError, setLoadingImage, setLoadingImageError } =
|
|
321
|
+
useLoadingImage();
|
|
322
|
+
|
|
323
|
+
const {
|
|
324
|
+
theme: {
|
|
325
|
+
colors: { overlay },
|
|
326
|
+
messageSimple: {
|
|
327
|
+
gallery: { image, imageContainer, moreImagesContainer, moreImagesText },
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
} = useTheme();
|
|
331
|
+
|
|
332
|
+
const openImageViewer = () => {
|
|
333
|
+
if (!legacyImageViewerSwipeBehaviour && message) {
|
|
334
|
+
// Added if-else to keep the logic readable, instead of DRY.
|
|
335
|
+
// if - legacyImageViewerSwipeBehaviour is disabled
|
|
336
|
+
// else - legacyImageViewerSwipeBehaviour is enabled
|
|
337
|
+
setImages([message]);
|
|
338
|
+
setImage({ messageId: message.id, url: thumbnail.url });
|
|
339
|
+
setOverlay('gallery');
|
|
340
|
+
} else if (legacyImageViewerSwipeBehaviour) {
|
|
341
|
+
setImage({ messageId: message?.id, url: thumbnail.url });
|
|
342
|
+
setOverlay('gallery');
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const defaultOnPress = () => {
|
|
347
|
+
if (thumbnail.type === 'video' && !isVideoPackageAvailable()) {
|
|
348
|
+
// This condition is kinda unreachable, since we render videos as file attachment if the video
|
|
349
|
+
// library is not installed. But doesn't hurt to have extra safeguard, in case of some customizations.
|
|
350
|
+
openUrlSafely(thumbnail.url);
|
|
351
|
+
} else {
|
|
352
|
+
openImageViewer();
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<TouchableOpacity
|
|
358
|
+
activeOpacity={0.8}
|
|
359
|
+
disabled={preventPress}
|
|
360
|
+
key={`gallery-item-${message.id}/${colIndex}/${rowIndex}/${imagesAndVideos.length}`}
|
|
361
|
+
onLongPress={(event) => {
|
|
362
|
+
if (onLongPress) {
|
|
363
|
+
onLongPress({
|
|
364
|
+
emitter: 'gallery',
|
|
365
|
+
event,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}}
|
|
369
|
+
onPress={(event) => {
|
|
370
|
+
if (onPress) {
|
|
371
|
+
onPress({
|
|
372
|
+
defaultHandler: defaultOnPress,
|
|
373
|
+
emitter: 'gallery',
|
|
374
|
+
event,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}}
|
|
378
|
+
onPressIn={(event) => {
|
|
379
|
+
if (onPressIn) {
|
|
380
|
+
onPressIn({
|
|
381
|
+
defaultHandler: defaultOnPress,
|
|
382
|
+
emitter: 'gallery',
|
|
383
|
+
event,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}}
|
|
387
|
+
style={[
|
|
388
|
+
styles.imageContainer,
|
|
389
|
+
{
|
|
390
|
+
height: thumbnail.height,
|
|
391
|
+
width: thumbnail.width,
|
|
392
|
+
},
|
|
393
|
+
imageContainer,
|
|
394
|
+
]}
|
|
395
|
+
testID={`gallery-${invertedDirections ? 'row' : 'column'}-${colIndex}-item-${rowIndex}`}
|
|
396
|
+
{...additionalTouchableProps}
|
|
397
|
+
>
|
|
398
|
+
{thumbnail.type === 'video' ? (
|
|
399
|
+
<VideoThumbnail
|
|
400
|
+
style={[
|
|
401
|
+
borderRadius,
|
|
402
|
+
image,
|
|
403
|
+
{
|
|
404
|
+
height: thumbnail.height - 1,
|
|
405
|
+
width: thumbnail.width - 1,
|
|
406
|
+
},
|
|
407
|
+
]}
|
|
408
|
+
thumb_url={thumbnail.thumb_url}
|
|
409
|
+
/>
|
|
410
|
+
) : (
|
|
411
|
+
<View style={styles.imageContainerStyle}>
|
|
412
|
+
<MemoizedGalleryImage
|
|
413
|
+
onError={(error) => {
|
|
414
|
+
console.warn(error);
|
|
415
|
+
setLoadingImage(false);
|
|
416
|
+
setLoadingImageError(true);
|
|
417
|
+
}}
|
|
418
|
+
onLoadEnd={() => setLoadingImage(false)}
|
|
419
|
+
onLoadStart={() => setLoadingImage(true)}
|
|
420
|
+
resizeMode={thumbnail.resizeMode}
|
|
421
|
+
style={[
|
|
422
|
+
borderRadius,
|
|
423
|
+
image,
|
|
424
|
+
{
|
|
425
|
+
height: thumbnail.height - 1,
|
|
426
|
+
width: thumbnail.width - 1,
|
|
427
|
+
},
|
|
428
|
+
]}
|
|
429
|
+
uri={thumbnail.url}
|
|
430
|
+
/>
|
|
431
|
+
{isLoadingImage && (
|
|
432
|
+
<View style={{ position: 'absolute' }}>
|
|
433
|
+
<ImageLoadingIndicator style={styles.imageLoadingIndicatorStyle} />
|
|
434
|
+
</View>
|
|
435
|
+
)}
|
|
436
|
+
{isLoadingImageError && (
|
|
437
|
+
<View style={{ position: 'absolute' }}>
|
|
438
|
+
<ImageLoadingFailedIndicator style={styles.imageLoadingIndicatorStyle} />
|
|
439
|
+
</View>
|
|
440
|
+
)}
|
|
441
|
+
</View>
|
|
442
|
+
)}
|
|
443
|
+
{colIndex === numOfColumns - 1 && rowIndex === numOfRows - 1 && imagesAndVideos.length > 4 ? (
|
|
444
|
+
<View
|
|
445
|
+
style={[
|
|
446
|
+
StyleSheet.absoluteFillObject,
|
|
447
|
+
styles.moreImagesContainer,
|
|
448
|
+
{ backgroundColor: overlay },
|
|
449
|
+
moreImagesContainer,
|
|
450
|
+
]}
|
|
451
|
+
>
|
|
452
|
+
<Text style={[styles.moreImagesText, moreImagesText]}>
|
|
453
|
+
{`+${imagesAndVideos.length - 4}`}
|
|
454
|
+
</Text>
|
|
455
|
+
</View>
|
|
456
|
+
) : null}
|
|
457
|
+
</TouchableOpacity>
|
|
458
|
+
);
|
|
459
|
+
};
|
|
460
|
+
|
|
371
461
|
const areEqual = <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(
|
|
372
462
|
prevProps: GalleryPropsWithContext<StreamChatGenerics>,
|
|
373
463
|
nextProps: GalleryPropsWithContext<StreamChatGenerics>,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../../contexts/channelsContext/ChannelsContext';
|
|
11
11
|
import { ChatContextValue, useChatContext } from '../../contexts/chatContext/ChatContext';
|
|
12
12
|
|
|
13
|
+
import { useTranslatedMessage } from '../../hooks/useTranslatedMessage';
|
|
13
14
|
import type { DefaultStreamChatGenerics } from '../../types/types';
|
|
14
15
|
|
|
15
16
|
export type ChannelPreviewPropsWithContext<
|
|
@@ -38,10 +39,15 @@ const ChannelPreviewWithContext = <
|
|
|
38
39
|
| MessageResponse<StreamChatGenerics>
|
|
39
40
|
| undefined
|
|
40
41
|
>(channel.state.messages[channel.state.messages.length - 1]);
|
|
42
|
+
|
|
43
|
+
const translatedLastMessage = useTranslatedMessage<StreamChatGenerics>(
|
|
44
|
+
lastMessage || ({} as MessageResponse<StreamChatGenerics>),
|
|
45
|
+
);
|
|
46
|
+
|
|
41
47
|
const [forceUpdate, setForceUpdate] = useState(0);
|
|
42
48
|
const [unread, setUnread] = useState(channel.countUnread());
|
|
43
49
|
|
|
44
|
-
const latestMessagePreview = useLatestMessagePreview(channel, forceUpdate,
|
|
50
|
+
const latestMessagePreview = useLatestMessagePreview(channel, forceUpdate, translatedLastMessage);
|
|
45
51
|
|
|
46
52
|
const channelLastMessage = channel.lastMessage();
|
|
47
53
|
const channelLastMessageString = `${channelLastMessage?.id}${channelLastMessage?.updated_at}`;
|
package/src/components/ChannelPreview/__tests__/{ChannelPreview.test.js → ChannelPreview.test.tsx}
RENAMED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { ComponentType } from 'react';
|
|
2
2
|
import { Text } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { act, render, waitFor } from '@testing-library/react-native';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import type { Channel, StreamChat } from 'stream-chat';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
getOrCreateChannelApi,
|
|
10
|
+
GetOrCreateChannelApiParams,
|
|
11
|
+
} from '../../../mock-builders/api/getOrCreateChannel';
|
|
7
12
|
import { useMockedApis } from '../../../mock-builders/api/useMockedApis';
|
|
8
13
|
import dispatchMessageNewEvent from '../../../mock-builders/event/messageNew';
|
|
9
14
|
import dispatchMessageReadEvent from '../../../mock-builders/event/messageRead';
|
|
@@ -13,8 +18,23 @@ import { generateUser } from '../../../mock-builders/generator/user';
|
|
|
13
18
|
import { getTestClientWithUser } from '../../../mock-builders/mock';
|
|
14
19
|
import { Chat } from '../../Chat/Chat';
|
|
15
20
|
import { ChannelPreview } from '../ChannelPreview';
|
|
21
|
+
import type { ChannelPreviewMessengerProps } from '../ChannelPreviewMessenger';
|
|
22
|
+
|
|
23
|
+
import '@testing-library/jest-native/extend-expect';
|
|
24
|
+
|
|
25
|
+
type ChannelPreviewUIComponentProps = {
|
|
26
|
+
channel: {
|
|
27
|
+
id: string;
|
|
28
|
+
};
|
|
29
|
+
latestMessagePreview: {
|
|
30
|
+
messageObject: {
|
|
31
|
+
text: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
unread: number;
|
|
35
|
+
};
|
|
16
36
|
|
|
17
|
-
const ChannelPreviewUIComponent = (props) => (
|
|
37
|
+
const ChannelPreviewUIComponent = (props: ChannelPreviewUIComponentProps) => (
|
|
18
38
|
<>
|
|
19
39
|
<Text testID='channel-id'>{props.channel.id}</Text>
|
|
20
40
|
<Text testID='unread-count'>{props.unread}</Text>
|
|
@@ -28,21 +48,27 @@ const ChannelPreviewUIComponent = (props) => (
|
|
|
28
48
|
|
|
29
49
|
describe('ChannelPreview', () => {
|
|
30
50
|
const clientUser = generateUser();
|
|
31
|
-
let chatClient;
|
|
32
|
-
let channel;
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
let chatClient: StreamChat;
|
|
52
|
+
let channel: Channel | null;
|
|
53
|
+
|
|
54
|
+
const TestComponent = (props = {}) => {
|
|
55
|
+
if (channel === null) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Chat client={chatClient}>
|
|
61
|
+
<ChannelPreview
|
|
62
|
+
{...props}
|
|
63
|
+
channel={channel}
|
|
64
|
+
client={chatClient}
|
|
65
|
+
Preview={ChannelPreviewUIComponent as ComponentType<ChannelPreviewMessengerProps>}
|
|
66
|
+
/>
|
|
67
|
+
</Chat>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const useInitializeChannel = async (c: GetOrCreateChannelApiParams) => {
|
|
46
72
|
useMockedApis(chatClient, [getOrCreateChannelApi(c)]);
|
|
47
73
|
|
|
48
74
|
channel = chatClient.channel('messaging');
|
|
@@ -58,31 +84,22 @@ describe('ChannelPreview', () => {
|
|
|
58
84
|
channel = null;
|
|
59
85
|
});
|
|
60
86
|
|
|
61
|
-
it('should render with latest message on channel', async () => {
|
|
62
|
-
const message = generateMessage({
|
|
63
|
-
user: clientUser,
|
|
64
|
-
});
|
|
65
|
-
const c = generateChannelResponse({
|
|
66
|
-
messages: [message],
|
|
67
|
-
});
|
|
68
|
-
await initializeChannel(c);
|
|
69
|
-
const { queryByText } = render(getComponent());
|
|
70
|
-
await waitFor(() => queryByText(message.text));
|
|
71
|
-
});
|
|
72
|
-
|
|
73
87
|
it('should mark channel as read, when message.read event is received for current user', async () => {
|
|
74
88
|
const c = generateChannelResponse();
|
|
75
|
-
await
|
|
76
|
-
|
|
89
|
+
await useInitializeChannel(c);
|
|
90
|
+
|
|
91
|
+
if (channel !== null) {
|
|
92
|
+
channel.countUnread = () => 20;
|
|
93
|
+
}
|
|
77
94
|
|
|
78
|
-
const { getByTestId } = render(
|
|
95
|
+
const { getByTestId } = render(<TestComponent />);
|
|
79
96
|
|
|
80
97
|
await waitFor(() => getByTestId('channel-id'));
|
|
81
98
|
|
|
82
99
|
expect(getByTestId('unread-count')).toHaveTextContent('20');
|
|
83
100
|
|
|
84
101
|
act(() => {
|
|
85
|
-
dispatchMessageReadEvent(chatClient, clientUser, channel);
|
|
102
|
+
dispatchMessageReadEvent(chatClient, clientUser, channel || {});
|
|
86
103
|
});
|
|
87
104
|
|
|
88
105
|
await waitFor(() => {
|
|
@@ -92,9 +109,9 @@ describe('ChannelPreview', () => {
|
|
|
92
109
|
|
|
93
110
|
it('should update the lastest message on "message.new" event', async () => {
|
|
94
111
|
const c = generateChannelResponse();
|
|
95
|
-
await
|
|
112
|
+
await useInitializeChannel(c);
|
|
96
113
|
|
|
97
|
-
const { getByTestId } = render(
|
|
114
|
+
const { getByTestId } = render(<TestComponent />);
|
|
98
115
|
|
|
99
116
|
await waitFor(() => getByTestId('channel-id'));
|
|
100
117
|
|
|
@@ -103,7 +120,7 @@ describe('ChannelPreview', () => {
|
|
|
103
120
|
});
|
|
104
121
|
|
|
105
122
|
act(() => {
|
|
106
|
-
dispatchMessageNewEvent(chatClient, message, channel);
|
|
123
|
+
dispatchMessageNewEvent(chatClient, message, channel || {});
|
|
107
124
|
});
|
|
108
125
|
|
|
109
126
|
await waitFor(() => {
|
|
@@ -113,9 +130,9 @@ describe('ChannelPreview', () => {
|
|
|
113
130
|
|
|
114
131
|
it('should update the unread count on "message.new" event', async () => {
|
|
115
132
|
const c = generateChannelResponse();
|
|
116
|
-
await
|
|
133
|
+
await useInitializeChannel(c);
|
|
117
134
|
|
|
118
|
-
const { getByTestId } = render(
|
|
135
|
+
const { getByTestId } = render(<TestComponent />);
|
|
119
136
|
|
|
120
137
|
await waitFor(() => getByTestId('channel-id'));
|
|
121
138
|
|
|
@@ -123,14 +140,35 @@ describe('ChannelPreview', () => {
|
|
|
123
140
|
user: clientUser,
|
|
124
141
|
});
|
|
125
142
|
|
|
126
|
-
channel
|
|
143
|
+
if (channel !== null) {
|
|
144
|
+
channel.countUnread = () => 10;
|
|
145
|
+
}
|
|
127
146
|
|
|
128
147
|
act(() => {
|
|
129
|
-
dispatchMessageNewEvent(chatClient, message, channel);
|
|
148
|
+
dispatchMessageNewEvent(chatClient, message, channel || {});
|
|
130
149
|
});
|
|
131
150
|
|
|
132
151
|
await waitFor(() => {
|
|
133
152
|
expect(getByTestId('unread-count')).toHaveTextContent('10');
|
|
134
153
|
});
|
|
135
154
|
});
|
|
155
|
+
|
|
156
|
+
it('displays messages translated if applicable', async () => {
|
|
157
|
+
chatClient = await getTestClientWithUser({ id: 'mads', language: 'no' });
|
|
158
|
+
|
|
159
|
+
const message = {
|
|
160
|
+
i18n: {
|
|
161
|
+
no_text: 'Hallo verden!',
|
|
162
|
+
},
|
|
163
|
+
text: 'Hello world!',
|
|
164
|
+
};
|
|
165
|
+
const channel = generateChannelResponse({ messages: [message] });
|
|
166
|
+
await useInitializeChannel(channel);
|
|
167
|
+
|
|
168
|
+
const { getByText } = render(<TestComponent />);
|
|
169
|
+
|
|
170
|
+
await waitFor(() => {
|
|
171
|
+
expect(getByText(message.i18n.no_text)).toBeTruthy();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
136
174
|
});
|