stream-chat-react-native-core 9.1.2 → 9.1.3-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.
- 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/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/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/package.json +1 -1
- 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/version.json +1 -1
|
@@ -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/version.json
CHANGED