Skip to content

Commit e3d2bc4

Browse files
committed
Merge branch 'develop' into V7
# Conflicts: # examples/ExpoMessaging/yarn.lock # examples/SampleApp/ios/Podfile.lock # examples/SampleApp/yarn.lock # examples/TypeScriptMessaging/yarn.lock # package/expo-package/package.json # package/expo-package/yarn.lock # package/native-package/package.json # package/native-package/yarn.lock # package/src/components/Channel/Channel.tsx # package/src/components/MessageList/MessageList.tsx # package/src/components/Thread/components/ThreadFooterComponent.tsx
2 parents eae044b + 3e49044 commit e3d2bc4

File tree

14 files changed

+377
-78
lines changed

14 files changed

+377
-78
lines changed

examples/SampleApp/android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<uses-permission android:name="android.permission.INTERNET" />
33
<uses-permission android:name="android.permission.CAMERA" />
44
<uses-permission android:name="android.permission.RECORD_AUDIO" />
5+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
6+
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
57

68
<queries>
79
<intent>

examples/SampleApp/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,7 +1433,7 @@ PODS:
14331433
- ReactCommon/turbomodule/bridging
14341434
- ReactCommon/turbomodule/core
14351435
- Yoga
1436-
- react-native-cameraroll (7.9.0):
1436+
- react-native-cameraroll (7.10.0):
14371437
- DoubleConversion
14381438
- glog
14391439
- hermes-engine
@@ -2213,7 +2213,7 @@ PODS:
22132213
- libwebp (~> 1.0)
22142214
- SDWebImage/Core (~> 5.10)
22152215
- SocketRocket (0.7.1)
2216-
- stream-chat-react-native (6.6.7):
2216+
- stream-chat-react-native (6.6.8):
22172217
- DoubleConversion
22182218
- glog
22192219
- hermes-engine

examples/SampleApp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"@notifee/react-native": "^9.1.8",
2727
"@op-engineering/op-sqlite": "^11.4.7",
2828
"@react-native-async-storage/async-storage": "^2.1.1",
29-
"@react-native-camera-roll/camera-roll": "^7.9.0",
29+
"@react-native-camera-roll/camera-roll": "^7.10.0",
3030
"@react-native-community/netinfo": "^11.4.1",
3131
"@react-native-documents/picker": "^10.1.1",
3232
"@react-native-firebase/app": "21.11.0",

examples/TypeScriptMessaging/ios/Podfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,7 +2039,7 @@ PODS:
20392039
- ReactCommon/turbomodule/core
20402040
- Yoga
20412041
- SocketRocket (0.7.1)
2042-
- stream-chat-react-native (6.6.7):
2042+
- stream-chat-react-native (6.6.8):
20432043
- DoubleConversion
20442044
- glog
20452045
- hermes-engine
@@ -2322,7 +2322,7 @@ EXTERNAL SOURCES:
23222322
:path: "../node_modules/react-native/ReactCommon/yoga"
23232323

23242324
SPEC CHECKSUMS:
2325-
boost: 1dca942403ed9342f98334bf4c3621f011aa7946
2325+
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
23262326
DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385
23272327
fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6
23282328
FBLazyVector: 6fe148afcef2e3213e484758e3459609d40d57f5
@@ -2404,7 +2404,7 @@ SPEC CHECKSUMS:
24042404
RNShare: 56b5431c60e1e9ee167191f4f327471af1c2941a
24052405
RNSVG: 8126581b369adf6a0004b6a6cab1a55e3002d5b0
24062406
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
2407-
stream-chat-react-native: f2cbd02c0a9838bb512b223d511267d2df5312bc
2407+
stream-chat-react-native: f394c3690415a9ad3c3ab741da3f0e72660de074
24082408
Yoga: afd04ff05ebe0121a00c468a8a3c8080221cb14c
24092409

24102410
PODFILE CHECKSUM: 6b7a4b74915b42bfe4ffddaf67cbf5e7a2bfeab3

package/CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# Change Log
22

3+
### [6.7.1](https://github.com/GetStream/stream-chat-react-native/compare/v6.7.0...v6.7.1) (2025-03-27)
4+
5+
6+
### Bug Fixes
7+
8+
* audio component recursive loading for expo ([#3038](https://github.com/GetStream/stream-chat-react-native/issues/3038)) ([57f67e7](https://github.com/GetStream/stream-chat-react-native/commit/57f67e752ab3ec5d6f4ffb83e36b648d4341a9fd))
9+
* thread list variety bugs ([#3033](https://github.com/GetStream/stream-chat-react-native/issues/3033)) ([ef46aac](https://github.com/GetStream/stream-chat-react-native/commit/ef46aac5f6b1679e5c503edcf546415003c70d3e))
10+
* unread indicator label presence in message list ([#3031](https://github.com/GetStream/stream-chat-react-native/issues/3031)) ([53eeafb](https://github.com/GetStream/stream-chat-react-native/commit/53eeafb8b8915f0a2c765b83e525ee86ea0af64f))
11+
12+
13+
### Refactors
14+
15+
* remove redundant console.log ([2a9d168](https://github.com/GetStream/stream-chat-react-native/commit/2a9d168b7439aa0dc5b4cb2a9663440863a58810))
16+
* remove redundant console.log ([f076c89](https://github.com/GetStream/stream-chat-react-native/commit/f076c899dadba5639e3e347b0d680d454e6d9f32))
17+
18+
## [6.7.0](https://github.com/GetStream/stream-chat-react-native/compare/v6.6.8...v6.7.0) (2025-03-17)
19+
20+
21+
### Features
22+
23+
* add support for react compiler on RN 0.77 ([#3024](https://github.com/GetStream/stream-chat-react-native/issues/3024)) ([6758d06](https://github.com/GetStream/stream-chat-react-native/commit/6758d06ec629e9779c536e39b22dfd933ebf5b27))
24+
25+
26+
### Bug Fixes
27+
28+
* show correct read status on the message status component for a message ([#3023](https://github.com/GetStream/stream-chat-react-native/issues/3023)) ([6a1e9f6](https://github.com/GetStream/stream-chat-react-native/commit/6a1e9f6e499324a3f97cced0fd68b5e30cc1145b))
29+
330
### [6.6.8](https://github.com/GetStream/stream-chat-react-native/compare/v6.6.7...v6.6.8) (2025-03-13)
431

532

package/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "stream-chat-react-native-core",
33
"description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
4-
"version": "6.6.8",
4+
"version": "6.7.1",
55
"author": {
66
"company": "Stream.io Inc",
77
"name": "Stream.io Inc"
@@ -22,7 +22,7 @@
2222
"README.md"
2323
],
2424
"scripts": {
25-
"build": "rm -rf lib && yarn run --silent build-translations && bob build && yarn run --silent copy-translations",
25+
"build": "rimraf lib && yarn run --silent build-translations && bob build && yarn run --silent copy-translations",
2626
"build-translations": "i18next",
2727
"copy-translations": "echo '\u001b[34mℹ\u001b[0m Copying translation files to \u001b[34mlib/typescript/i18n\u001b[0m' && cp -R -f ./src/i18n ./lib/typescript/i18n && echo '\u001b[32m✓\u001b[0m Done Copying Translations'",
2828
"eslint": "eslint 'src/**/*.{js,md,ts,jsx,tsx}' --max-warnings 0",
@@ -147,6 +147,7 @@
147147
"react-native-reanimated": "~3.17.1",
148148
"react-native-svg": "15.11.2",
149149
"react-test-renderer": "19.0.0",
150+
"rimraf": "^6.0.1",
150151
"typescript": "5.8.2",
151152
"typescript-eslint": "^8.25.0",
152153
"uuid": "^11.1.0"

package/src/components/Attachment/AudioAttachment.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,14 @@ export const AudioAttachment = (props: AudioAttachmentProps) => {
208208
if (!isExpoCLI) {
209209
return;
210210
}
211-
if (item.paused) {
212-
await pauseAudio();
213-
} else {
214-
await playAudio();
211+
try {
212+
if (item.paused) {
213+
await pauseAudio();
214+
} else {
215+
await playAudio();
216+
}
217+
} catch (e) {
218+
console.log('An error has occurred while trying to interact with the audio. ', e);
215219
}
216220
};
217221
// For expo CLI

package/src/components/Channel/Channel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,8 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
686686
const [deleted, setDeleted] = useState<boolean>(false);
687687
const [editing, setEditing] = useState<MessageType | undefined>(undefined);
688688
const [error, setError] = useState<Error | boolean>(false);
689-
const [lastRead, setLastRead] = useState<ChannelContextValue['lastRead']>();
689+
const [lastRead, setLastRead] = useState<Date | undefined>();
690+
690691
const [quotedMessage, setQuotedMessage] = useState<MessageType | undefined>(undefined);
691692
const [thread, setThread] = useState<MessageType | null>(threadProps || null);
692693
const [threadHasMore, setThreadHasMore] = useState(true);
@@ -949,6 +950,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
949950
last_read_message_id: response?.event.last_read_message_id,
950951
unread_messages: 0,
951952
});
953+
setLastRead(new Date());
952954
}
953955
} catch (err) {
954956
console.log('Error marking channel as read:', err);
@@ -1360,8 +1362,6 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
13601362
} else {
13611363
updateMessage(messageResponse.message);
13621364
}
1363-
1364-
threadInstance?.upsertReplyLocally?.({ message: messageResponse.message });
13651365
}
13661366
} catch (err) {
13671367
console.log(err);

package/src/components/MessageList/MessageList.tsx

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
ViewToken,
1111
} from 'react-native';
1212

13-
import type { LocalMessage } from 'stream-chat';
13+
import type { Channel, LocalMessage } from 'stream-chat';
1414

1515
import {
1616
isMessageWithStylesReadByAndDateSeparator,
@@ -104,6 +104,32 @@ const flatListViewabilityConfig: ViewabilityConfig = {
104104
viewAreaCoveragePercentThreshold: 1,
105105
};
106106

107+
const hasReadLastMessage = (
108+
channel: Channel,
109+
userId: string,
110+
) => {
111+
const latestMessageIdInChannel = channel.state.latestMessages.slice(-1)[0]?.id;
112+
const lastReadMessageIdServer = channel.state.read[userId]?.last_read_message_id;
113+
return latestMessageIdInChannel === lastReadMessageIdServer;
114+
};
115+
116+
const getPreviousLastMessage = (
117+
messages: MessageType[],
118+
newMessage?: MessageResponse,
119+
) => {
120+
if (!newMessage) return;
121+
let previousLastMessage;
122+
for (let i = messages.length - 1; i >= 0; i--) {
123+
const msg = messages[i];
124+
if (!msg?.id) break;
125+
if (msg.id !== newMessage.id) {
126+
previousLastMessage = msg;
127+
break;
128+
}
129+
}
130+
return previousLastMessage;
131+
};
132+
107133
type MessageListPropsWithContext = Pick<
108134
AttachmentPickerContextValue,
109135
'closePicker' | 'selectedPicker' | 'setSelectedPicker'
@@ -123,6 +149,7 @@ type MessageListPropsWithContext = Pick<
123149
| 'NetworkDownIndicator'
124150
| 'reloadChannel'
125151
| 'scrollToFirstUnreadThreshold'
152+
| 'setChannelUnreadState'
126153
| 'setTargetedMessage'
127154
| 'StickyHeader'
128155
| 'targetedMessage'
@@ -264,6 +291,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
264291
reloadChannel,
265292
ScrollToBottomButton,
266293
selectedPicker,
294+
setChannelUnreadState,
267295
setFlatListRef,
268296
setMessages,
269297
setSelectedPicker,
@@ -409,19 +437,32 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
409437
const lastItem = viewableItems[viewableItems.length - 1];
410438

411439
if (lastItem) {
412-
const lastItemCreatedAt = lastItem.item.created_at;
440+
const lastItemMessage = lastItem.item;
441+
const lastItemCreatedAt = lastItemMessage.created_at;
413442

414443
const unreadIndicatorDate = channelUnreadState?.last_read.getTime();
415444
const lastItemDate = lastItemCreatedAt.getTime();
416445

417446
if (
418447
!channel.state.messagePagination.hasPrev &&
419-
processedMessageList[processedMessageList.length - 1].id === lastItem.item.id
448+
processedMessageList[processedMessageList.length - 1].id === lastItemMessage.id
449+
) {
450+
setIsUnreadNotificationOpen(false);
451+
return;
452+
}
453+
/**
454+
* This is a special case where there is a single long message by the sender.
455+
* When a message is sent, we mark it as read before it actually has a `created_at` timestamp.
456+
* This is a workaround to prevent the unread indicator from showing when the message is sent.
457+
*/
458+
if (
459+
viewableItems.length === 1 &&
460+
channel.countUnread() === 0 &&
461+
lastItemMessage.user.id === client.userID
420462
) {
421463
setIsUnreadNotificationOpen(false);
422464
return;
423465
}
424-
425466
if (unreadIndicatorDate && lastItemDate > unreadIndicatorDate) {
426467
setIsUnreadNotificationOpen(true);
427468
} else {
@@ -476,19 +517,54 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
476517
* Effect to mark the channel as read when the user scrolls to the bottom of the message list.
477518
*/
478519
useEffect(() => {
479-
const listener: ReturnType<typeof channel.on> = channel.on('message.new', (event) => {
480-
const newMessageToCurrentChannel = event.cid === channel.cid;
481-
const mainChannelUpdated = !event.message?.parent_id || event.message?.show_in_channel;
520+
const shouldMarkRead = () => {
521+
return (
522+
!channelUnreadState?.first_unread_message_id &&
523+
!scrollToBottomButtonVisible &&
524+
client.user?.id &&
525+
!hasReadLastMessage(channel, client.user?.id)
526+
);
527+
};
482528

483-
if (newMessageToCurrentChannel && mainChannelUpdated && !scrollToBottomButtonVisible) {
484-
markRead();
529+
const handleEvent = async (event: Event<StreamChatGenerics>) => {
530+
const mainChannelUpdated = !event.message?.parent_id || event.message?.show_in_channel;
531+
// When the scrollToBottomButtonVisible is true, we need to manually update the channelUnreadState.
532+
if (scrollToBottomButtonVisible || channelUnreadState?.first_unread_message_id) {
533+
setChannelUnreadState((prev) => {
534+
const previousUnreadCount = prev?.unread_messages ?? 0;
535+
const previousLastMessage = getPreviousLastMessage<StreamChatGenerics>(
536+
channel.state.messages,
537+
event.message,
538+
);
539+
return {
540+
...(prev || {}),
541+
last_read:
542+
prev?.last_read ??
543+
(previousUnreadCount === 0 && previousLastMessage?.created_at
544+
? new Date(previousLastMessage.created_at)
545+
: new Date(0)), // not having information about the last read message means the whole channel is unread,
546+
unread_messages: previousUnreadCount + 1,
547+
};
548+
});
549+
} else if (mainChannelUpdated && shouldMarkRead()) {
550+
await markRead();
485551
}
486-
});
552+
};
553+
554+
const listener: ReturnType<typeof channel.on> = channel.on('message.new', handleEvent);
487555

488556
return () => {
489557
listener?.unsubscribe();
490558
};
491-
}, [channel, markRead, scrollToBottomButtonVisible]);
559+
}, [
560+
channel,
561+
channelUnreadState?.first_unread_message_id,
562+
client.user?.id,
563+
markRead,
564+
scrollToBottomButtonVisible,
565+
setChannelUnreadState,
566+
threadList,
567+
]);
492568

493569
useEffect(() => {
494570
const lastReceivedMessage = getLastReceivedMessage(processedMessageList);
@@ -886,6 +962,13 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
886962
}
887963

888964
setScrollToBottomButtonVisible(false);
965+
/**
966+
* When we are not in the bottom of the list, and we receive new messages, we need to mark the channel as read.
967+
We would still need to show the unread label, where the first unread message appeared so we don't update the channelUnreadState.
968+
*/
969+
await markRead({
970+
updateChannelUnreadState: false,
971+
});
889972
};
890973

891974
const scrollToIndexFailedRetryCountRef = useRef<number>(0);
@@ -1191,6 +1274,7 @@ export const MessageList = (props: MessageListProps) => {
11911274
NetworkDownIndicator,
11921275
reloadChannel,
11931276
scrollToFirstUnreadThreshold,
1277+
setChannelUnreadState,
11941278
setTargetedMessage,
11951279
StickyHeader,
11961280
targetedMessage,
@@ -1255,6 +1339,7 @@ export const MessageList = (props: MessageListProps) => {
12551339
ScrollToBottomButton,
12561340
scrollToFirstUnreadThreshold,
12571341
selectedPicker,
1342+
setChannelUnreadState,
12581343
setMessages,
12591344
setSelectedPicker,
12601345
setTargetedMessage,

0 commit comments

Comments
 (0)