stream-chat-react-native-core 6.0.2 → 6.1.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 +371 -279
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useChannelDataState.js +8 -0
- package/lib/commonjs/components/Channel/hooks/useChannelDataState.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js +10 -1
- package/lib/commonjs/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js +4 -0
- package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js +161 -69
- package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useTargetedMessage.js +10 -0
- package/lib/commonjs/components/Channel/hooks/useTargetedMessage.js.map +1 -1
- package/lib/commonjs/components/Chat/hooks/handleEventToSyncDB.js +81 -54
- package/lib/commonjs/components/Chat/hooks/handleEventToSyncDB.js.map +1 -1
- package/lib/commonjs/components/Message/Message.js +6 -0
- package/lib/commonjs/components/Message/Message.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js +117 -79
- package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useMessageActions.js +32 -14
- package/lib/commonjs/components/Message/hooks/useMessageActions.js.map +1 -1
- package/lib/commonjs/components/Message/utils/messageActions.js +4 -0
- package/lib/commonjs/components/Message/utils/messageActions.js.map +1 -1
- package/lib/commonjs/components/MessageList/InlineUnreadIndicator.js +19 -55
- package/lib/commonjs/components/MessageList/InlineUnreadIndicator.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageList.js +249 -211
- package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
- package/lib/commonjs/components/MessageList/UnreadMessagesNotification.js +148 -0
- package/lib/commonjs/components/MessageList/UnreadMessagesNotification.js.map +1 -0
- package/lib/commonjs/components/MessageMenu/MessageActionListItem.js.map +1 -1
- package/lib/commonjs/contexts/channelContext/ChannelContext.js.map +1 -1
- package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js +7 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/commonjs/i18n/en.json +2 -0
- package/lib/commonjs/i18n/es.json +2 -0
- package/lib/commonjs/i18n/fr.json +2 -0
- package/lib/commonjs/i18n/he.json +2 -0
- package/lib/commonjs/i18n/hi.json +2 -0
- package/lib/commonjs/i18n/it.json +2 -0
- package/lib/commonjs/i18n/ja.json +2 -0
- package/lib/commonjs/i18n/ko.json +2 -0
- package/lib/commonjs/i18n/nl.json +2 -0
- package/lib/commonjs/i18n/pt-br.json +2 -0
- package/lib/commonjs/i18n/ru.json +2 -0
- package/lib/commonjs/i18n/tr.json +2 -0
- package/lib/commonjs/icons/UnreadIndicator.js +30 -0
- package/lib/commonjs/icons/UnreadIndicator.js.map +1 -0
- package/lib/commonjs/icons/index.js +11 -0
- package/lib/commonjs/icons/index.js.map +1 -1
- package/lib/commonjs/store/SqliteClient.js +1 -1
- package/lib/commonjs/store/schema.js +1 -0
- package/lib/commonjs/store/schema.js.map +1 -1
- package/lib/commonjs/types/types.js.map +1 -1
- package/lib/commonjs/utils/utils.js +35 -1
- package/lib/commonjs/utils/utils.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Channel/Channel.js +371 -279
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/hooks/useChannelDataState.js +8 -0
- package/lib/module/components/Channel/hooks/useChannelDataState.js.map +1 -1
- package/lib/module/components/Channel/hooks/useCreateChannelContext.js +10 -1
- package/lib/module/components/Channel/hooks/useCreateChannelContext.js.map +1 -1
- package/lib/module/components/Channel/hooks/useCreateMessagesContext.js +4 -0
- package/lib/module/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
- package/lib/module/components/Channel/hooks/useMessageListPagination.js +161 -69
- package/lib/module/components/Channel/hooks/useMessageListPagination.js.map +1 -1
- package/lib/module/components/Channel/hooks/useTargetedMessage.js +10 -0
- package/lib/module/components/Channel/hooks/useTargetedMessage.js.map +1 -1
- package/lib/module/components/Chat/hooks/handleEventToSyncDB.js +81 -54
- package/lib/module/components/Chat/hooks/handleEventToSyncDB.js.map +1 -1
- package/lib/module/components/Message/Message.js +6 -0
- package/lib/module/components/Message/Message.js.map +1 -1
- package/lib/module/components/Message/hooks/useMessageActionHandlers.js +117 -79
- package/lib/module/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
- package/lib/module/components/Message/hooks/useMessageActions.js +32 -14
- package/lib/module/components/Message/hooks/useMessageActions.js.map +1 -1
- package/lib/module/components/Message/utils/messageActions.js +4 -0
- package/lib/module/components/Message/utils/messageActions.js.map +1 -1
- package/lib/module/components/MessageList/InlineUnreadIndicator.js +19 -55
- package/lib/module/components/MessageList/InlineUnreadIndicator.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +249 -211
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/components/MessageList/UnreadMessagesNotification.js +148 -0
- package/lib/module/components/MessageList/UnreadMessagesNotification.js.map +1 -0
- package/lib/module/components/MessageMenu/MessageActionListItem.js.map +1 -1
- package/lib/module/contexts/channelContext/ChannelContext.js.map +1 -1
- package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/module/contexts/themeContext/utils/theme.js +7 -1
- package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/module/i18n/en.json +2 -0
- package/lib/module/i18n/es.json +2 -0
- package/lib/module/i18n/fr.json +2 -0
- package/lib/module/i18n/he.json +2 -0
- package/lib/module/i18n/hi.json +2 -0
- package/lib/module/i18n/it.json +2 -0
- package/lib/module/i18n/ja.json +2 -0
- package/lib/module/i18n/ko.json +2 -0
- package/lib/module/i18n/nl.json +2 -0
- package/lib/module/i18n/pt-br.json +2 -0
- package/lib/module/i18n/ru.json +2 -0
- package/lib/module/i18n/tr.json +2 -0
- package/lib/module/icons/UnreadIndicator.js +30 -0
- package/lib/module/icons/UnreadIndicator.js.map +1 -0
- package/lib/module/icons/index.js +11 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/store/SqliteClient.js +1 -1
- package/lib/module/store/schema.js +1 -0
- package/lib/module/store/schema.js.map +1 -1
- package/lib/module/types/types.js.map +1 -1
- package/lib/module/utils/utils.js +35 -1
- package/lib/module/utils/utils.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Channel/Channel.d.ts +15 -3
- package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useChannelDataState.d.ts +1 -0
- package/lib/typescript/components/Channel/hooks/useChannelDataState.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/Channel/hooks/useCreateMessagesContext.d.ts +4 -2
- package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts +4 -1
- package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useTargetedMessage.d.ts +2 -1
- package/lib/typescript/components/Channel/hooks/useTargetedMessage.d.ts.map +1 -1
- package/lib/typescript/components/Chat/hooks/handleEventToSyncDB.d.ts.map +1 -1
- package/lib/typescript/components/Message/Message.d.ts +2 -1
- package/lib/typescript/components/Message/Message.d.ts.map +1 -1
- package/lib/typescript/components/Message/hooks/useMessageActionHandlers.d.ts +2 -1
- package/lib/typescript/components/Message/hooks/useMessageActionHandlers.d.ts.map +1 -1
- package/lib/typescript/components/Message/hooks/useMessageActions.d.ts +3 -2
- package/lib/typescript/components/Message/hooks/useMessageActions.d.ts.map +1 -1
- package/lib/typescript/components/Message/hooks/useMessageData.d.ts +1 -1
- package/lib/typescript/components/Message/utils/messageActions.d.ts +2 -1
- package/lib/typescript/components/Message/utils/messageActions.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/InlineUnreadIndicator.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageList.d.ts +1 -1
- package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/UnreadMessagesNotification.d.ts +13 -0
- package/lib/typescript/components/MessageList/UnreadMessagesNotification.d.ts.map +1 -0
- package/lib/typescript/components/MessageMenu/MessageActionListItem.d.ts +2 -2
- package/lib/typescript/components/MessageMenu/MessageActionListItem.d.ts.map +1 -1
- package/lib/typescript/contexts/channelContext/ChannelContext.d.ts +25 -8
- package/lib/typescript/contexts/channelContext/ChannelContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +5 -0
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts.map +1 -1
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts +6 -0
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
- package/lib/typescript/i18n/en.json +2 -0
- package/lib/typescript/i18n/es.json +2 -0
- package/lib/typescript/i18n/fr.json +2 -0
- package/lib/typescript/i18n/he.json +2 -0
- package/lib/typescript/i18n/hi.json +2 -0
- package/lib/typescript/i18n/it.json +2 -0
- package/lib/typescript/i18n/ja.json +2 -0
- package/lib/typescript/i18n/ko.json +2 -0
- package/lib/typescript/i18n/nl.json +2 -0
- package/lib/typescript/i18n/pt-br.json +2 -0
- package/lib/typescript/i18n/ru.json +2 -0
- package/lib/typescript/i18n/tr.json +2 -0
- package/lib/typescript/icons/UnreadIndicator.d.ts +8 -0
- package/lib/typescript/icons/UnreadIndicator.d.ts.map +1 -0
- package/lib/typescript/icons/index.d.ts +1 -0
- package/lib/typescript/icons/index.d.ts.map +1 -1
- package/lib/typescript/store/mappers/mapStorableToChannel.d.ts +1 -1
- package/lib/typescript/store/schema.d.ts +1 -0
- package/lib/typescript/store/schema.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +2 -1
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/utils/i18n/Streami18n.d.ts +2 -0
- package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
- package/lib/typescript/utils/utils.d.ts +21 -1
- package/lib/typescript/utils/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Channel/Channel.tsx +101 -24
- package/src/components/Channel/__tests__/Channel.test.js +109 -58
- package/src/components/Channel/__tests__/ownCapabilities.test.js +26 -0
- package/src/components/Channel/__tests__/useMessageListPagination.test.js +234 -37
- package/src/components/Channel/hooks/useChannelDataState.ts +8 -0
- package/src/components/Channel/hooks/useCreateChannelContext.ts +11 -0
- package/src/components/Channel/hooks/useCreateMessagesContext.ts +4 -0
- package/src/components/Channel/hooks/useMessageListPagination.tsx +134 -64
- package/src/components/Channel/hooks/useTargetedMessage.ts +9 -2
- package/src/components/Chat/hooks/handleEventToSyncDB.ts +23 -1
- package/src/components/Message/Message.tsx +8 -0
- package/src/components/Message/hooks/useMessageActionHandlers.ts +54 -40
- package/src/components/Message/hooks/useMessageActions.tsx +31 -14
- package/src/components/Message/utils/messageActions.ts +6 -0
- package/src/components/MessageList/InlineUnreadIndicator.tsx +17 -26
- package/src/components/MessageList/MessageList.tsx +197 -231
- package/src/components/MessageList/UnreadMessagesNotification.tsx +107 -0
- package/src/components/MessageList/__tests__/MessageList.test.js +213 -0
- package/src/components/MessageMenu/MessageActionListItem.tsx +2 -1
- package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +669 -679
- package/src/contexts/channelContext/ChannelContext.tsx +35 -9
- package/src/contexts/messagesContext/MessagesContext.tsx +7 -2
- package/src/contexts/themeContext/utils/theme.ts +12 -0
- package/src/i18n/en.json +2 -0
- package/src/i18n/es.json +2 -0
- package/src/i18n/fr.json +2 -0
- package/src/i18n/he.json +2 -0
- package/src/i18n/hi.json +2 -0
- package/src/i18n/it.json +2 -0
- package/src/i18n/ja.json +2 -0
- package/src/i18n/ko.json +2 -0
- package/src/i18n/nl.json +2 -0
- package/src/i18n/pt-br.json +2 -0
- package/src/i18n/ru.json +2 -0
- package/src/i18n/tr.json +2 -0
- package/src/icons/UnreadIndicator.tsx +18 -0
- package/src/icons/index.ts +1 -0
- package/src/store/SqliteClient.ts +1 -1
- package/src/store/schema.ts +2 -0
- package/src/types/types.ts +5 -2
- package/src/utils/utils.ts +61 -1
- package/src/version.json +1 -1
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
Channel as ChannelType,
|
|
13
13
|
EventHandler,
|
|
14
14
|
FormatMessageResponse,
|
|
15
|
-
logChatPromiseExecution,
|
|
16
15
|
MessageResponse,
|
|
17
16
|
Reaction,
|
|
18
17
|
SendMessageAPIResponse,
|
|
@@ -92,7 +91,7 @@ import {
|
|
|
92
91
|
isImagePickerAvailable,
|
|
93
92
|
} from '../../native';
|
|
94
93
|
import * as dbApi from '../../store/apis';
|
|
95
|
-
import { DefaultStreamChatGenerics, FileTypes } from '../../types/types';
|
|
94
|
+
import { ChannelUnreadState, DefaultStreamChatGenerics, FileTypes } from '../../types/types';
|
|
96
95
|
import { addReactionToLocalState } from '../../utils/addReactionToLocalState';
|
|
97
96
|
import { compressedImageURI } from '../../utils/compressImage';
|
|
98
97
|
import { DBSyncManager } from '../../utils/DBSyncManager';
|
|
@@ -179,6 +178,7 @@ import { ScrollToBottomButton as ScrollToBottomButtonDefault } from '../MessageL
|
|
|
179
178
|
import { StickyHeader as StickyHeaderDefault } from '../MessageList/StickyHeader';
|
|
180
179
|
import { TypingIndicator as TypingIndicatorDefault } from '../MessageList/TypingIndicator';
|
|
181
180
|
import { TypingIndicatorContainer as TypingIndicatorContainerDefault } from '../MessageList/TypingIndicatorContainer';
|
|
181
|
+
import { UnreadMessagesNotification as UnreadMessagesNotificationDefault } from '../MessageList/UnreadMessagesNotification';
|
|
182
182
|
import { MessageActionList as MessageActionListDefault } from '../MessageMenu/MessageActionList';
|
|
183
183
|
import { MessageActionListItem as MessageActionListItemDefault } from '../MessageMenu/MessageActionListItem';
|
|
184
184
|
import { MessageMenu as MessageMenuDefault } from '../MessageMenu/MessageMenu';
|
|
@@ -188,6 +188,15 @@ import { MessageUserReactionsAvatar as MessageUserReactionsAvatarDefault } from
|
|
|
188
188
|
import { MessageUserReactionsItem as MessageUserReactionsItemDefault } from '../MessageMenu/MessageUserReactionsItem';
|
|
189
189
|
import { Reply as ReplyDefault } from '../Reply/Reply';
|
|
190
190
|
|
|
191
|
+
export type MarkReadFunctionOptions = {
|
|
192
|
+
/**
|
|
193
|
+
* Signal, whether the `channelUnreadUiState` should be updated.
|
|
194
|
+
* By default, the local state update is prevented when the Channel component is mounted.
|
|
195
|
+
* This is in order to keep the UI indicating the original unread state, when the user opens a channel.
|
|
196
|
+
*/
|
|
197
|
+
updateChannelUnreadState?: boolean;
|
|
198
|
+
};
|
|
199
|
+
|
|
191
200
|
const styles = StyleSheet.create({
|
|
192
201
|
selectChannel: { fontWeight: 'bold', padding: 16 },
|
|
193
202
|
});
|
|
@@ -301,6 +310,7 @@ export type ChannelPropsWithContext<
|
|
|
301
310
|
| 'handleDelete'
|
|
302
311
|
| 'handleEdit'
|
|
303
312
|
| 'handleFlag'
|
|
313
|
+
| 'handleMarkUnread'
|
|
304
314
|
| 'handleMute'
|
|
305
315
|
| 'handlePinMessage'
|
|
306
316
|
| 'handleReaction'
|
|
@@ -360,6 +370,7 @@ export type ChannelPropsWithContext<
|
|
|
360
370
|
| 'VideoThumbnail'
|
|
361
371
|
| 'PollContent'
|
|
362
372
|
| 'hasCreatePoll'
|
|
373
|
+
| 'UnreadMessagesNotification'
|
|
363
374
|
| 'StreamingMessageView'
|
|
364
375
|
>
|
|
365
376
|
> &
|
|
@@ -384,7 +395,10 @@ export type ChannelPropsWithContext<
|
|
|
384
395
|
* Overrides the Stream default mark channel read request (Advanced usage only)
|
|
385
396
|
* @param channel Channel object
|
|
386
397
|
*/
|
|
387
|
-
doMarkReadRequest?: (
|
|
398
|
+
doMarkReadRequest?: (
|
|
399
|
+
channel: ChannelType<StreamChatGenerics>,
|
|
400
|
+
setChannelUnreadUiState?: (state: ChannelUnreadState) => void,
|
|
401
|
+
) => void;
|
|
388
402
|
/**
|
|
389
403
|
* Overrides the Stream default send message request (Advanced usage only)
|
|
390
404
|
* @param channelId
|
|
@@ -433,6 +447,10 @@ export type ChannelPropsWithContext<
|
|
|
433
447
|
* Custom loading error indicator to override the Stream default
|
|
434
448
|
*/
|
|
435
449
|
LoadingErrorIndicator?: React.ComponentType<LoadingErrorProps>;
|
|
450
|
+
/**
|
|
451
|
+
* Boolean flag to enable/disable marking the channel as read on mount
|
|
452
|
+
*/
|
|
453
|
+
markReadOnMount?: boolean;
|
|
436
454
|
maxMessageLength?: number;
|
|
437
455
|
/**
|
|
438
456
|
* Load the channel at a specified message instead of the most recent message.
|
|
@@ -529,6 +547,7 @@ const ChannelWithContext = <
|
|
|
529
547
|
handleDelete,
|
|
530
548
|
handleEdit,
|
|
531
549
|
handleFlag,
|
|
550
|
+
handleMarkUnread,
|
|
532
551
|
handleMute,
|
|
533
552
|
handlePinMessage,
|
|
534
553
|
handleQuotedReply,
|
|
@@ -566,6 +585,7 @@ const ChannelWithContext = <
|
|
|
566
585
|
loadingMore: loadingMoreProp,
|
|
567
586
|
loadingMoreRecent: loadingMoreRecentProp,
|
|
568
587
|
markdownRules,
|
|
588
|
+
markReadOnMount = true,
|
|
569
589
|
maxMessageLength: maxMessageLengthProp,
|
|
570
590
|
maxNumberOfFiles = 10,
|
|
571
591
|
maxTimeBetweenGroupedMessages,
|
|
@@ -647,6 +667,7 @@ const ChannelWithContext = <
|
|
|
647
667
|
threadMessages,
|
|
648
668
|
TypingIndicator = TypingIndicatorDefault,
|
|
649
669
|
TypingIndicatorContainer = TypingIndicatorContainerDefault,
|
|
670
|
+
UnreadMessagesNotification = UnreadMessagesNotificationDefault,
|
|
650
671
|
UploadProgressIndicator = UploadProgressIndicatorDefault,
|
|
651
672
|
UrlPreview = CardDefault,
|
|
652
673
|
VideoThumbnail = VideoThumbnailDefault,
|
|
@@ -674,10 +695,13 @@ const ChannelWithContext = <
|
|
|
674
695
|
const [thread, setThread] = useState<MessageType<StreamChatGenerics> | null>(threadProps || null);
|
|
675
696
|
const [threadHasMore, setThreadHasMore] = useState(true);
|
|
676
697
|
const [threadLoadingMore, setThreadLoadingMore] = useState(false);
|
|
698
|
+
const [channelUnreadState, setChannelUnreadState] = useState<ChannelUnreadState | undefined>(
|
|
699
|
+
undefined,
|
|
700
|
+
);
|
|
677
701
|
|
|
678
702
|
const syncingChannelRef = useRef(false);
|
|
679
703
|
|
|
680
|
-
const { setTargetedMessage, targetedMessage } = useTargetedMessage();
|
|
704
|
+
const { highlightedMessageId, setTargetedMessage, targetedMessage } = useTargetedMessage();
|
|
681
705
|
|
|
682
706
|
/**
|
|
683
707
|
* This ref will hold the abort controllers for
|
|
@@ -692,6 +716,7 @@ const ChannelWithContext = <
|
|
|
692
716
|
const {
|
|
693
717
|
copyStateFromChannel,
|
|
694
718
|
initStateFromChannel,
|
|
719
|
+
setRead,
|
|
695
720
|
setTyping,
|
|
696
721
|
state: channelState,
|
|
697
722
|
} = useChannelDataState<StreamChatGenerics>(channel);
|
|
@@ -754,6 +779,22 @@ const ChannelWithContext = <
|
|
|
754
779
|
}
|
|
755
780
|
}
|
|
756
781
|
|
|
782
|
+
if (event.type === 'notification.mark_unread') {
|
|
783
|
+
setChannelUnreadState((prev) => {
|
|
784
|
+
if (!(event.last_read_at && event.user)) return prev;
|
|
785
|
+
return {
|
|
786
|
+
first_unread_message_id: event.first_unread_message_id,
|
|
787
|
+
last_read: new Date(event.last_read_at),
|
|
788
|
+
last_read_message_id: event.last_read_message_id,
|
|
789
|
+
unread_messages: event.unread_messages ?? 0,
|
|
790
|
+
};
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (event.type === 'channel.truncated' && event.cid === channel.cid) {
|
|
795
|
+
setChannelUnreadState(undefined);
|
|
796
|
+
}
|
|
797
|
+
|
|
757
798
|
// only update channel state if the events are not the previously subscribed useEffect's subscription events
|
|
758
799
|
if (channel && channel.initialized) {
|
|
759
800
|
copyChannelState();
|
|
@@ -764,6 +805,8 @@ const ChannelWithContext = <
|
|
|
764
805
|
useEffect(() => {
|
|
765
806
|
let listener: ReturnType<typeof channel.on>;
|
|
766
807
|
const initChannel = async () => {
|
|
808
|
+
setLastRead(new Date());
|
|
809
|
+
const unreadCount = channel.countUnread();
|
|
767
810
|
if (!channel || !shouldSyncChannel || channel.offlineMode) return;
|
|
768
811
|
let errored = false;
|
|
769
812
|
|
|
@@ -782,14 +825,32 @@ const ChannelWithContext = <
|
|
|
782
825
|
loadInitialMessagesStateFromChannel(channel, channel.state.messagePagination.hasPrev);
|
|
783
826
|
}
|
|
784
827
|
|
|
828
|
+
if (client.user?.id && channel.state.read[client.user.id]) {
|
|
829
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
830
|
+
const { user, ...ownReadState } = channel.state.read[client.user.id];
|
|
831
|
+
setChannelUnreadState(ownReadState);
|
|
832
|
+
}
|
|
833
|
+
|
|
785
834
|
if (messageId) {
|
|
786
835
|
await loadChannelAroundMessage({ messageId, setTargetedMessage });
|
|
787
836
|
} else if (
|
|
788
837
|
initialScrollToFirstUnreadMessage &&
|
|
789
|
-
|
|
838
|
+
client.user &&
|
|
839
|
+
unreadCount > scrollToFirstUnreadThreshold
|
|
790
840
|
) {
|
|
791
|
-
|
|
841
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
842
|
+
const { user, ...ownReadState } = channel.state.read[client.user.id];
|
|
843
|
+
await loadChannelAtFirstUnreadMessage({
|
|
844
|
+
channelUnreadState: ownReadState,
|
|
845
|
+
setChannelUnreadState,
|
|
846
|
+
setTargetedMessage,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (unreadCount > 0 && markReadOnMount) {
|
|
851
|
+
await markRead({ updateChannelUnreadState: false });
|
|
792
852
|
}
|
|
853
|
+
|
|
793
854
|
listener = channel.on(handleEvent);
|
|
794
855
|
};
|
|
795
856
|
|
|
@@ -819,12 +880,12 @@ const ChannelWithContext = <
|
|
|
819
880
|
*/
|
|
820
881
|
useEffect(() => {
|
|
821
882
|
const handleEvent: EventHandler<StreamChatGenerics> = (event) => {
|
|
822
|
-
if (channel.cid === event.cid)
|
|
883
|
+
if (channel.cid === event.cid) setRead(channel);
|
|
823
884
|
};
|
|
824
885
|
|
|
825
886
|
const { unsubscribe } = client.on('notification.mark_read', handleEvent);
|
|
826
887
|
return unsubscribe;
|
|
827
|
-
}, [channel
|
|
888
|
+
}, [channel, client, setRead]);
|
|
828
889
|
|
|
829
890
|
const threadPropsExists = !!threadProps;
|
|
830
891
|
|
|
@@ -858,23 +919,33 @@ const ChannelWithContext = <
|
|
|
858
919
|
/**
|
|
859
920
|
* CHANNEL METHODS
|
|
860
921
|
*/
|
|
861
|
-
const markRead: ChannelContextValue<StreamChatGenerics>['markRead'] =
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
922
|
+
const markRead: ChannelContextValue<StreamChatGenerics>['markRead'] = throttle(
|
|
923
|
+
async (options?: MarkReadFunctionOptions) => {
|
|
924
|
+
const { updateChannelUnreadState = true } = options ?? {};
|
|
925
|
+
if (!channel || channel?.disconnected || !clientChannelConfig?.read_events) {
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
867
928
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
929
|
+
if (doMarkReadRequest) {
|
|
930
|
+
doMarkReadRequest(channel, updateChannelUnreadState ? setChannelUnreadState : undefined);
|
|
931
|
+
} else {
|
|
932
|
+
try {
|
|
933
|
+
const response = await channel.markRead();
|
|
934
|
+
if (updateChannelUnreadState && response && lastRead) {
|
|
935
|
+
setChannelUnreadState({
|
|
936
|
+
last_read: lastRead,
|
|
937
|
+
last_read_message_id: response?.event.last_read_message_id,
|
|
938
|
+
unread_messages: 0,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
} catch (err) {
|
|
942
|
+
console.log('Error marking channel as read:', err);
|
|
872
943
|
}
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
)
|
|
944
|
+
}
|
|
945
|
+
},
|
|
946
|
+
defaultThrottleInterval,
|
|
947
|
+
throttleOptions,
|
|
948
|
+
);
|
|
878
949
|
|
|
879
950
|
const reloadThread = async () => {
|
|
880
951
|
if (!channel || !thread?.id) return;
|
|
@@ -1596,8 +1667,9 @@ const ChannelWithContext = <
|
|
|
1596
1667
|
overrideCapabilities: overrideOwnCapabilities,
|
|
1597
1668
|
});
|
|
1598
1669
|
|
|
1599
|
-
const channelContext = useCreateChannelContext({
|
|
1670
|
+
const channelContext = useCreateChannelContext<StreamChatGenerics>({
|
|
1600
1671
|
channel,
|
|
1672
|
+
channelUnreadState,
|
|
1601
1673
|
disabled: !!channel?.data?.frozen,
|
|
1602
1674
|
EmptyStateIndicator,
|
|
1603
1675
|
enableMessageGroupingByUser,
|
|
@@ -1608,9 +1680,11 @@ const ChannelWithContext = <
|
|
|
1608
1680
|
!!(clientChannelConfig?.commands || [])?.some((command) => command.name === 'giphy'),
|
|
1609
1681
|
hideDateSeparators,
|
|
1610
1682
|
hideStickyDateHeader,
|
|
1683
|
+
highlightedMessageId,
|
|
1611
1684
|
isChannelActive: shouldSyncChannel,
|
|
1612
1685
|
lastRead,
|
|
1613
1686
|
loadChannelAroundMessage,
|
|
1687
|
+
loadChannelAtFirstUnreadMessage,
|
|
1614
1688
|
loading: channelMessagesState.loading,
|
|
1615
1689
|
LoadingIndicator,
|
|
1616
1690
|
markRead,
|
|
@@ -1620,6 +1694,7 @@ const ChannelWithContext = <
|
|
|
1620
1694
|
read: channelState.read ?? {},
|
|
1621
1695
|
reloadChannel,
|
|
1622
1696
|
scrollToFirstUnreadThreshold,
|
|
1697
|
+
setChannelUnreadState,
|
|
1623
1698
|
setLastRead,
|
|
1624
1699
|
setTargetedMessage,
|
|
1625
1700
|
StickyHeader,
|
|
@@ -1748,6 +1823,7 @@ const ChannelWithContext = <
|
|
|
1748
1823
|
handleDelete,
|
|
1749
1824
|
handleEdit,
|
|
1750
1825
|
handleFlag,
|
|
1826
|
+
handleMarkUnread,
|
|
1751
1827
|
handleMute,
|
|
1752
1828
|
handlePinMessage,
|
|
1753
1829
|
handleQuotedReply,
|
|
@@ -1815,6 +1891,7 @@ const ChannelWithContext = <
|
|
|
1815
1891
|
targetedMessage,
|
|
1816
1892
|
TypingIndicator,
|
|
1817
1893
|
TypingIndicatorContainer,
|
|
1894
|
+
UnreadMessagesNotification,
|
|
1818
1895
|
updateMessage,
|
|
1819
1896
|
UrlPreview,
|
|
1820
1897
|
VideoThumbnail,
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
useChannelDataState,
|
|
30
30
|
useChannelMessageDataState,
|
|
31
31
|
} from '../hooks/useChannelDataState';
|
|
32
|
+
import * as MessageListPaginationHooks from '../hooks/useMessageListPagination';
|
|
32
33
|
|
|
33
34
|
// This component is used for performing effects in a component that consumes ChannelContext,
|
|
34
35
|
// i.e. making use of the callbacks & values provided by the Channel component.
|
|
@@ -87,6 +88,7 @@ describe('Channel', () => {
|
|
|
87
88
|
const nullChannel = {
|
|
88
89
|
...channel,
|
|
89
90
|
cid: null,
|
|
91
|
+
countUnread: () => 0,
|
|
90
92
|
off: () => {},
|
|
91
93
|
on: () => ({
|
|
92
94
|
unsubscribe: () => null,
|
|
@@ -464,79 +466,128 @@ describe('Channel initial load useEffect', () => {
|
|
|
464
466
|
);
|
|
465
467
|
});
|
|
466
468
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
469
|
+
describe('initialScrollToFirstUnreadMessage', () => {
|
|
470
|
+
afterEach(() => {
|
|
471
|
+
// Clear all mocks after each test
|
|
472
|
+
jest.clearAllMocks();
|
|
473
|
+
// Restore all mocks to their original implementation
|
|
474
|
+
jest.restoreAllMocks();
|
|
475
|
+
cleanup();
|
|
470
476
|
});
|
|
477
|
+
const mockedHook = (values) =>
|
|
478
|
+
jest.spyOn(MessageListPaginationHooks, 'useMessageListPagination').mockImplementation(() => ({
|
|
479
|
+
copyMessagesStateFromChannel: jest.fn(),
|
|
480
|
+
loadChannelAroundMessage: jest.fn(),
|
|
481
|
+
loadChannelAtFirstUnreadMessage: jest.fn(),
|
|
482
|
+
loadInitialMessagesStateFromChannel: jest.fn(),
|
|
483
|
+
loadLatestMessages: jest.fn(),
|
|
484
|
+
loadMore: jest.fn(),
|
|
485
|
+
loadMoreRecent: jest.fn(),
|
|
486
|
+
state: { ...channelInitialState },
|
|
487
|
+
...values,
|
|
488
|
+
}));
|
|
489
|
+
it("should not call loadChannelAtFirstUnreadMessage if channel's unread count is 0", async () => {
|
|
490
|
+
const mockedChannel = generateChannelResponse({
|
|
491
|
+
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ text: `message-${i}` })),
|
|
492
|
+
});
|
|
471
493
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
494
|
+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
|
|
495
|
+
const channel = chatClient.channel('messaging', mockedChannel.id);
|
|
496
|
+
await channel.watch();
|
|
497
|
+
const user = generateUser();
|
|
498
|
+
const read_data = {};
|
|
476
499
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
messagePagination: {
|
|
482
|
-
hasNext: true,
|
|
483
|
-
hasPrev: true,
|
|
484
|
-
},
|
|
485
|
-
messages,
|
|
486
|
-
};
|
|
487
|
-
channel.countUnread = jest.fn(() => 0);
|
|
500
|
+
read_data[chatClient.user.id] = {
|
|
501
|
+
last_read: new Date(),
|
|
502
|
+
user,
|
|
503
|
+
};
|
|
488
504
|
|
|
489
|
-
|
|
505
|
+
channel.state = {
|
|
506
|
+
...channelInitialState,
|
|
507
|
+
read: read_data,
|
|
508
|
+
};
|
|
509
|
+
channel.countUnread = jest.fn(() => 0);
|
|
490
510
|
|
|
491
|
-
|
|
492
|
-
expect(loadMessageIntoState).not.toHaveBeenCalled();
|
|
493
|
-
});
|
|
494
|
-
});
|
|
511
|
+
const loadChannelAtFirstUnreadMessageFn = jest.fn();
|
|
495
512
|
|
|
496
|
-
|
|
497
|
-
const mockedChannel = generateChannelResponse({
|
|
498
|
-
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ text: `message-${i}` })),
|
|
499
|
-
});
|
|
513
|
+
mockedHook({ loadChannelAtFirstUnreadMessage: loadChannelAtFirstUnreadMessageFn });
|
|
500
514
|
|
|
501
|
-
|
|
502
|
-
const channel = chatClient.channel('messaging', mockedChannel.id);
|
|
503
|
-
await channel.watch();
|
|
504
|
-
const messages = Array.from({ length: 100 }, (_, i) => generateMessage({ id: i }));
|
|
515
|
+
renderComponent({ channel, initialScrollToFirstUnreadMessage: true });
|
|
505
516
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const newMessages = getElementsAround(messages, 'id', id);
|
|
510
|
-
channel.state.messages = newMessages;
|
|
517
|
+
await waitFor(() => {
|
|
518
|
+
expect(loadChannelAtFirstUnreadMessageFn).not.toHaveBeenCalled();
|
|
519
|
+
});
|
|
511
520
|
});
|
|
512
521
|
|
|
513
|
-
channel
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
hasNext: true,
|
|
518
|
-
hasPrev: true,
|
|
519
|
-
},
|
|
520
|
-
messages,
|
|
521
|
-
messageSets: [{ isCurrent: true, isLatest: true }],
|
|
522
|
-
};
|
|
522
|
+
it("should call loadChannelAtFirstUnreadMessage if channel's unread count is greater than 0", async () => {
|
|
523
|
+
const mockedChannel = generateChannelResponse({
|
|
524
|
+
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ text: `message-${i}` })),
|
|
525
|
+
});
|
|
523
526
|
|
|
524
|
-
|
|
527
|
+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
|
|
528
|
+
const channel = chatClient.channel('messaging', mockedChannel.id);
|
|
529
|
+
await channel.watch();
|
|
525
530
|
|
|
526
|
-
|
|
531
|
+
const user = generateUser();
|
|
532
|
+
const numberOfUnreadMessages = 15;
|
|
533
|
+
const read_data = {};
|
|
527
534
|
|
|
528
|
-
|
|
529
|
-
|
|
535
|
+
read_data[chatClient.user.id] = {
|
|
536
|
+
last_read: new Date(),
|
|
537
|
+
unread_messages: numberOfUnreadMessages,
|
|
538
|
+
user,
|
|
539
|
+
};
|
|
540
|
+
channel.state = {
|
|
541
|
+
...channelInitialState,
|
|
542
|
+
read: read_data,
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
channel.countUnread = jest.fn(() => numberOfUnreadMessages);
|
|
546
|
+
const loadChannelAtFirstUnreadMessageFn = jest.fn();
|
|
547
|
+
|
|
548
|
+
mockedHook({ loadChannelAtFirstUnreadMessage: loadChannelAtFirstUnreadMessageFn });
|
|
549
|
+
|
|
550
|
+
renderComponent({ channel, initialScrollToFirstUnreadMessage: true });
|
|
551
|
+
|
|
552
|
+
await waitFor(() => {
|
|
553
|
+
expect(loadChannelAtFirstUnreadMessageFn).toHaveBeenCalled();
|
|
554
|
+
});
|
|
530
555
|
});
|
|
531
556
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
557
|
+
it("should not call loadChannelAtFirstUnreadMessage if channel's unread count is greater than 0 lesser than scrollToFirstUnreadThreshold", async () => {
|
|
558
|
+
const mockedChannel = generateChannelResponse({
|
|
559
|
+
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ text: `message-${i}` })),
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
|
|
563
|
+
const channel = chatClient.channel('messaging', mockedChannel.id);
|
|
564
|
+
await channel.watch();
|
|
565
|
+
|
|
566
|
+
const user = generateUser();
|
|
567
|
+
const numberOfUnreadMessages = 2;
|
|
568
|
+
const read_data = {};
|
|
569
|
+
|
|
570
|
+
read_data[chatClient.user.id] = {
|
|
571
|
+
last_read: new Date(),
|
|
572
|
+
unread_messages: numberOfUnreadMessages,
|
|
573
|
+
user,
|
|
574
|
+
};
|
|
575
|
+
channel.state = {
|
|
576
|
+
...channelInitialState,
|
|
577
|
+
read: read_data,
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
channel.countUnread = jest.fn(() => numberOfUnreadMessages);
|
|
581
|
+
const loadChannelAtFirstUnreadMessageFn = jest.fn();
|
|
582
|
+
|
|
583
|
+
mockedHook({ loadChannelAtFirstUnreadMessage: loadChannelAtFirstUnreadMessageFn });
|
|
584
|
+
|
|
585
|
+
renderComponent({ channel, initialScrollToFirstUnreadMessage: true });
|
|
586
|
+
|
|
587
|
+
await waitFor(() => {
|
|
588
|
+
expect(loadChannelAtFirstUnreadMessageFn).not.toHaveBeenCalled();
|
|
589
|
+
});
|
|
590
|
+
});
|
|
540
591
|
});
|
|
541
592
|
|
|
542
593
|
it('should call resyncChannel when connection changed event is triggered', async () => {
|
|
@@ -236,6 +236,32 @@ describe('Own capabilities', () => {
|
|
|
236
236
|
});
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
+
describe(`${allOwnCapabilities.readEvents} capability`, () => {
|
|
240
|
+
it(`should render "Mark as Unread" action for messages when "${allOwnCapabilities.readEvents}" capability is enabled`, async () => {
|
|
241
|
+
await generateChannelWithCapabilities([allOwnCapabilities.readEvents]);
|
|
242
|
+
const { queryByLabelText } = await renderChannelAndOpenMessageActionsList(receivedMessage);
|
|
243
|
+
expect(!!queryByLabelText('markUnread action list item')).toBeTruthy();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it(`should not render "Mark Read" action for received message when "${allOwnCapabilities.readEvents}" capability is disabled`, async () => {
|
|
247
|
+
await generateChannelWithCapabilities();
|
|
248
|
+
|
|
249
|
+
const { queryByLabelText } = await renderChannelAndOpenMessageActionsList(receivedMessage);
|
|
250
|
+
expect(!!queryByLabelText('markUnread action list item')).toBeFalsy();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should override capability from "overrideOwnCapability.readEvents" prop', async () => {
|
|
254
|
+
await generateChannelWithCapabilities([allOwnCapabilities.readEvents]);
|
|
255
|
+
|
|
256
|
+
const { queryByLabelText } = await renderChannelAndOpenMessageActionsList(receivedMessage, {
|
|
257
|
+
overrideOwnCapabilities: {
|
|
258
|
+
readEvents: false,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
expect(!!queryByLabelText('markUnread action list item')).toBeFalsy();
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
239
265
|
describe(`${allOwnCapabilities.pinMessage} capability`, () => {
|
|
240
266
|
it(`should render "Pin Message" action for sent message when "${allOwnCapabilities.pinMessage}" capability is enabled`, async () => {
|
|
241
267
|
await generateChannelWithCapabilities([allOwnCapabilities.pinMessage]);
|