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.
Files changed (70) hide show
  1. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryImage.js +29 -0
  2. package/lib/commonjs/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
  3. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js +25 -8
  4. package/lib/commonjs/components/ImageGallery/components/ImageGrid.js.map +1 -1
  5. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js +2 -1
  6. package/lib/commonjs/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js.map +1 -1
  7. package/lib/commonjs/components/MessageList/MessageFlashList.js +7 -9
  8. package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
  9. package/lib/commonjs/components/UIComponents/SvgAwareImage.js +54 -0
  10. package/lib/commonjs/components/UIComponents/SvgAwareImage.js.map +1 -0
  11. package/lib/commonjs/components/UIComponents/index.js +11 -0
  12. package/lib/commonjs/components/UIComponents/index.js.map +1 -1
  13. package/lib/commonjs/components/index.js +11 -0
  14. package/lib/commonjs/components/index.js.map +1 -1
  15. package/lib/commonjs/contexts/componentsContext/defaultComponents.js +2 -2
  16. package/lib/commonjs/contexts/componentsContext/defaultComponents.js.map +1 -1
  17. package/lib/commonjs/hooks/index.js +11 -0
  18. package/lib/commonjs/hooks/index.js.map +1 -1
  19. package/lib/commonjs/hooks/useIsSvg.js +22 -0
  20. package/lib/commonjs/hooks/useIsSvg.js.map +1 -0
  21. package/lib/commonjs/version.json +1 -1
  22. package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js +29 -0
  23. package/lib/module/components/ImageGallery/components/AnimatedGalleryImage.js.map +1 -1
  24. package/lib/module/components/ImageGallery/components/ImageGrid.js +25 -8
  25. package/lib/module/components/ImageGallery/components/ImageGrid.js.map +1 -1
  26. package/lib/module/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js +2 -1
  27. package/lib/module/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.js.map +1 -1
  28. package/lib/module/components/MessageList/MessageFlashList.js +7 -9
  29. package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
  30. package/lib/module/components/UIComponents/SvgAwareImage.js +54 -0
  31. package/lib/module/components/UIComponents/SvgAwareImage.js.map +1 -0
  32. package/lib/module/components/UIComponents/index.js +11 -0
  33. package/lib/module/components/UIComponents/index.js.map +1 -1
  34. package/lib/module/components/index.js +11 -0
  35. package/lib/module/components/index.js.map +1 -1
  36. package/lib/module/contexts/componentsContext/defaultComponents.js +2 -2
  37. package/lib/module/contexts/componentsContext/defaultComponents.js.map +1 -1
  38. package/lib/module/hooks/index.js +11 -0
  39. package/lib/module/hooks/index.js.map +1 -1
  40. package/lib/module/hooks/useIsSvg.js +22 -0
  41. package/lib/module/hooks/useIsSvg.js.map +1 -0
  42. package/lib/module/version.json +1 -1
  43. package/lib/typescript/components/ImageGallery/components/AnimatedGalleryImage.d.ts.map +1 -1
  44. package/lib/typescript/components/ImageGallery/components/ImageGrid.d.ts.map +1 -1
  45. package/lib/typescript/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.d.ts.map +1 -1
  46. package/lib/typescript/components/MessageList/MessageFlashList.d.ts.map +1 -1
  47. package/lib/typescript/components/UIComponents/SvgAwareImage.d.ts +11 -0
  48. package/lib/typescript/components/UIComponents/SvgAwareImage.d.ts.map +1 -0
  49. package/lib/typescript/components/UIComponents/index.d.ts +1 -0
  50. package/lib/typescript/components/UIComponents/index.d.ts.map +1 -1
  51. package/lib/typescript/components/index.d.ts +1 -0
  52. package/lib/typescript/components/index.d.ts.map +1 -1
  53. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +2 -2
  54. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts.map +1 -1
  55. package/lib/typescript/hooks/index.d.ts +1 -0
  56. package/lib/typescript/hooks/index.d.ts.map +1 -1
  57. package/lib/typescript/hooks/useIsSvg.d.ts +15 -0
  58. package/lib/typescript/hooks/useIsSvg.d.ts.map +1 -0
  59. package/package.json +1 -1
  60. package/src/components/ImageGallery/components/AnimatedGalleryImage.tsx +35 -1
  61. package/src/components/ImageGallery/components/ImageGrid.tsx +15 -4
  62. package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx +3 -2
  63. package/src/components/MessageList/MessageFlashList.tsx +4 -6
  64. package/src/components/UIComponents/SvgAwareImage.tsx +49 -0
  65. package/src/components/UIComponents/index.ts +1 -0
  66. package/src/components/index.ts +1 -0
  67. package/src/contexts/componentsContext/defaultComponents.ts +3 -2
  68. package/src/hooks/index.ts +1 -0
  69. package/src/hooks/useIsSvg.ts +28 -0
  70. package/src/version.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { ImageProps, TextInputProps } from 'react-native';
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.ComponentType<ImageProps>;
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,EAAS,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEjE,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;AAiFtG,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA2JW,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC;CACzD,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"}
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.2",
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
- <VideoThumbnail thumb_url={thumb_url} />
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
- <Image source={{ uri }} style={[styles.image, { height: size, width: size }]} />
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
  };
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useMemo, useState } from 'react';
2
2
 
3
- import { Image, StyleSheet, View } from 'react-native';
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
- <Image
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
- requestAnimationFrame(async () => {
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';
@@ -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 { Image, ImageProps, TextInputProps } from 'react-native';
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: Image as React.ComponentType<ImageProps>,
321
+ ImageComponent: SvgAwareImage,
321
322
  };
322
323
 
323
324
  /**
@@ -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
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "9.3.0-beta.2"
2
+ "version": "9.3.0-beta.4"
3
3
  }