stream-chat-react-native-core 9.3.0-beta.2 → 9.3.0-beta.4
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/ImageGallery/components/AnimatedGalleryImage.js +29 -0
- package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
- package/lib/commonjs/components/ImageGallery/components/ImageGrid.js +25 -8
- package/lib/commonjs/components/ImageGallery/components/ImageGrid.js.map +1 -1
- package/lib/commonjs/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js +2 -1
- package/lib/commonjs/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageFlashList.js +7 -9
- package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/commonjs/components/UIComponents/SvgAwareImage.js +54 -0
- package/lib/commonjs/components/UIComponents/SvgAwareImage.js.map +1 -0
- package/lib/commonjs/components/UIComponents/index.js +11 -0
- package/lib/commonjs/components/UIComponents/index.js.map +1 -1
- package/lib/commonjs/components/index.js +11 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/contexts/componentsContext/defaultComponents.js +2 -2
- package/lib/commonjs/contexts/componentsContext/defaultComponents.js.map +1 -1
- package/lib/commonjs/hooks/index.js +11 -0
- package/lib/commonjs/hooks/index.js.map +1 -1
- package/lib/commonjs/hooks/useIsSvg.js +22 -0
- package/lib/commonjs/hooks/useIsSvg.js.map +1 -0
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js +29 -0
- package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
- package/lib/module/components/ImageGallery/components/ImageGrid.js +25 -8
- package/lib/module/components/ImageGallery/components/ImageGrid.js.map +1 -1
- package/lib/module/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js +2 -1
- package/lib/module/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js.map +1 -1
- package/lib/module/components/MessageList/MessageFlashList.js +7 -9
- package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/module/components/UIComponents/SvgAwareImage.js +54 -0
- package/lib/module/components/UIComponents/SvgAwareImage.js.map +1 -0
- package/lib/module/components/UIComponents/index.js +11 -0
- package/lib/module/components/UIComponents/index.js.map +1 -1
- package/lib/module/components/index.js +11 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/contexts/componentsContext/defaultComponents.js +2 -2
- package/lib/module/contexts/componentsContext/defaultComponents.js.map +1 -1
- package/lib/module/hooks/index.js +11 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/useIsSvg.js +22 -0
- package/lib/module/hooks/useIsSvg.js.map +1 -0
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/ImageGallery/components/AnimatedGalleryImage.d.ts.map +1 -1
- package/lib/typescript/components/ImageGallery/components/ImageGrid.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageFlashList.d.ts.map +1 -1
- package/lib/typescript/components/UIComponents/SvgAwareImage.d.ts +11 -0
- package/lib/typescript/components/UIComponents/SvgAwareImage.d.ts.map +1 -0
- package/lib/typescript/components/UIComponents/index.d.ts +1 -0
- package/lib/typescript/components/UIComponents/index.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +2 -2
- package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts.map +1 -1
- package/lib/typescript/hooks/index.d.ts +1 -0
- package/lib/typescript/hooks/index.d.ts.map +1 -1
- package/lib/typescript/hooks/useIsSvg.d.ts +15 -0
- package/lib/typescript/hooks/useIsSvg.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/ImageGallery/components/AnimatedGalleryImage.tsx +35 -1
- package/src/components/ImageGallery/components/ImageGrid.tsx +15 -4
- package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx +3 -2
- package/src/components/MessageList/MessageFlashList.tsx +4 -6
- package/src/components/UIComponents/SvgAwareImage.tsx +49 -0
- package/src/components/UIComponents/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/contexts/componentsContext/defaultComponents.ts +3 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useIsSvg.ts +28 -0
- package/src/version.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { TextInputProps } from 'react-native';
|
|
3
3
|
import type { LocalMessage, UserResponse } from 'stream-chat';
|
|
4
4
|
import { KeyboardCompatibleView } from '../../components/KeyboardCompatibleView/KeyboardCompatibleView';
|
|
5
5
|
import type { MessageTextProps } from '../../components/Message/MessageItemView/MessageTextContainer';
|
|
@@ -293,7 +293,7 @@ declare const components: {
|
|
|
293
293
|
};
|
|
294
294
|
ImageGalleryVideoControls: React.MemoExoticComponent<(props: import("../..").ImageGalleryVideoControlProps) => React.JSX.Element>;
|
|
295
295
|
MessageOverlayBackground: () => React.JSX.Element;
|
|
296
|
-
ImageComponent: React.
|
|
296
|
+
ImageComponent: (props: import("react-native").ImageProps) => React.JSX.Element;
|
|
297
297
|
};
|
|
298
298
|
/**
|
|
299
299
|
* Optional component slots that have no default implementation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultComponents.d.ts","sourceRoot":"","sources":["../../../../src/contexts/componentsContext/defaultComponents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"defaultComponents.d.ts","sourceRoot":"","sources":["../../../../src/contexts/componentsContext/defaultComponents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiD9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gEAAgE,CAAC;AAmBxG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+DAA+D,CAAC;AAkFtG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAC;AAEjG;;;;;GAKG;AACH,KAAK,mBAAmB,CAAC,CAAC,IAAI;KAC3B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAC1F,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Jf,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC,mCAAmC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC1D,oBAAoB,CAAC,EAAE,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;IAClD,iBAAiB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACxC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAC1B,wBAAwB,CAAC,EAAE,cAAc,CAAC;QAC1C,QAAQ,EAAE,MAAM,YAAY,EAAE,CAAC;KAChC,CAAC,CAAC;IACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC1C,cAAc,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC;IAC1D,wBAAwB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC/C,yBAAyB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAChD,qBAAqB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5C,0BAA0B,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACjD,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACjE,aAAa,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACpC,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACpD,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CACnC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,mBAAmB,CAAC,OAAO,UAAU,CAAc,CAAC"}
|
|
@@ -7,6 +7,7 @@ export * from './useStateStore';
|
|
|
7
7
|
export * from './usePendingAttachmentUpload';
|
|
8
8
|
export * from './useStableCallback';
|
|
9
9
|
export * from './useLoadingImage';
|
|
10
|
+
export * from './useIsSvg';
|
|
10
11
|
export * from './useMessageReminder';
|
|
11
12
|
export * from './useQueryReminders';
|
|
12
13
|
export * from './useAfterKeyboardOpenCallback';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true if `uri` points to an SVG (either by `.svg` extension or by
|
|
3
|
+
* `image/svg+xml` data-URI prefix). Exported as a pure function for non-React
|
|
4
|
+
* callers; React components should prefer `useIsSvg` so the check is memoized
|
|
5
|
+
* per URI.
|
|
6
|
+
*/
|
|
7
|
+
export declare const isSvgUri: (uri: string | null | undefined) => boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Memoized variant of `isSvgUri`. Re-runs the string check only when `uri`
|
|
10
|
+
* actually changes — useful in hot render paths like the gallery where the
|
|
11
|
+
* surrounding component re-renders frequently (animated styles, swipe state)
|
|
12
|
+
* but the URI is stable.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useIsSvg: (uri: string | null | undefined) => boolean;
|
|
15
|
+
//# sourceMappingURL=useIsSvg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsSvg.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIsSvg.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OAUzD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OACrB,CAAC"}
|
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.3.0-beta.
|
|
4
|
+
"version": "9.3.0-beta.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"company": "Stream.io Inc",
|
|
7
7
|
"name": "Stream.io Inc"
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
import type { ImageStyle, StyleProp } from 'react-native';
|
|
4
4
|
import Animated, { SharedValue } from 'react-native-reanimated';
|
|
5
5
|
|
|
6
6
|
import { useChatConfigContext } from '../../../contexts/chatConfigContext/ChatConfigContext';
|
|
7
7
|
import { useImageGalleryContext } from '../../../contexts/imageGalleryContext/ImageGalleryContext';
|
|
8
8
|
import { useStateStore } from '../../../hooks';
|
|
9
|
+
import { useIsSvg } from '../../../hooks/useIsSvg';
|
|
9
10
|
import {
|
|
10
11
|
ImageGalleryAsset,
|
|
11
12
|
ImageGalleryState,
|
|
12
13
|
} from '../../../state-store/image-gallery-state-store';
|
|
13
14
|
import { getResizedImageUrl } from '../../../utils/getResizedImageUrl';
|
|
15
|
+
import { SvgAwareImage } from '../../UIComponents/SvgAwareImage';
|
|
14
16
|
import { useAnimatedGalleryStyle } from '../hooks/useAnimatedGalleryStyle';
|
|
15
17
|
|
|
16
18
|
const oneEighth = 1 / 8;
|
|
@@ -59,6 +61,7 @@ export const AnimatedGalleryImage = React.memo(
|
|
|
59
61
|
});
|
|
60
62
|
}, [photo.uri, resizableCDNHosts, screenHeight, screenWidth]);
|
|
61
63
|
|
|
64
|
+
const isSvg = useIsSvg(uri);
|
|
62
65
|
const selected = currentIndex === index;
|
|
63
66
|
const previous = currentIndex > index;
|
|
64
67
|
const shouldRender = Math.abs(currentIndex - index) < 4;
|
|
@@ -83,6 +86,27 @@ export const AnimatedGalleryImage = React.memo(
|
|
|
83
86
|
return <View style={[style, { transform: [{ scale: oneEighth }] }]} />;
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
if (isSvg) {
|
|
90
|
+
// The outer Animated.View is sized at 8× screen so raster images stay
|
|
91
|
+
// crisp under pinch zoom (see useAnimatedGalleryStyle). rn-svg on
|
|
92
|
+
// Android rasterizes the SVG to a bitmap at its layout size and an
|
|
93
|
+
// 8x screen bitmap exceeds RecordingCanvas's per draw byte limit. The
|
|
94
|
+
// inner SvgAwareImage is sized at 1x screen with a counter scale of 8 so
|
|
95
|
+
// the bitmap stays small while the composed visible scale (1/8 × 8 === 1)
|
|
96
|
+
// is unchanged.
|
|
97
|
+
return (
|
|
98
|
+
<Animated.View
|
|
99
|
+
accessibilityLabel={accessibilityLabel}
|
|
100
|
+
style={[...animatedStyles, style, styles.svgOuter]}
|
|
101
|
+
>
|
|
102
|
+
<SvgAwareImage
|
|
103
|
+
source={{ uri }}
|
|
104
|
+
style={[{ height: screenHeight, width: screenWidth }, styles.svgInner]}
|
|
105
|
+
/>
|
|
106
|
+
</Animated.View>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
86
110
|
return (
|
|
87
111
|
<Animated.Image
|
|
88
112
|
accessibilityLabel={accessibilityLabel}
|
|
@@ -106,3 +130,13 @@ export const AnimatedGalleryImage = React.memo(
|
|
|
106
130
|
);
|
|
107
131
|
|
|
108
132
|
AnimatedGalleryImage.displayName = 'AnimatedGalleryImage';
|
|
133
|
+
|
|
134
|
+
const styles = StyleSheet.create({
|
|
135
|
+
svgInner: {
|
|
136
|
+
transform: [{ scale: 8 }],
|
|
137
|
+
},
|
|
138
|
+
svgOuter: {
|
|
139
|
+
alignItems: 'center',
|
|
140
|
+
justifyContent: 'center',
|
|
141
|
+
},
|
|
142
|
+
});
|
|
@@ -3,7 +3,6 @@ import { Image, Pressable, StyleSheet, View } from 'react-native';
|
|
|
3
3
|
|
|
4
4
|
import type { ImageGalleryGridProps } from './types';
|
|
5
5
|
|
|
6
|
-
import { VideoThumbnail } from '../../../components/Attachment/VideoThumbnail';
|
|
7
6
|
import { useImageGalleryContext } from '../../../contexts/imageGalleryContext/ImageGalleryContextBase';
|
|
8
7
|
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
|
|
9
8
|
import { useStateStore } from '../../../hooks/useStateStore';
|
|
@@ -14,7 +13,9 @@ import type {
|
|
|
14
13
|
} from '../../../state-store/image-gallery-state-store';
|
|
15
14
|
import { primitives } from '../../../theme';
|
|
16
15
|
import { FileTypes } from '../../../types/types';
|
|
16
|
+
import { VideoPlayIndicator } from '../../ui/VideoPlayIndicator';
|
|
17
17
|
import { StreamBottomSheetModalFlatList } from '../../UIComponents/StreamBottomSheetModalFlatList';
|
|
18
|
+
import { SvgAwareImage } from '../../UIComponents/SvgAwareImage';
|
|
18
19
|
|
|
19
20
|
export type ImageGalleryGridImageComponent = ({
|
|
20
21
|
item,
|
|
@@ -42,11 +43,14 @@ const GridImage = ({ item }: { item: GridImageItem }) => {
|
|
|
42
43
|
return (
|
|
43
44
|
<Pressable accessibilityLabel='Grid Image' onPress={selectAndClose}>
|
|
44
45
|
{type === FileTypes.Video ? (
|
|
45
|
-
<View style={[styles.image, { height: size, width: size }]}>
|
|
46
|
-
<
|
|
46
|
+
<View style={[styles.image, { height: size, width: size }, styles.videoCell]}>
|
|
47
|
+
{thumb_url ? <Image source={{ uri: thumb_url }} style={StyleSheet.absoluteFill} /> : null}
|
|
48
|
+
<View pointerEvents='none' style={[StyleSheet.absoluteFill, styles.playIndicator]}>
|
|
49
|
+
<VideoPlayIndicator size='md' />
|
|
50
|
+
</View>
|
|
47
51
|
</View>
|
|
48
52
|
) : (
|
|
49
|
-
<
|
|
53
|
+
<SvgAwareImage source={{ uri }} style={[styles.image, { height: size, width: size }]} />
|
|
50
54
|
)}
|
|
51
55
|
</Pressable>
|
|
52
56
|
);
|
|
@@ -114,6 +118,13 @@ const useStyles = () => {
|
|
|
114
118
|
...contentContainer,
|
|
115
119
|
},
|
|
116
120
|
image: { margin: 1, ...gridImage },
|
|
121
|
+
playIndicator: {
|
|
122
|
+
alignItems: 'center',
|
|
123
|
+
justifyContent: 'center',
|
|
124
|
+
},
|
|
125
|
+
videoCell: {
|
|
126
|
+
overflow: 'hidden',
|
|
127
|
+
},
|
|
117
128
|
});
|
|
118
129
|
}, [contentContainer, gridImage, semantics]);
|
|
119
130
|
};
|
package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { StyleSheet, View } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import { LocalImageAttachment } from 'stream-chat';
|
|
6
6
|
|
|
@@ -26,6 +26,7 @@ export const ImageAttachmentUploadPreview = ({
|
|
|
26
26
|
const [loading, setLoading] = useState(true);
|
|
27
27
|
const { allowSendBeforeAttachmentsUpload } = useMessageInputContext();
|
|
28
28
|
const {
|
|
29
|
+
ImageComponent,
|
|
29
30
|
ImageLoadingIndicator,
|
|
30
31
|
ImageUploadInProgressIndicator,
|
|
31
32
|
ImageUploadRetryIndicator,
|
|
@@ -67,7 +68,7 @@ export const ImageAttachmentUploadPreview = ({
|
|
|
67
68
|
return (
|
|
68
69
|
<View style={[styles.wrapper, wrapper]} testID={'image-attachment-upload-preview'}>
|
|
69
70
|
<View style={[styles.image, upload]}>
|
|
70
|
-
<
|
|
71
|
+
<ImageComponent
|
|
71
72
|
onError={onErrorHandler}
|
|
72
73
|
onLoadEnd={onLoadEndHandler}
|
|
73
74
|
source={{ uri: previewUri }}
|
|
@@ -431,7 +431,7 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
|
|
|
431
431
|
if (indexOfParentInMessageList === -1) {
|
|
432
432
|
loadChannelAroundMessage({ messageId: targetedMessage, setTargetedMessage });
|
|
433
433
|
} else {
|
|
434
|
-
scrollToDebounceTimeoutRef.current = setTimeout(() => {
|
|
434
|
+
scrollToDebounceTimeoutRef.current = setTimeout(async () => {
|
|
435
435
|
clearTimeout(scrollToDebounceTimeoutRef.current);
|
|
436
436
|
|
|
437
437
|
const scrollToIndex = async () => {
|
|
@@ -442,20 +442,18 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
|
|
|
442
442
|
}
|
|
443
443
|
|
|
444
444
|
await list.scrollToIndex({
|
|
445
|
-
index: indexOfParentInMessageList,
|
|
446
445
|
animated: true,
|
|
446
|
+
index: indexOfParentInMessageList,
|
|
447
447
|
viewPosition: 0.5,
|
|
448
448
|
});
|
|
449
449
|
|
|
450
450
|
return true;
|
|
451
451
|
};
|
|
452
452
|
|
|
453
|
+
await scrollToIndex();
|
|
453
454
|
requestAnimationFrame(async () => {
|
|
454
455
|
await scrollToIndex();
|
|
455
|
-
|
|
456
|
-
await scrollToIndex();
|
|
457
|
-
setTargetedMessage(undefined);
|
|
458
|
-
});
|
|
456
|
+
setTargetedMessage(undefined);
|
|
459
457
|
});
|
|
460
458
|
}, WAIT_FOR_SCROLL_TIMEOUT);
|
|
461
459
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Image, ImageProps, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { SvgUri } from 'react-native-svg';
|
|
5
|
+
|
|
6
|
+
import { useIsSvg } from '../../hooks/useIsSvg';
|
|
7
|
+
|
|
8
|
+
const getSourceUri = (source: ImageProps['source']): string | undefined => {
|
|
9
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return source.uri;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Default `ImageComponent` for the SDK. Behaves exactly like RN's `Image` for
|
|
17
|
+
* raster sources, but transparently renders SVG URIs (`.svg`, `image/svg+xml`
|
|
18
|
+
* data URIs) via `SvgUri` from `react-native-svg`. Integrators who override
|
|
19
|
+
* `ImageComponent` with a custom image library (e.g. FastImage) are
|
|
20
|
+
* responsible for SVG handling in their override.
|
|
21
|
+
*/
|
|
22
|
+
export const SvgAwareImage = (props: ImageProps) => {
|
|
23
|
+
const uri = getSourceUri(props.source);
|
|
24
|
+
const isSvg = useIsSvg(uri);
|
|
25
|
+
|
|
26
|
+
if (!isSvg || !uri) {
|
|
27
|
+
return <Image {...props} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { accessibilityLabel, onError, onLoad, onLoadEnd, style, testID } = props;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<View accessibilityLabel={accessibilityLabel} style={style} testID={testID}>
|
|
34
|
+
<SvgUri
|
|
35
|
+
height='100%'
|
|
36
|
+
onError={(error) => {
|
|
37
|
+
onError?.({ nativeEvent: { error } } as never);
|
|
38
|
+
onLoadEnd?.();
|
|
39
|
+
}}
|
|
40
|
+
onLoad={() => {
|
|
41
|
+
onLoad?.({} as never);
|
|
42
|
+
onLoadEnd?.();
|
|
43
|
+
}}
|
|
44
|
+
uri={uri}
|
|
45
|
+
width='100%'
|
|
46
|
+
/>
|
|
47
|
+
</View>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './BottomSheetModal';
|
|
2
2
|
export * from './StreamBottomSheetModalFlatList';
|
|
3
3
|
export * from './ImageBackground';
|
|
4
|
+
export * from './SvgAwareImage';
|
|
4
5
|
export * from './Spinner';
|
|
5
6
|
export * from './SwipableWrapper';
|
|
6
7
|
export * from './PortalWhileClosingView';
|
package/src/components/index.ts
CHANGED
|
@@ -188,6 +188,7 @@ export * from './ui';
|
|
|
188
188
|
export * from './UIComponents/BottomSheetModal';
|
|
189
189
|
export * from './UIComponents/StreamBottomSheetModalFlatList';
|
|
190
190
|
export * from './UIComponents/ImageBackground';
|
|
191
|
+
export * from './UIComponents/SvgAwareImage';
|
|
191
192
|
export * from './UIComponents/Spinner';
|
|
192
193
|
export * from './UIComponents/SwipableWrapper';
|
|
193
194
|
export * from './UIComponents/PortalWhileClosingView';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { TextInputProps } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type { LocalMessage, UserResponse } from 'stream-chat';
|
|
5
5
|
|
|
@@ -149,6 +149,7 @@ import { ThreadListItemMessagePreview } from '../../components/ThreadList/Thread
|
|
|
149
149
|
import { ThreadListUnreadBanner } from '../../components/ThreadList/ThreadListUnreadBanner';
|
|
150
150
|
import { ThreadMessagePreviewDeliveryStatus } from '../../components/ThreadList/ThreadMessagePreviewDeliveryStatus';
|
|
151
151
|
import { ChannelAvatar } from '../../components/ui/Avatar/ChannelAvatar';
|
|
152
|
+
import { SvgAwareImage } from '../../components/UIComponents/SvgAwareImage';
|
|
152
153
|
import { DefaultMessageOverlayBackground } from '../../contexts/overlayContext/MessageOverlayHostLayer';
|
|
153
154
|
import type { MessageActionsProps } from '../../contexts/overlayContext/MessageOverlayHostLayer';
|
|
154
155
|
|
|
@@ -317,7 +318,7 @@ const components = {
|
|
|
317
318
|
MessageOverlayBackground: DefaultMessageOverlayBackground,
|
|
318
319
|
|
|
319
320
|
// Image
|
|
320
|
-
ImageComponent:
|
|
321
|
+
ImageComponent: SvgAwareImage,
|
|
321
322
|
};
|
|
322
323
|
|
|
323
324
|
/**
|
package/src/hooks/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './useStateStore';
|
|
|
7
7
|
export * from './usePendingAttachmentUpload';
|
|
8
8
|
export * from './useStableCallback';
|
|
9
9
|
export * from './useLoadingImage';
|
|
10
|
+
export * from './useIsSvg';
|
|
10
11
|
export * from './useMessageReminder';
|
|
11
12
|
export * from './useQueryReminders';
|
|
12
13
|
export * from './useAfterKeyboardOpenCallback';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns true if `uri` points to an SVG (either by `.svg` extension or by
|
|
5
|
+
* `image/svg+xml` data-URI prefix). Exported as a pure function for non-React
|
|
6
|
+
* callers; React components should prefer `useIsSvg` so the check is memoized
|
|
7
|
+
* per URI.
|
|
8
|
+
*/
|
|
9
|
+
export const isSvgUri = (uri: string | null | undefined): boolean => {
|
|
10
|
+
if (typeof uri !== 'string' || uri.length === 0) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const lower = uri.toLowerCase();
|
|
14
|
+
if (lower.startsWith('data:image/svg+xml')) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
const pathOnly = lower.split('#')[0].split('?')[0];
|
|
18
|
+
return pathOnly.endsWith('.svg');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Memoized variant of `isSvgUri`. Re-runs the string check only when `uri`
|
|
23
|
+
* actually changes — useful in hot render paths like the gallery where the
|
|
24
|
+
* surrounding component re-renders frequently (animated styles, swipe state)
|
|
25
|
+
* but the URI is stable.
|
|
26
|
+
*/
|
|
27
|
+
export const useIsSvg = (uri: string | null | undefined): boolean =>
|
|
28
|
+
useMemo(() => isSvgUri(uri), [uri]);
|
package/src/version.json
CHANGED