stream-chat-react-native-core 9.5.2-beta.4 → 9.6.0-beta.1
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.
- package/README.md +1 -1
- package/lib/commonjs/components/Channel/Channel.js +37 -25
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js +1 -6
- package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js +8 -5
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageFlashList.js +2 -1
- package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageList.js +2 -1
- package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
- package/lib/commonjs/contexts/channelContext/ChannelContext.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Channel/Channel.js +37 -25
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/hooks/useCreateChannelContext.js +1 -6
- package/lib/module/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js +8 -5
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
- package/lib/module/components/MessageList/MessageFlashList.js +2 -1
- package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +2 -1
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/contexts/channelContext/ChannelContext.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useCreateChannelContext.d.ts +1 -1
- package/lib/typescript/components/Channel/hooks/useCreateChannelContext.d.ts.map +1 -1
- package/lib/typescript/components/ChannelPreview/hooks/useChannelPreviewData.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageFlashList.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
- package/lib/typescript/contexts/channelContext/ChannelContext.d.ts +0 -2
- package/lib/typescript/contexts/channelContext/ChannelContext.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/Channel/Channel.tsx +33 -10
- package/src/components/Channel/hooks/useCreateChannelContext.ts +0 -6
- package/src/components/ChannelPreview/hooks/useChannelPreviewData.ts +8 -2
- package/src/components/MessageList/MessageFlashList.tsx +6 -1
- package/src/components/MessageList/MessageList.tsx +6 -1
- package/src/components/Thread/__tests__/Thread.test.tsx +5 -3
- package/src/contexts/channelContext/ChannelContext.tsx +0 -3
- package/src/version.json +1 -1
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.
|
|
4
|
+
"version": "9.6.0-beta.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"company": "Stream.io Inc",
|
|
7
7
|
"name": "Stream.io Inc"
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"path": "0.12.7",
|
|
79
79
|
"react-native-markdown-package": "1.8.2",
|
|
80
80
|
"react-native-url-polyfill": "^2.0.0",
|
|
81
|
-
"stream-chat": "^9.
|
|
81
|
+
"stream-chat": "^9.50.0",
|
|
82
82
|
"use-sync-external-store": "^1.5.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
@@ -511,12 +511,12 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
|
|
|
511
511
|
const styles = useStyles();
|
|
512
512
|
const [deleted, setDeleted] = useState<boolean>(false);
|
|
513
513
|
const [error, setError] = useState<Error | boolean>(false);
|
|
514
|
-
const
|
|
514
|
+
const lastReadRef = useRef<Date | undefined>(undefined);
|
|
515
515
|
const [thread, setThread] = useState<LocalMessage | null>(threadProps || null);
|
|
516
516
|
const [threadHasMore, setThreadHasMore] = useState(true);
|
|
517
517
|
const [threadLoadingMore, setThreadLoadingMore] = useState(false);
|
|
518
|
-
const [channelUnreadStateStore] = useState(new ChannelUnreadStateStore());
|
|
519
|
-
const [messageInputHeightStore] = useState(new MessageInputHeightStore());
|
|
518
|
+
const [channelUnreadStateStore] = useState(() => new ChannelUnreadStateStore());
|
|
519
|
+
const [messageInputHeightStore] = useState(() => new MessageInputHeightStore());
|
|
520
520
|
// TODO: Think if we can remove this and just rely on the channelUnreadStateStore everywhere.
|
|
521
521
|
const setChannelUnreadState = useCallback(
|
|
522
522
|
(data: ChannelUnreadStateStoreType['channelUnreadState']) => {
|
|
@@ -690,6 +690,13 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
|
|
|
690
690
|
return;
|
|
691
691
|
}
|
|
692
692
|
|
|
693
|
+
if (event.type === 'message.read_locally') {
|
|
694
|
+
// When local unread reset happens, the count is already updated in the client state,
|
|
695
|
+
// and the preview badge / unread divider are handled elsewhere, so there is nothing
|
|
696
|
+
// to copy into channel state here. Thus, we skip it.
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
|
|
693
700
|
if (event.type === 'message.read' || event.type === 'notification.mark_read') {
|
|
694
701
|
setReadThrottled();
|
|
695
702
|
return;
|
|
@@ -703,7 +710,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
|
|
|
703
710
|
useEffect(() => {
|
|
704
711
|
let listener: ReturnType<typeof channel.on>;
|
|
705
712
|
const initChannel = async () => {
|
|
706
|
-
|
|
713
|
+
lastReadRef.current = new Date();
|
|
707
714
|
const unreadCount = channel.countUnread();
|
|
708
715
|
const shouldLoadAtFirstUnread = shouldLoadInitialChannelAtFirstUnreadMessage(unreadCount);
|
|
709
716
|
if (!channel || !shouldSyncChannel) {
|
|
@@ -812,7 +819,25 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
|
|
|
812
819
|
const markReadInternal: ChannelContextValue['markRead'] = throttle(
|
|
813
820
|
async (options?: MarkReadFunctionOptions) => {
|
|
814
821
|
const { updateChannelUnreadState = true } = options ?? {};
|
|
815
|
-
if (!channel || channel?.disconnected
|
|
822
|
+
if (!channel || channel?.disconnected) {
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// When read events are disabled (e.g. livestreams) we cannot mark read on the backend. If the
|
|
827
|
+
// client opted into a local unread count, reset it locally instead so the user's "caught up"
|
|
828
|
+
// state is reflected without a server round trip.
|
|
829
|
+
if (!clientChannelConfig?.read_events) {
|
|
830
|
+
if (client.options.isLocalUnreadCountEnabled) {
|
|
831
|
+
const event = channel.markReadLocally();
|
|
832
|
+
if (updateChannelUnreadState && event && lastReadRef.current) {
|
|
833
|
+
setChannelUnreadState({
|
|
834
|
+
last_read: lastReadRef.current,
|
|
835
|
+
last_read_message_id: event.last_read_message_id,
|
|
836
|
+
unread_messages: 0,
|
|
837
|
+
});
|
|
838
|
+
lastReadRef.current = new Date();
|
|
839
|
+
}
|
|
840
|
+
}
|
|
816
841
|
return;
|
|
817
842
|
}
|
|
818
843
|
|
|
@@ -821,13 +846,13 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
|
|
|
821
846
|
} else {
|
|
822
847
|
try {
|
|
823
848
|
const response = await channel.markRead();
|
|
824
|
-
if (updateChannelUnreadState && response &&
|
|
849
|
+
if (updateChannelUnreadState && response && lastReadRef.current) {
|
|
825
850
|
setChannelUnreadState({
|
|
826
|
-
last_read:
|
|
851
|
+
last_read: lastReadRef.current,
|
|
827
852
|
last_read_message_id: response?.event.last_read_message_id,
|
|
828
853
|
unread_messages: 0,
|
|
829
854
|
});
|
|
830
|
-
|
|
855
|
+
lastReadRef.current = new Date();
|
|
831
856
|
}
|
|
832
857
|
} catch (err) {
|
|
833
858
|
console.log('Error marking channel as read:', err);
|
|
@@ -1578,7 +1603,6 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
|
|
|
1578
1603
|
hideStickyDateHeader,
|
|
1579
1604
|
highlightedMessageId,
|
|
1580
1605
|
isChannelActive: shouldSyncChannel,
|
|
1581
|
-
lastRead,
|
|
1582
1606
|
loadChannelAroundMessage,
|
|
1583
1607
|
loadChannelAtFirstUnreadMessage,
|
|
1584
1608
|
loading: channelMessagesState.loading,
|
|
@@ -1590,7 +1614,6 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
|
|
|
1590
1614
|
reloadChannel,
|
|
1591
1615
|
scrollToFirstUnreadThreshold,
|
|
1592
1616
|
setChannelUnreadState,
|
|
1593
|
-
setLastRead,
|
|
1594
1617
|
setTargetedMessage,
|
|
1595
1618
|
hasPendingInitialTargetLoad,
|
|
1596
1619
|
targetedMessage,
|
|
@@ -13,7 +13,6 @@ export const useCreateChannelContext = ({
|
|
|
13
13
|
hideStickyDateHeader,
|
|
14
14
|
highlightedMessageId,
|
|
15
15
|
isChannelActive,
|
|
16
|
-
lastRead,
|
|
17
16
|
loadChannelAroundMessage,
|
|
18
17
|
loadChannelAtFirstUnreadMessage,
|
|
19
18
|
loading,
|
|
@@ -25,7 +24,6 @@ export const useCreateChannelContext = ({
|
|
|
25
24
|
reloadChannel,
|
|
26
25
|
scrollToFirstUnreadThreshold,
|
|
27
26
|
setChannelUnreadState,
|
|
28
|
-
setLastRead,
|
|
29
27
|
setTargetedMessage,
|
|
30
28
|
hasPendingInitialTargetLoad,
|
|
31
29
|
targetedMessage,
|
|
@@ -35,7 +33,6 @@ export const useCreateChannelContext = ({
|
|
|
35
33
|
watchers,
|
|
36
34
|
}: ChannelContextValue) => {
|
|
37
35
|
const channelId = channel?.id;
|
|
38
|
-
const lastReadTime = lastRead?.getTime();
|
|
39
36
|
const membersLength = Object.keys(members).length;
|
|
40
37
|
|
|
41
38
|
const readUsers = Object.values(read);
|
|
@@ -56,7 +53,6 @@ export const useCreateChannelContext = ({
|
|
|
56
53
|
hideStickyDateHeader,
|
|
57
54
|
highlightedMessageId,
|
|
58
55
|
isChannelActive,
|
|
59
|
-
lastRead,
|
|
60
56
|
loadChannelAroundMessage,
|
|
61
57
|
loadChannelAtFirstUnreadMessage,
|
|
62
58
|
loading,
|
|
@@ -68,7 +64,6 @@ export const useCreateChannelContext = ({
|
|
|
68
64
|
reloadChannel,
|
|
69
65
|
scrollToFirstUnreadThreshold,
|
|
70
66
|
setChannelUnreadState,
|
|
71
|
-
setLastRead,
|
|
72
67
|
setTargetedMessage,
|
|
73
68
|
hasPendingInitialTargetLoad,
|
|
74
69
|
targetedMessage,
|
|
@@ -84,7 +79,6 @@ export const useCreateChannelContext = ({
|
|
|
84
79
|
error,
|
|
85
80
|
isChannelActive,
|
|
86
81
|
highlightedMessageId,
|
|
87
|
-
lastReadTime,
|
|
88
82
|
loading,
|
|
89
83
|
membersLength,
|
|
90
84
|
readUsersLength,
|
|
@@ -109,8 +109,14 @@ export const useChannelPreviewData = (
|
|
|
109
109
|
setForceUpdate((prev) => prev + 1);
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
|
-
const
|
|
113
|
-
|
|
112
|
+
const readSubscription = client.on('message.read', handleReadEvent);
|
|
113
|
+
// `message.read_locally` is the client-only equivalent emitted by `channel.markReadLocally()` when
|
|
114
|
+
// read events are disabled (e.g. livestreams with `isLocalUnreadCountEnabled`).
|
|
115
|
+
const localReadSubscription = client.on('message.read_locally', handleReadEvent);
|
|
116
|
+
return () => {
|
|
117
|
+
readSubscription.unsubscribe();
|
|
118
|
+
localReadSubscription.unsubscribe();
|
|
119
|
+
};
|
|
114
120
|
}, [client, channel]);
|
|
115
121
|
|
|
116
122
|
/**
|
|
@@ -718,9 +718,14 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
|
|
|
718
718
|
const lastReadMessageId = channelUnreadState?.last_read_message_id;
|
|
719
719
|
const lastReadMessageVisible = viewableItems.some((item) => item.item.id === lastReadMessageId);
|
|
720
720
|
|
|
721
|
+
// Channels with disabled `read-events` (i.e livestreams) still surface the unread
|
|
722
|
+
// notification when the client opted into a local unread count, so the gate accepts
|
|
723
|
+
// either source.
|
|
724
|
+
const unreadNotificationSupported = readEvents || client.options.isLocalUnreadCountEnabled;
|
|
725
|
+
|
|
721
726
|
if (
|
|
722
727
|
!viewableItems.length ||
|
|
723
|
-
!
|
|
728
|
+
!unreadNotificationSupported ||
|
|
724
729
|
lastReadMessageVisible ||
|
|
725
730
|
attachmentPickerStore.state.getLatestValue().selectedPicker === 'images'
|
|
726
731
|
) {
|
|
@@ -555,9 +555,14 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
|
|
|
555
555
|
(item) => item.item.message.id === lastReadMessageId,
|
|
556
556
|
);
|
|
557
557
|
|
|
558
|
+
// Channels with disabled `read-events` (i.e livestreams) still surface the unread
|
|
559
|
+
// notification when the client opted into a local unread count, so the gate accepts
|
|
560
|
+
// either source.
|
|
561
|
+
const unreadNotificationSupported = readEvents || client.options.isLocalUnreadCountEnabled;
|
|
562
|
+
|
|
558
563
|
if (
|
|
559
564
|
!viewableItems.length ||
|
|
560
|
-
!
|
|
565
|
+
!unreadNotificationSupported ||
|
|
561
566
|
lastReadMessageVisible ||
|
|
562
567
|
attachmentPickerStore.state.getLatestValue().selectedPicker === 'images'
|
|
563
568
|
) {
|
|
@@ -142,7 +142,9 @@ describe('Thread', () => {
|
|
|
142
142
|
threadResponses as unknown as Parameters<typeof channel.state.addMessagesSorted>[0],
|
|
143
143
|
);
|
|
144
144
|
|
|
145
|
-
let
|
|
145
|
+
let setChannelUnreadState:
|
|
146
|
+
| React.ContextType<typeof ChannelContext>['setChannelUnreadState']
|
|
147
|
+
| undefined;
|
|
146
148
|
|
|
147
149
|
const { getByText, toJSON } = render(
|
|
148
150
|
<ChannelsStateProvider>
|
|
@@ -161,7 +163,7 @@ describe('Thread', () => {
|
|
|
161
163
|
<Channel channel={channel} thread={thread} threadList>
|
|
162
164
|
<ChannelContext.Consumer>
|
|
163
165
|
{(c) => {
|
|
164
|
-
|
|
166
|
+
setChannelUnreadState = c.setChannelUnreadState;
|
|
165
167
|
return <Thread />;
|
|
166
168
|
}}
|
|
167
169
|
</ChannelContext.Consumer>
|
|
@@ -178,7 +180,7 @@ describe('Thread', () => {
|
|
|
178
180
|
expect(getByText('Message6')).toBeTruthy();
|
|
179
181
|
});
|
|
180
182
|
|
|
181
|
-
act(() =>
|
|
183
|
+
act(() => setChannelUnreadState!(undefined));
|
|
182
184
|
|
|
183
185
|
const snapshot = toJSON() as unknown as {
|
|
184
186
|
children: Array<{
|
|
@@ -110,7 +110,6 @@ export type ChannelContextValue = {
|
|
|
110
110
|
reloadChannel: () => Promise<void>;
|
|
111
111
|
scrollToFirstUnreadThreshold: number;
|
|
112
112
|
setChannelUnreadState: (data: ChannelUnreadStateStoreType['channelUnreadState']) => void;
|
|
113
|
-
setLastRead: React.Dispatch<React.SetStateAction<Date | undefined>>;
|
|
114
113
|
setTargetedMessage: (messageId?: string) => void;
|
|
115
114
|
/**
|
|
116
115
|
* Returns true when Channel is about to load an initial targeted message.
|
|
@@ -131,8 +130,6 @@ export type ChannelContextValue = {
|
|
|
131
130
|
*/
|
|
132
131
|
highlightedMessageId?: string;
|
|
133
132
|
isChannelActive?: boolean;
|
|
134
|
-
|
|
135
|
-
lastRead?: Date;
|
|
136
133
|
loading?: boolean;
|
|
137
134
|
/**
|
|
138
135
|
* Maximum time in milliseconds that should occur between messages
|
package/src/version.json
CHANGED