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.
Files changed (32) hide show
  1. package/lib/commonjs/components/Attachment/Gallery.js +9 -49
  2. package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
  3. package/lib/commonjs/components/Attachment/GalleryImage.js +65 -5
  4. package/lib/commonjs/components/Attachment/GalleryImage.js.map +1 -1
  5. package/lib/commonjs/components/Attachment/ImageLoadingIndicator.js +2 -14
  6. package/lib/commonjs/components/Attachment/ImageLoadingIndicator.js.map +1 -1
  7. package/lib/commonjs/components/Attachment/VideoThumbnail.js +18 -16
  8. package/lib/commonjs/components/Attachment/VideoThumbnail.js.map +1 -1
  9. package/lib/commonjs/version.json +1 -1
  10. package/lib/module/components/Attachment/Gallery.js +9 -49
  11. package/lib/module/components/Attachment/Gallery.js.map +1 -1
  12. package/lib/module/components/Attachment/GalleryImage.js +65 -5
  13. package/lib/module/components/Attachment/GalleryImage.js.map +1 -1
  14. package/lib/module/components/Attachment/ImageLoadingIndicator.js +2 -14
  15. package/lib/module/components/Attachment/ImageLoadingIndicator.js.map +1 -1
  16. package/lib/module/components/Attachment/VideoThumbnail.js +18 -16
  17. package/lib/module/components/Attachment/VideoThumbnail.js.map +1 -1
  18. package/lib/module/version.json +1 -1
  19. package/lib/typescript/components/Attachment/Gallery.d.ts.map +1 -1
  20. package/lib/typescript/components/Attachment/GalleryImage.d.ts +8 -1
  21. package/lib/typescript/components/Attachment/GalleryImage.d.ts.map +1 -1
  22. package/lib/typescript/components/Attachment/ImageLoadingIndicator.d.ts +1 -1
  23. package/lib/typescript/components/Attachment/ImageLoadingIndicator.d.ts.map +1 -1
  24. package/lib/typescript/components/Attachment/VideoThumbnail.d.ts.map +1 -1
  25. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +1 -1
  26. package/package.json +1 -1
  27. package/src/components/Attachment/Gallery.tsx +9 -50
  28. package/src/components/Attachment/GalleryImage.tsx +90 -4
  29. package/src/components/Attachment/ImageLoadingIndicator.tsx +3 -15
  30. package/src/components/Attachment/VideoThumbnail.tsx +22 -9
  31. package/src/components/Attachment/__tests__/VideoThumbnail.test.tsx +44 -0
  32. package/src/version.json +1 -1
@@ -1,7 +1,18 @@
1
1
  import React from 'react';
2
- import { Image, ImageProps, StyleSheet } from 'react-native';
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 { ImageComponent = Image, uri, style, ...rest } = props;
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='Gallery Image'
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='Gallery Image'
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 { ActivityIndicator, StyleSheet, View } from 'react-native';
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 { useComponentsContext } from '../../contexts/componentsContext/ComponentsContext';
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
- <ImageComponent
41
- accessibilityLabel='Video Thumbnail'
42
- source={{ uri: thumb_url }}
43
- style={[StyleSheet.absoluteFill, imageStyle]}
44
- />
45
- <VideoPlayIndicator size='md' />
46
- <AttachmentUploadIndicator localId={localId} sourceUrl={thumb_url} variant='overlay' />
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
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "9.1.2"
2
+ "version": "9.1.3-beta.1"
3
3
  }