Skip to content

Commit 257295c

Browse files
committed
chore: resolve conflicts
2 parents db81e73 + 40dee7e commit 257295c

File tree

11 files changed

+350
-71
lines changed

11 files changed

+350
-71
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
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_EXTERNAL_STORAGE" />
6-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
75
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
86
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
97

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: 87 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, Event, LocalMessage, MessageResponse } from 'stream-chat';
1414

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

107+
const hasReadLastMessage = (channel: Channel, userId: string) => {
108+
const latestMessageIdInChannel = channel.state.latestMessages.slice(-1)[0]?.id;
109+
const lastReadMessageIdServer = channel.state.read[userId]?.last_read_message_id;
110+
return latestMessageIdInChannel === lastReadMessageIdServer;
111+
};
112+
113+
const getPreviousLastMessage = (messages: MessageType[], newMessage?: MessageResponse) => {
114+
if (!newMessage) return;
115+
let previousLastMessage;
116+
for (let i = messages.length - 1; i >= 0; i--) {
117+
const msg = messages[i];
118+
if (!msg?.id) break;
119+
if (msg.id !== newMessage.id) {
120+
previousLastMessage = msg;
121+
break;
122+
}
123+
}
124+
return previousLastMessage;
125+
};
126+
107127
type MessageListPropsWithContext = Pick<
108128
AttachmentPickerContextValue,
109129
'closePicker' | 'selectedPicker' | 'setSelectedPicker'
@@ -123,6 +143,7 @@ type MessageListPropsWithContext = Pick<
123143
| 'NetworkDownIndicator'
124144
| 'reloadChannel'
125145
| 'scrollToFirstUnreadThreshold'
146+
| 'setChannelUnreadState'
126147
| 'setTargetedMessage'
127148
| 'StickyHeader'
128149
| 'targetedMessage'
@@ -264,6 +285,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
264285
reloadChannel,
265286
ScrollToBottomButton,
266287
selectedPicker,
288+
setChannelUnreadState,
267289
setFlatListRef,
268290
setMessages,
269291
setSelectedPicker,
@@ -409,19 +431,32 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
409431
const lastItem = viewableItems[viewableItems.length - 1];
410432

411433
if (lastItem) {
412-
const lastItemCreatedAt = lastItem.item.created_at;
434+
const lastItemMessage = lastItem.item;
435+
const lastItemCreatedAt = lastItemMessage.created_at;
413436

414437
const unreadIndicatorDate = channelUnreadState?.last_read.getTime();
415438
const lastItemDate = lastItemCreatedAt.getTime();
416439

417440
if (
418441
!channel.state.messagePagination.hasPrev &&
419-
processedMessageList[processedMessageList.length - 1].id === lastItem.item.id
442+
processedMessageList[processedMessageList.length - 1].id === lastItemMessage.id
443+
) {
444+
setIsUnreadNotificationOpen(false);
445+
return;
446+
}
447+
/**
448+
* This is a special case where there is a single long message by the sender.
449+
* When a message is sent, we mark it as read before it actually has a `created_at` timestamp.
450+
* This is a workaround to prevent the unread indicator from showing when the message is sent.
451+
*/
452+
if (
453+
viewableItems.length === 1 &&
454+
channel.countUnread() === 0 &&
455+
lastItemMessage.user.id === client.userID
420456
) {
421457
setIsUnreadNotificationOpen(false);
422458
return;
423459
}
424-
425460
if (unreadIndicatorDate && lastItemDate > unreadIndicatorDate) {
426461
setIsUnreadNotificationOpen(true);
427462
} else {
@@ -476,19 +511,51 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
476511
* Effect to mark the channel as read when the user scrolls to the bottom of the message list.
477512
*/
478513
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;
514+
const shouldMarkRead = () => {
515+
return (
516+
!channelUnreadState?.first_unread_message_id &&
517+
!scrollToBottomButtonVisible &&
518+
client.user?.id &&
519+
!hasReadLastMessage(channel, client.user?.id)
520+
);
521+
};
482522

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

488547
return () => {
489548
listener?.unsubscribe();
490549
};
491-
}, [channel, markRead, scrollToBottomButtonVisible]);
550+
}, [
551+
channel,
552+
channelUnreadState?.first_unread_message_id,
553+
client.user?.id,
554+
markRead,
555+
scrollToBottomButtonVisible,
556+
setChannelUnreadState,
557+
threadList,
558+
]);
492559

493560
useEffect(() => {
494561
const lastReceivedMessage = getLastReceivedMessage(processedMessageList);
@@ -886,6 +953,13 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
886953
}
887954

888955
setScrollToBottomButtonVisible(false);
956+
/**
957+
* When we are not in the bottom of the list, and we receive new messages, we need to mark the channel as read.
958+
We would still need to show the unread label, where the first unread message appeared so we don't update the channelUnreadState.
959+
*/
960+
await markRead({
961+
updateChannelUnreadState: false,
962+
});
889963
};
890964

891965
const scrollToIndexFailedRetryCountRef = useRef<number>(0);
@@ -1191,6 +1265,7 @@ export const MessageList = (props: MessageListProps) => {
11911265
NetworkDownIndicator,
11921266
reloadChannel,
11931267
scrollToFirstUnreadThreshold,
1268+
setChannelUnreadState,
11941269
setTargetedMessage,
11951270
StickyHeader,
11961271
targetedMessage,
@@ -1255,6 +1330,7 @@ export const MessageList = (props: MessageListProps) => {
12551330
ScrollToBottomButton,
12561331
scrollToFirstUnreadThreshold,
12571332
selectedPicker,
1333+
setChannelUnreadState,
12581334
setMessages,
12591335
setSelectedPicker,
12601336
setTargetedMessage,

0 commit comments

Comments
 (0)