stream-chat-react-native-core 9.1.2 → 9.1.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/Attachment/Gallery.js +9 -49
- package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
- package/lib/commonjs/components/Attachment/GalleryImage.js +65 -5
- package/lib/commonjs/components/Attachment/GalleryImage.js.map +1 -1
- package/lib/commonjs/components/Attachment/ImageLoadingIndicator.js +2 -14
- package/lib/commonjs/components/Attachment/ImageLoadingIndicator.js.map +1 -1
- package/lib/commonjs/components/Attachment/VideoThumbnail.js +18 -16
- package/lib/commonjs/components/Attachment/VideoThumbnail.js.map +1 -1
- package/lib/commonjs/utils/utils.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/Gallery.js +9 -49
- package/lib/module/components/Attachment/Gallery.js.map +1 -1
- package/lib/module/components/Attachment/GalleryImage.js +65 -5
- package/lib/module/components/Attachment/GalleryImage.js.map +1 -1
- package/lib/module/components/Attachment/ImageLoadingIndicator.js +2 -14
- package/lib/module/components/Attachment/ImageLoadingIndicator.js.map +1 -1
- package/lib/module/components/Attachment/VideoThumbnail.js +18 -16
- package/lib/module/components/Attachment/VideoThumbnail.js.map +1 -1
- package/lib/module/utils/utils.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Attachment/Gallery.d.ts.map +1 -1
- package/lib/typescript/components/Attachment/GalleryImage.d.ts +8 -1
- package/lib/typescript/components/Attachment/GalleryImage.d.ts.map +1 -1
- package/lib/typescript/components/Attachment/ImageLoadingIndicator.d.ts +1 -1
- package/lib/typescript/components/Attachment/ImageLoadingIndicator.d.ts.map +1 -1
- package/lib/typescript/components/Attachment/VideoThumbnail.d.ts.map +1 -1
- package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +1 -1
- package/lib/typescript/hooks/useTranslatedMessage.d.ts +6 -2
- package/lib/typescript/hooks/useTranslatedMessage.d.ts.map +1 -1
- package/lib/typescript/utils/utils.d.ts +2 -2
- package/lib/typescript/utils/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/Attachment/Gallery.tsx +9 -50
- package/src/components/Attachment/GalleryImage.tsx +90 -4
- package/src/components/Attachment/ImageLoadingIndicator.tsx +3 -15
- package/src/components/Attachment/VideoThumbnail.tsx +22 -9
- package/src/components/Attachment/__tests__/VideoThumbnail.test.tsx +44 -0
- package/src/utils/utils.ts +4 -4
- package/src/version.json +1 -1
|
@@ -32,6 +32,8 @@ export declare const useTranslatedMessage: (message?: LocalMessage | MessageResp
|
|
|
32
32
|
member?: import("stream-chat").ChannelMemberResponse | undefined;
|
|
33
33
|
mentioned_users?: import("stream-chat").UserResponse[] | undefined;
|
|
34
34
|
mentioned_channel?: boolean | undefined;
|
|
35
|
+
mentioned_here?: boolean | undefined;
|
|
36
|
+
mentioned_roles?: string[] | undefined;
|
|
35
37
|
message_text_updated_at?: string | undefined;
|
|
36
38
|
moderation?: import("stream-chat").ModerationResponse | undefined;
|
|
37
39
|
moderation_details?: import("stream-chat").ModerationDetailsResponse | undefined;
|
|
@@ -58,8 +60,8 @@ export declare const useTranslatedMessage: (message?: LocalMessage | MessageResp
|
|
|
58
60
|
pinned_at: Date | null;
|
|
59
61
|
status: string;
|
|
60
62
|
updated_at: Date;
|
|
61
|
-
error?: import("stream-chat").ErrorFromResponse<import("stream-chat").APIErrorResponse
|
|
62
|
-
quoted_message?: import("stream-chat").LocalMessageBase;
|
|
63
|
+
error?: import("stream-chat").ErrorFromResponse<import("stream-chat").APIErrorResponse> | null;
|
|
64
|
+
quoted_message?: import("stream-chat").LocalMessageBase | null;
|
|
63
65
|
} | {
|
|
64
66
|
id: string;
|
|
65
67
|
attachments?: import("stream-chat").Attachment[];
|
|
@@ -96,6 +98,8 @@ export declare const useTranslatedMessage: (message?: LocalMessage | MessageResp
|
|
|
96
98
|
member?: import("stream-chat").ChannelMemberResponse;
|
|
97
99
|
mentioned_users?: import("stream-chat").UserResponse[];
|
|
98
100
|
mentioned_channel?: boolean;
|
|
101
|
+
mentioned_here?: boolean;
|
|
102
|
+
mentioned_roles?: string[];
|
|
99
103
|
message_text_updated_at?: string;
|
|
100
104
|
moderation?: import("stream-chat").ModerationResponse;
|
|
101
105
|
moderation_details?: import("stream-chat").ModerationDetailsResponse;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTranslatedMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/useTranslatedMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMvF,eAAO,MAAM,oBAAoB,GAAI,UAAU,YAAY,GAAG,eAAe;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"useTranslatedMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/useTranslatedMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMvF,eAAO,MAAM,oBAAoB,GAAI,UAAU,YAAY,GAAG,eAAe;;;;;;;;;;;;;;;;;;;;;;;YAkB+tkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAAD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAD5ykB,CAAC"}
|
|
@@ -121,7 +121,7 @@ export declare const findInMessagesByDate: (messages: MessageResponse[] | Channe
|
|
|
121
121
|
* If any of the properties differ, it returns false, indicating that the messages are not equal.
|
|
122
122
|
* Useful for the `areEqual` logic in the React.memo of the Message component/sub-components.
|
|
123
123
|
*/
|
|
124
|
-
export declare const checkMessageEquality: (prevMessage?: LocalMessage, nextMessage?: LocalMessage) => boolean;
|
|
124
|
+
export declare const checkMessageEquality: (prevMessage?: LocalMessage | null, nextMessage?: LocalMessage | null) => boolean;
|
|
125
125
|
/**
|
|
126
126
|
* The purpose of this function is to compare two quoted messages and determine if they are equal.
|
|
127
127
|
* It checks various properties of the messages, such as status, type, text, updated_at timestamp, and deleted_at.
|
|
@@ -129,7 +129,7 @@ export declare const checkMessageEquality: (prevMessage?: LocalMessage, nextMess
|
|
|
129
129
|
* If any of the properties differ, it returns false, indicating that the messages are not equal.
|
|
130
130
|
* Useful for the `areEqual` logic in the React.memo of the Message component/sub-components.
|
|
131
131
|
*/
|
|
132
|
-
export declare const checkQuotedMessageEquality: (prevQuotedMessage?: LocalMessage, nextQuotedMessage?: LocalMessage) => boolean;
|
|
132
|
+
export declare const checkQuotedMessageEquality: (prevQuotedMessage?: LocalMessage | null, nextQuotedMessage?: LocalMessage | null) => boolean;
|
|
133
133
|
/**
|
|
134
134
|
* Utility to get initials from name.
|
|
135
135
|
* @param name string
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,SAAS;;;;;;EAMpB,CAAC;AAEH,eAAO,MAAM,sBAAsB,EAAE;IACnC,WAAW,EAAE,aAAa,CAAC;IAC3B,QAAQ,EAAE,UAAU,CAAC;IACrB,aAAa,EAAE,eAAe,CAAC;IAC/B,OAAO,EAAE,SAAS,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAOf,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;CAK9B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAG9D,eAAO,MAAM,4BAA4B,GACvC,WAAW,sBAAsB,EACjC,kCAAkC,OAAO,KACxC,QAAQ,GAAG,SAcb,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,YAAY,YAMrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,YAAY,YAGT,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,YAAY,YAAsC,CAAC;AAE5F,eAAO,MAAM,sBAAsB,GAAI,KAAK,MAAM,WACO,CAAC;AAE1D,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,uBAW/C,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,YAA0B,CAAC;AAEjE,eAAO,MAAM,gBAAgB,GAAI,UAAM,KAAG,MAIqC,CAAC;AAKhF,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,YAazC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,gCAG9B;IACD,OAAO,EAAE,eAAe,GAAG,YAAY,CAAC;IACxC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,KAAG,MA6BH,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,UAAU,YAAY,EAAE,KAAG,MAOvD,CAAC;AAEZ;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,WAI/C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,WAgB5D,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,IAAI,MAAM,WAc1C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,UAExC;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,UAAU,YAAY,CAAC,UAAU,CAAC,EAAE,UAAU,MAAM,WAGtF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,UAAU,eAAe,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,EACtD,YAAY,IAAI;;;;;;CAgCjB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,cAAc,YAAY,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils/utils.ts"],"names":[],"mappings":"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"}
|
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": "9.1.2",
|
|
4
|
+
"version": "9.1.3-beta.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"company": "Stream.io Inc",
|
|
7
7
|
"name": "Stream.io Inc"
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"path": "0.12.7",
|
|
84
84
|
"react-native-markdown-package": "1.8.2",
|
|
85
85
|
"react-native-url-polyfill": "^2.0.0",
|
|
86
|
-
"stream-chat": "^9.43.
|
|
86
|
+
"stream-chat": "^9.43.1",
|
|
87
87
|
"use-sync-external-store": "^1.5.0"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type { Attachment, LocalMessage } from 'stream-chat';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { LoadableGalleryImage } from './GalleryImage';
|
|
7
7
|
import { buildGallery } from './utils/buildGallery/buildGallery';
|
|
8
8
|
|
|
9
9
|
import type { Thumbnail } from './utils/buildGallery/types';
|
|
@@ -35,8 +35,6 @@ import {
|
|
|
35
35
|
} from '../../contexts/overlayContext/OverlayContext';
|
|
36
36
|
import { useTheme } from '../../contexts/themeContext/ThemeContext';
|
|
37
37
|
|
|
38
|
-
import { useLoadingImage } from '../../hooks/useLoadingImage';
|
|
39
|
-
import { useStableCallback } from '../../hooks/useStableCallback';
|
|
40
38
|
import { isVideoPlayerAvailable } from '../../native';
|
|
41
39
|
import { primitives } from '../../theme';
|
|
42
40
|
import { FileTypes } from '../../types/types';
|
|
@@ -331,15 +329,6 @@ const GalleryImageThumbnail = ({
|
|
|
331
329
|
borderRadius,
|
|
332
330
|
thumbnail,
|
|
333
331
|
}: Pick<GalleryThumbnailProps, 'thumbnail' | 'borderRadius'>) => {
|
|
334
|
-
const { AttachmentUploadIndicator, ImageLoadingFailedIndicator, ImageLoadingIndicator } =
|
|
335
|
-
useComponentsContext();
|
|
336
|
-
const {
|
|
337
|
-
isLoadingImage,
|
|
338
|
-
isLoadingImageError,
|
|
339
|
-
onReloadImage,
|
|
340
|
-
setLoadingImage,
|
|
341
|
-
setLoadingImageError,
|
|
342
|
-
} = useLoadingImage();
|
|
343
332
|
const {
|
|
344
333
|
theme: {
|
|
345
334
|
messageItemView: { gallery },
|
|
@@ -347,44 +336,14 @@ const GalleryImageThumbnail = ({
|
|
|
347
336
|
} = useTheme();
|
|
348
337
|
const styles = useStyles();
|
|
349
338
|
|
|
350
|
-
const onLoadStart = useStableCallback(() => {
|
|
351
|
-
setLoadingImageError(false);
|
|
352
|
-
setLoadingImage(true);
|
|
353
|
-
});
|
|
354
|
-
const onLoad = useStableCallback(() => {
|
|
355
|
-
setTimeout(() => {
|
|
356
|
-
setLoadingImage(false);
|
|
357
|
-
setLoadingImageError(false);
|
|
358
|
-
}, 0);
|
|
359
|
-
});
|
|
360
|
-
const onError = useStableCallback(({ nativeEvent: { error } }: ImageErrorEvent) => {
|
|
361
|
-
console.warn(error);
|
|
362
|
-
setLoadingImage(false);
|
|
363
|
-
setLoadingImageError(true);
|
|
364
|
-
});
|
|
365
339
|
return (
|
|
366
|
-
<
|
|
367
|
-
{
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
onLoad={onLoad}
|
|
374
|
-
onLoadStart={onLoadStart}
|
|
375
|
-
resizeMode={thumbnail.resizeMode}
|
|
376
|
-
style={gallery.image}
|
|
377
|
-
uri={thumbnail.url}
|
|
378
|
-
/>
|
|
379
|
-
{isLoadingImage ? <ImageLoadingIndicator /> : null}
|
|
380
|
-
<AttachmentUploadIndicator
|
|
381
|
-
localId={thumbnail.localId}
|
|
382
|
-
sourceUrl={thumbnail.url}
|
|
383
|
-
variant='overlay'
|
|
384
|
-
/>
|
|
385
|
-
</>
|
|
386
|
-
)}
|
|
387
|
-
</View>
|
|
340
|
+
<LoadableGalleryImage
|
|
341
|
+
containerStyle={[styles.image, borderRadius]}
|
|
342
|
+
imageStyle={gallery.image}
|
|
343
|
+
localId={thumbnail.localId}
|
|
344
|
+
resizeMode={thumbnail.resizeMode}
|
|
345
|
+
uri={thumbnail.url}
|
|
346
|
+
/>
|
|
388
347
|
);
|
|
389
348
|
};
|
|
390
349
|
const areEqual = (prevProps: GalleryPropsWithContext, nextProps: GalleryPropsWithContext) => {
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Image,
|
|
4
|
+
ImageErrorEvent,
|
|
5
|
+
ImageProps,
|
|
6
|
+
ImageStyle,
|
|
7
|
+
StyleProp,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
View,
|
|
10
|
+
ViewStyle,
|
|
11
|
+
} from 'react-native';
|
|
3
12
|
|
|
4
13
|
import { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext';
|
|
14
|
+
import { useLoadingImage } from '../../hooks/useLoadingImage';
|
|
15
|
+
import { useStableCallback } from '../../hooks/useStableCallback';
|
|
5
16
|
|
|
6
17
|
import { getUrlWithoutParams, isLocalUrl, makeImageCompatibleUrl } from '../../utils/utils';
|
|
7
18
|
|
|
@@ -10,7 +21,13 @@ export type GalleryImageWithContextProps = GalleryImageProps & {
|
|
|
10
21
|
};
|
|
11
22
|
|
|
12
23
|
export const GalleryImageWithContext = (props: GalleryImageWithContextProps) => {
|
|
13
|
-
const {
|
|
24
|
+
const {
|
|
25
|
+
accessibilityLabel = 'Gallery Image',
|
|
26
|
+
ImageComponent = Image,
|
|
27
|
+
uri,
|
|
28
|
+
style,
|
|
29
|
+
...rest
|
|
30
|
+
} = props;
|
|
14
31
|
|
|
15
32
|
// Caching image components such as FastImage will not work with local images.
|
|
16
33
|
// This for the case of local uris, we use the default Image component.
|
|
@@ -18,7 +35,7 @@ export const GalleryImageWithContext = (props: GalleryImageWithContextProps) =>
|
|
|
18
35
|
return (
|
|
19
36
|
<ImageComponent
|
|
20
37
|
{...rest}
|
|
21
|
-
accessibilityLabel=
|
|
38
|
+
accessibilityLabel={accessibilityLabel}
|
|
22
39
|
style={[styles.image, style]}
|
|
23
40
|
source={{
|
|
24
41
|
uri: makeImageCompatibleUrl(uri),
|
|
@@ -30,7 +47,7 @@ export const GalleryImageWithContext = (props: GalleryImageWithContextProps) =>
|
|
|
30
47
|
return (
|
|
31
48
|
<Image
|
|
32
49
|
{...rest}
|
|
33
|
-
accessibilityLabel=
|
|
50
|
+
accessibilityLabel={accessibilityLabel}
|
|
34
51
|
style={[styles.image, style]}
|
|
35
52
|
source={{
|
|
36
53
|
uri: makeImageCompatibleUrl(uri),
|
|
@@ -54,6 +71,75 @@ export const GalleryImage = (props: GalleryImageProps) => {
|
|
|
54
71
|
return <MemoizedGalleryImage ImageComponent={ImageComponent} {...props} />;
|
|
55
72
|
};
|
|
56
73
|
|
|
74
|
+
export type LoadableGalleryImageProps = Pick<
|
|
75
|
+
GalleryImageProps,
|
|
76
|
+
'accessibilityLabel' | 'resizeMode' | 'uri'
|
|
77
|
+
> & {
|
|
78
|
+
children?: React.ReactNode;
|
|
79
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
80
|
+
imageStyle?: StyleProp<ImageStyle>;
|
|
81
|
+
localId?: string;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const LoadableGalleryImage = ({
|
|
85
|
+
accessibilityLabel = 'Gallery Image',
|
|
86
|
+
children,
|
|
87
|
+
containerStyle,
|
|
88
|
+
imageStyle,
|
|
89
|
+
localId,
|
|
90
|
+
resizeMode,
|
|
91
|
+
uri,
|
|
92
|
+
}: LoadableGalleryImageProps) => {
|
|
93
|
+
const { AttachmentUploadIndicator, ImageLoadingFailedIndicator, ImageLoadingIndicator } =
|
|
94
|
+
useComponentsContext();
|
|
95
|
+
const {
|
|
96
|
+
isLoadingImage,
|
|
97
|
+
isLoadingImageError,
|
|
98
|
+
onReloadImage,
|
|
99
|
+
setLoadingImage,
|
|
100
|
+
setLoadingImageError,
|
|
101
|
+
} = useLoadingImage();
|
|
102
|
+
|
|
103
|
+
const onLoadStart = useStableCallback(() => {
|
|
104
|
+
setLoadingImageError(false);
|
|
105
|
+
setLoadingImage(true);
|
|
106
|
+
});
|
|
107
|
+
const onLoad = useStableCallback(() => {
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
setLoadingImage(false);
|
|
110
|
+
setLoadingImageError(false);
|
|
111
|
+
}, 0);
|
|
112
|
+
});
|
|
113
|
+
const onError = useStableCallback(({ nativeEvent: { error } }: ImageErrorEvent) => {
|
|
114
|
+
console.warn(error);
|
|
115
|
+
setLoadingImage(false);
|
|
116
|
+
setLoadingImageError(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<View style={[styles.image, containerStyle]}>
|
|
121
|
+
{isLoadingImageError ? (
|
|
122
|
+
<ImageLoadingFailedIndicator onReloadImage={onReloadImage} />
|
|
123
|
+
) : (
|
|
124
|
+
<>
|
|
125
|
+
<GalleryImage
|
|
126
|
+
accessibilityLabel={accessibilityLabel}
|
|
127
|
+
onError={onError}
|
|
128
|
+
onLoad={onLoad}
|
|
129
|
+
onLoadStart={onLoadStart}
|
|
130
|
+
resizeMode={resizeMode}
|
|
131
|
+
style={imageStyle}
|
|
132
|
+
uri={uri}
|
|
133
|
+
/>
|
|
134
|
+
{children}
|
|
135
|
+
{isLoadingImage ? <ImageLoadingIndicator /> : null}
|
|
136
|
+
<AttachmentUploadIndicator localId={localId} sourceUrl={uri} variant='overlay' />
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
</View>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
57
143
|
const styles = StyleSheet.create({
|
|
58
144
|
image: {
|
|
59
145
|
flex: 1,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { useTheme } from '../../contexts';
|
|
5
5
|
import { NativeShimmerView } from '../UIComponents/NativeShimmerView';
|
|
6
6
|
|
|
7
|
-
export const ImageLoadingIndicator = () => {
|
|
7
|
+
export const ImageLoadingIndicator = React.memo(() => {
|
|
8
8
|
const {
|
|
9
9
|
theme: { semantics },
|
|
10
10
|
} = useTheme();
|
|
@@ -15,18 +15,6 @@ export const ImageLoadingIndicator = () => {
|
|
|
15
15
|
enabled
|
|
16
16
|
gradientColor={semantics.skeletonLoadingHighlight}
|
|
17
17
|
style={StyleSheet.absoluteFill}
|
|
18
|
-
|
|
19
|
-
<View pointerEvents='none' style={styles.centered}>
|
|
20
|
-
<ActivityIndicator />
|
|
21
|
-
</View>
|
|
22
|
-
</NativeShimmerView>
|
|
18
|
+
/>
|
|
23
19
|
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const styles = StyleSheet.create({
|
|
27
|
-
centered: {
|
|
28
|
-
alignItems: 'center',
|
|
29
|
-
flex: 1,
|
|
30
|
-
justifyContent: 'center',
|
|
31
|
-
},
|
|
32
20
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ImageStyle, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { LoadableGalleryImage } from './GalleryImage';
|
|
5
|
+
|
|
5
6
|
import { useTheme } from '../../contexts/themeContext/ThemeContext';
|
|
6
7
|
import { VideoPlayIndicator } from '../ui/VideoPlayIndicator';
|
|
7
8
|
|
|
@@ -12,6 +13,10 @@ const styles = StyleSheet.create({
|
|
|
12
13
|
flex: 1,
|
|
13
14
|
overflow: 'hidden',
|
|
14
15
|
},
|
|
16
|
+
playIndicatorContainer: {
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
justifyContent: 'center',
|
|
19
|
+
},
|
|
15
20
|
});
|
|
16
21
|
|
|
17
22
|
export type VideoThumbnailProps = {
|
|
@@ -32,18 +37,26 @@ export const VideoThumbnail = (props: VideoThumbnailProps) => {
|
|
|
32
37
|
},
|
|
33
38
|
},
|
|
34
39
|
} = useTheme();
|
|
35
|
-
const { AttachmentUploadIndicator, ImageComponent } = useComponentsContext();
|
|
36
40
|
const { imageStyle, localId, style, thumb_url } = props;
|
|
37
41
|
|
|
38
42
|
return (
|
|
39
43
|
<View style={[styles.container, container, style]}>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
{thumb_url ? (
|
|
45
|
+
<LoadableGalleryImage
|
|
46
|
+
accessibilityLabel='Video Thumbnail'
|
|
47
|
+
containerStyle={StyleSheet.absoluteFill}
|
|
48
|
+
imageStyle={imageStyle}
|
|
49
|
+
localId={localId}
|
|
50
|
+
uri={thumb_url}
|
|
51
|
+
>
|
|
52
|
+
<View
|
|
53
|
+
pointerEvents='none'
|
|
54
|
+
style={[StyleSheet.absoluteFill, styles.playIndicatorContainer]}
|
|
55
|
+
>
|
|
56
|
+
<VideoPlayIndicator size='md' />
|
|
57
|
+
</View>
|
|
58
|
+
</LoadableGalleryImage>
|
|
59
|
+
) : null}
|
|
47
60
|
</View>
|
|
48
61
|
);
|
|
49
62
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ImageProps, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { fireEvent, render, screen } from '@testing-library/react-native';
|
|
5
|
+
|
|
6
|
+
import { WithComponents } from '../../../contexts/componentsContext/ComponentsContext';
|
|
7
|
+
import { ThemeProvider } from '../../../contexts/themeContext/ThemeContext';
|
|
8
|
+
import { VideoThumbnail } from '../VideoThumbnail';
|
|
9
|
+
|
|
10
|
+
const CustomImageComponent = (props: ImageProps) => (
|
|
11
|
+
<View accessibilityLabel={props.accessibilityLabel} testID='custom-image-component' />
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const renderVideoThumbnail = (thumb_url: string) =>
|
|
15
|
+
render(
|
|
16
|
+
<ThemeProvider>
|
|
17
|
+
<WithComponents overrides={{ ImageComponent: CustomImageComponent }}>
|
|
18
|
+
<VideoThumbnail thumb_url={thumb_url} />
|
|
19
|
+
</WithComponents>
|
|
20
|
+
</ThemeProvider>,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
describe('VideoThumbnail', () => {
|
|
24
|
+
it('uses the configured ImageComponent for remote thumbnail URLs', () => {
|
|
25
|
+
renderVideoThumbnail('https://example.com/video-thumbnail.jpg');
|
|
26
|
+
|
|
27
|
+
expect(screen.getByTestId('custom-image-component')).toBeTruthy();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('uses the default Image fallback for local thumbnail paths', () => {
|
|
31
|
+
renderVideoThumbnail('file:///tmp/video-thumbnail.jpg');
|
|
32
|
+
|
|
33
|
+
expect(screen.queryByTestId('custom-image-component')).toBeNull();
|
|
34
|
+
expect(screen.getByLabelText('Video Thumbnail')).toBeTruthy();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders the image loading indicator while the thumbnail is loading', () => {
|
|
38
|
+
renderVideoThumbnail('file:///tmp/video-thumbnail.jpg');
|
|
39
|
+
|
|
40
|
+
fireEvent(screen.getByLabelText('Video Thumbnail'), 'loadStart');
|
|
41
|
+
|
|
42
|
+
expect(screen.getByLabelText('Image Loading Indicator')).toBeTruthy();
|
|
43
|
+
});
|
|
44
|
+
});
|
package/src/utils/utils.ts
CHANGED
|
@@ -325,8 +325,8 @@ export const findInMessagesByDate = (
|
|
|
325
325
|
* Useful for the `areEqual` logic in the React.memo of the Message component/sub-components.
|
|
326
326
|
*/
|
|
327
327
|
export const checkMessageEquality = (
|
|
328
|
-
prevMessage?: LocalMessage,
|
|
329
|
-
nextMessage?: LocalMessage,
|
|
328
|
+
prevMessage?: LocalMessage | null,
|
|
329
|
+
nextMessage?: LocalMessage | null,
|
|
330
330
|
): boolean => {
|
|
331
331
|
const prevMessageExists = !!prevMessage;
|
|
332
332
|
const nextMessageExists = !!nextMessage;
|
|
@@ -357,8 +357,8 @@ export const checkMessageEquality = (
|
|
|
357
357
|
* Useful for the `areEqual` logic in the React.memo of the Message component/sub-components.
|
|
358
358
|
*/
|
|
359
359
|
export const checkQuotedMessageEquality = (
|
|
360
|
-
prevQuotedMessage?: LocalMessage,
|
|
361
|
-
nextQuotedMessage?: LocalMessage,
|
|
360
|
+
prevQuotedMessage?: LocalMessage | null,
|
|
361
|
+
nextQuotedMessage?: LocalMessage | null,
|
|
362
362
|
): boolean => {
|
|
363
363
|
const prevQuotedMessageExists = !!prevQuotedMessage;
|
|
364
364
|
const nextQuotedMessageExists = !!nextQuotedMessage;
|
package/src/version.json
CHANGED