diff --git a/README.md b/README.md
index fe06e4b4b4..d466b59f5b 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
[](https://www.npmjs.com/package/stream-chat-react-native)
[](https://github.com/GetStream/stream-chat-react-native/actions)
[](https://getstream.io/chat/docs/sdk/reactnative)
-
+
diff --git a/package/expo-package/src/optionalDependencies/getLocalAssetUri.ts b/package/expo-package/src/optionalDependencies/getLocalAssetUri.ts
index 5f51479f79..362e0904da 100644
--- a/package/expo-package/src/optionalDependencies/getLocalAssetUri.ts
+++ b/package/expo-package/src/optionalDependencies/getLocalAssetUri.ts
@@ -3,9 +3,13 @@ import { Platform } from 'react-native';
let MediaLibrary;
try {
- MediaLibrary = require('expo-media-library');
+ MediaLibrary = require('expo-media-library/legacy');
} catch (e) {
- // do nothing
+ try {
+ MediaLibrary = require('expo-media-library');
+ } catch (e) {
+ // do nothing
+ }
}
if (!MediaLibrary) {
diff --git a/package/expo-package/src/optionalDependencies/getPhotos.ts b/package/expo-package/src/optionalDependencies/getPhotos.ts
index 0e4f2bd728..366ddc112d 100644
--- a/package/expo-package/src/optionalDependencies/getPhotos.ts
+++ b/package/expo-package/src/optionalDependencies/getPhotos.ts
@@ -10,9 +10,13 @@ import { getLocalAssetUri } from './getLocalAssetUri';
let MediaLibrary;
try {
- MediaLibrary = require('expo-media-library');
+ MediaLibrary = require('expo-media-library/legacy');
} catch (e) {
- // do nothing
+ try {
+ MediaLibrary = require('expo-media-library');
+ } catch (e) {
+ // do nothing
+ }
}
if (!MediaLibrary) {
diff --git a/package/expo-package/src/optionalDependencies/iOS14RefreshGallerySelection.ts b/package/expo-package/src/optionalDependencies/iOS14RefreshGallerySelection.ts
index 49bfe503cc..fd0dff44e4 100644
--- a/package/expo-package/src/optionalDependencies/iOS14RefreshGallerySelection.ts
+++ b/package/expo-package/src/optionalDependencies/iOS14RefreshGallerySelection.ts
@@ -3,9 +3,13 @@ import { Platform } from 'react-native';
let MediaLibrary;
try {
- MediaLibrary = require('expo-media-library');
+ MediaLibrary = require('expo-media-library/legacy');
} catch (e) {
- // do nothing
+ try {
+ MediaLibrary = require('expo-media-library');
+ } catch (e) {
+ // do nothing
+ }
}
if (!MediaLibrary) {
diff --git a/package/expo-package/src/optionalDependencies/oniOS14GalleryLibrarySelectionChange.ts b/package/expo-package/src/optionalDependencies/oniOS14GalleryLibrarySelectionChange.ts
index 89721ac6a3..de2be0f0a5 100644
--- a/package/expo-package/src/optionalDependencies/oniOS14GalleryLibrarySelectionChange.ts
+++ b/package/expo-package/src/optionalDependencies/oniOS14GalleryLibrarySelectionChange.ts
@@ -3,9 +3,13 @@ import { Platform } from 'react-native';
let MediaLibrary;
try {
- MediaLibrary = require('expo-media-library');
+ MediaLibrary = require('expo-media-library/legacy');
} catch (e) {
- // do nothing
+ try {
+ MediaLibrary = require('expo-media-library');
+ } catch (e) {
+ // do nothing
+ }
}
if (!MediaLibrary) {
diff --git a/package/src/components/Attachment/FileAttachmentGroup.tsx b/package/src/components/Attachment/FileAttachmentGroup.tsx
index 84374798b3..d89cb934c3 100644
--- a/package/src/components/Attachment/FileAttachmentGroup.tsx
+++ b/package/src/components/Attachment/FileAttachmentGroup.tsx
@@ -17,7 +17,7 @@ export type FileAttachmentGroupPropsWithContext = Pick {
- const { files, message, styles: stylesProp = {} } = props;
+ const { files, styles: stylesProp = {} } = props;
const { Attachment } = useComponentsContext();
const {
@@ -32,7 +32,7 @@ const FileAttachmentGroupWithContext = (props: FileAttachmentGroupPropsWithConte
{files.map((file, index) => (
diff --git a/package/src/components/Attachment/Gallery.tsx b/package/src/components/Attachment/Gallery.tsx
index 5a1b0bd103..b675a24725 100644
--- a/package/src/components/Attachment/Gallery.tsx
+++ b/package/src/components/Attachment/Gallery.tsx
@@ -272,7 +272,7 @@ const GalleryThumbnail = ({
accessibilityLabel={thumbnailAccessibilityLabel}
accessibilityRole='button'
disabled={preventPress}
- key={`gallery-item-${message.id}/${colIndex}/${rowIndex}/${imagesAndVideos.length}`}
+ key={`gallery-item-${colIndex}/${rowIndex}`}
ref={thumbnailRef}
onLongPress={(event) => {
if (onLongPress) {
diff --git a/package/src/components/Message/MessageItemView/MessageContent.tsx b/package/src/components/Message/MessageItemView/MessageContent.tsx
index 716dd4d651..8f699f06ee 100644
--- a/package/src/components/Message/MessageItemView/MessageContent.tsx
+++ b/package/src/components/Message/MessageItemView/MessageContent.tsx
@@ -281,7 +281,7 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => {
);
case 'attachments':
return otherAttachments.map((attachment, attachmentIndex) => (
-
+
));
case 'files':
return (
@@ -297,7 +297,7 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => {
const pollId = message.poll_id;
const poll = pollId && client.polls.fromState(pollId);
return pollId && poll ? (
-
+
) : null;
}
case 'location':
diff --git a/package/src/components/MessageList/MessageFlashList.tsx b/package/src/components/MessageList/MessageFlashList.tsx
index 7c6d75a341..8eebd18cb1 100644
--- a/package/src/components/MessageList/MessageFlashList.tsx
+++ b/package/src/components/MessageList/MessageFlashList.tsx
@@ -55,9 +55,11 @@ import { mergeThemes, useTheme } from '../../contexts/themeContext/ThemeContext'
import { ThreadContextValue, useThreadContext } from '../../contexts/threadContext/ThreadContext';
import { useStableCallback, useStateStore } from '../../hooks';
+import { isVideoPlayerAvailable } from '../../native';
import { bumpOverlayLayoutRevision, useHasActiveId } from '../../state-store';
import { MessageInputHeightState } from '../../state-store/message-input-height-store';
import { primitives } from '../../theme';
+import { FileTypes } from '../../types/types';
import { transitions } from '../../utils/animations/transitions';
import { MessageWrapper } from '../Message/MessageItemView/MessageWrapper';
import { excludeCanceledUploadNotifications } from '../Notifications/notificationFilters';
@@ -222,10 +224,61 @@ type MessageFlashListPropsWithContext = Pick<
const WAIT_FOR_SCROLL_TIMEOUT = 0;
+// Classify an attachment bearing message by its primary shape so FlashList only
+// recycles same shaped cells (means less work to rerender). Gallery/media is the
+// heaviest subtree to mount, so we short circuit to it as soon as we see one gallery
+// image/video nad this keeps gallery cells recycling only with other gallery cells,
+// so the Gallery subtree reconciles on rebind instead of unmount & remount. Mirrors
+// the attachment categorization in Message.
+const getAttachmentItemType = (message: LocalMessage) => {
+ const attachments = message.attachments ?? [];
+ let hasGiphy = false;
+ let hasAudio = false;
+ let hasFile = false;
+ let hasCard = false;
+ for (const attachment of attachments) {
+ const isGalleryImage =
+ attachment.type === FileTypes.Image &&
+ !attachment.og_scrape_url &&
+ !attachment.title_link &&
+ (!!attachment.image_url || !!attachment.thumb_url);
+ const isGalleryVideo =
+ attachment.type === FileTypes.Video && !attachment.og_scrape_url && isVideoPlayerAvailable();
+ if (isGalleryImage || isGalleryVideo) {
+ return 'message-with-gallery';
+ }
+ if (attachment.type === FileTypes.Giphy) {
+ hasGiphy = true;
+ } else if (
+ attachment.type === FileTypes.Audio ||
+ attachment.type === FileTypes.VoiceRecording
+ ) {
+ hasAudio = true;
+ } else if (attachment.type === FileTypes.File) {
+ hasFile = true;
+ } else if (attachment.og_scrape_url || attachment.title_link) {
+ hasCard = true;
+ }
+ }
+ if (hasGiphy) {
+ return 'message-with-giphy';
+ }
+ if (hasAudio) {
+ return 'message-with-audio';
+ }
+ if (hasFile) {
+ return 'message-with-file';
+ }
+ if (hasCard) {
+ return 'message-with-card';
+ }
+ return 'message-with-attachments';
+};
+
const getItemTypeInternal = (message: LocalMessage) => {
if (message.type === 'regular') {
if ((message.attachments?.length ?? 0) > 0) {
- return 'message-with-attachments';
+ return getAttachmentItemType(message);
}
if (message.poll_id) {