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.
Files changed (39) 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/utils/utils.js.map +1 -1
  10. package/lib/commonjs/version.json +1 -1
  11. package/lib/module/components/Attachment/Gallery.js +9 -49
  12. package/lib/module/components/Attachment/Gallery.js.map +1 -1
  13. package/lib/module/components/Attachment/GalleryImage.js +65 -5
  14. package/lib/module/components/Attachment/GalleryImage.js.map +1 -1
  15. package/lib/module/components/Attachment/ImageLoadingIndicator.js +2 -14
  16. package/lib/module/components/Attachment/ImageLoadingIndicator.js.map +1 -1
  17. package/lib/module/components/Attachment/VideoThumbnail.js +18 -16
  18. package/lib/module/components/Attachment/VideoThumbnail.js.map +1 -1
  19. package/lib/module/utils/utils.js.map +1 -1
  20. package/lib/module/version.json +1 -1
  21. package/lib/typescript/components/Attachment/Gallery.d.ts.map +1 -1
  22. package/lib/typescript/components/Attachment/GalleryImage.d.ts +8 -1
  23. package/lib/typescript/components/Attachment/GalleryImage.d.ts.map +1 -1
  24. package/lib/typescript/components/Attachment/ImageLoadingIndicator.d.ts +1 -1
  25. package/lib/typescript/components/Attachment/ImageLoadingIndicator.d.ts.map +1 -1
  26. package/lib/typescript/components/Attachment/VideoThumbnail.d.ts.map +1 -1
  27. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +1 -1
  28. package/lib/typescript/hooks/useTranslatedMessage.d.ts +6 -2
  29. package/lib/typescript/hooks/useTranslatedMessage.d.ts.map +1 -1
  30. package/lib/typescript/utils/utils.d.ts +2 -2
  31. package/lib/typescript/utils/utils.d.ts.map +1 -1
  32. package/package.json +2 -2
  33. package/src/components/Attachment/Gallery.tsx +9 -50
  34. package/src/components/Attachment/GalleryImage.tsx +90 -4
  35. package/src/components/Attachment/ImageLoadingIndicator.tsx +3 -15
  36. package/src/components/Attachment/VideoThumbnail.tsx +22 -9
  37. package/src/components/Attachment/__tests__/VideoThumbnail.test.tsx +44 -0
  38. package/src/utils/utils.ts +4 -4
  39. 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;;;;;;;;;;;;;;;;;;;;;;;YAkBy/jB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAAD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aADtkkB,CAAC"}
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":"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"}
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.0",
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 { ImageErrorEvent, Pressable, StyleSheet, Text, View } from 'react-native';
2
+ import { Pressable, StyleSheet, Text, View } from 'react-native';
3
3
 
4
4
  import type { Attachment, LocalMessage } from 'stream-chat';
5
5
 
6
- import { GalleryImage } from './GalleryImage';
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
- <View style={[styles.image, borderRadius]}>
367
- {isLoadingImageError ? (
368
- <ImageLoadingFailedIndicator onReloadImage={onReloadImage} />
369
- ) : (
370
- <>
371
- <GalleryImage
372
- onError={onError}
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 { 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
+ });
@@ -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
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "9.1.2"
2
+ "version": "9.1.3-beta.2"
3
3
  }