UNPKG

@sendbird/uikit-chat-hooks

Version:

A set of React hooks for integrating Sendbird chat functionality into your React app.

320 lines (317 loc) 17.7 kB
import { useRef } from 'react'; import { ASYNC_NOOP, NOOP, isDifferentChannel, isMyMessage, useAsyncEffect, useForceUpdate, useFreshCallback, useUniqHandlerId } from '@sendbird/uikit-utils'; import { useChannelHandler } from '../../handler/useChannelHandler'; import { useConnectionHandler } from '../../handler/useConnectionHandler'; import { useChannelMessagesReducer } from '../useChannelMessagesReducer'; const createMessageQuery = (channel, creator) => { if (creator) return creator(); return channel.createPreviousMessageListQuery({ limit: 100, reverse: true }); }; export const useOpenChannelMessagesWithQuery = (sdk, channel, userId, options) => { const queryRef = useRef(); const forceUpdate = useForceUpdate(); const handlerId = useUniqHandlerId('useOpenChannelMessagesWithQuery'); const { loading, refreshing, messages, newMessages, updateMessages, updateNewMessages, deleteNewMessages, deleteMessages, updateLoading, updateRefreshing } = useChannelMessagesReducer(options === null || options === void 0 ? void 0 : options.sortComparator); const init = useFreshCallback(async uid => { if (uid) { var _queryRef$current, _sdk$currentUser2; queryRef.current = createMessageQuery(channel, options === null || options === void 0 ? void 0 : options.queryCreator); if ((_queryRef$current = queryRef.current) !== null && _queryRef$current !== void 0 && _queryRef$current.hasNext) { var _queryRef$current2, _sdk$currentUser; const list = await ((_queryRef$current2 = queryRef.current) === null || _queryRef$current2 === void 0 ? void 0 : _queryRef$current2.load()); updateMessages(list, true, (_sdk$currentUser = sdk.currentUser) === null || _sdk$currentUser === void 0 ? void 0 : _sdk$currentUser.userId); } updateNewMessages([], true, (_sdk$currentUser2 = sdk.currentUser) === null || _sdk$currentUser2 === void 0 ? void 0 : _sdk$currentUser2.userId); } }); const channelUpdater = channel => { if (channel.isOpenChannel() && !isDifferentChannel(channel, channel)) { forceUpdate(); } }; useConnectionHandler(sdk, handlerId, { async onReconnectSucceeded() { var _queryRef$current3, _queryRef$current4, _queryRef$current5, _queryRef$current6, _queryRef$current7, _queryRef$current8, _queryRef$current9, _queryRef$current10, _queryRef$current11, _queryRef$current12, _queryRef$current13, _queryRef$current14, _queryRef$current15, _queryRef$current16, _queryRef$current17, _queryRef$current18, _sdk$currentUser3; const lastMessage = messages[0]; if (!lastMessage) return; const messageContext = { updatedMessages: [], addedMessages: [], deletedMessageIds: [] }; const changeLogsContext = { hasMore: false, token: '' }; const messageQueryContext = { hasMore: false, timestamp: lastMessage.createdAt }; // Updated & Deleted messages const changelogsParams = { replyType: (_queryRef$current3 = queryRef.current) === null || _queryRef$current3 === void 0 ? void 0 : _queryRef$current3.replyType, includeMetaArray: (_queryRef$current4 = queryRef.current) === null || _queryRef$current4 === void 0 ? void 0 : _queryRef$current4.includeMetaArray, includeReactions: (_queryRef$current5 = queryRef.current) === null || _queryRef$current5 === void 0 ? void 0 : _queryRef$current5.includeReactions, includeThreadInfo: (_queryRef$current6 = queryRef.current) === null || _queryRef$current6 === void 0 ? void 0 : _queryRef$current6.includeThreadInfo, includeParentMessageInfo: (_queryRef$current7 = queryRef.current) === null || _queryRef$current7 === void 0 ? void 0 : _queryRef$current7.includeParentMessageInfo }; const changeLogsByTS = await channel.getMessageChangeLogsSinceTimestamp(lastMessage.createdAt); changeLogsContext.token = changeLogsByTS.token; changeLogsContext.hasMore = changeLogsByTS.hasMore; messageContext.updatedMessages.push(...changeLogsByTS.updatedMessages); messageContext.deletedMessageIds.push(...changeLogsByTS.deletedMessageIds); while (changeLogsContext.hasMore) { const changeLogsByToken = await channel.getMessageChangeLogsSinceToken(changeLogsByTS.token, changelogsParams); changeLogsContext.token = changeLogsByToken.token; changeLogsContext.hasMore = changeLogsByToken.hasMore; messageContext.updatedMessages.push(...changeLogsByToken.updatedMessages); messageContext.deletedMessageIds.push(...changeLogsByToken.deletedMessageIds); } // Added messages const messageQueryParams = { prevResultSize: 0, nextResultSize: ((_queryRef$current8 = queryRef.current) === null || _queryRef$current8 === void 0 ? void 0 : _queryRef$current8.limit) ?? 100, reverse: (_queryRef$current9 = queryRef.current) === null || _queryRef$current9 === void 0 ? void 0 : _queryRef$current9.reverse, includeParentMessageInfo: (_queryRef$current10 = queryRef.current) === null || _queryRef$current10 === void 0 ? void 0 : _queryRef$current10.includeParentMessageInfo, includeThreadInfo: (_queryRef$current11 = queryRef.current) === null || _queryRef$current11 === void 0 ? void 0 : _queryRef$current11.includeThreadInfo, includeReactions: (_queryRef$current12 = queryRef.current) === null || _queryRef$current12 === void 0 ? void 0 : _queryRef$current12.includeReactions, includeMetaArray: (_queryRef$current13 = queryRef.current) === null || _queryRef$current13 === void 0 ? void 0 : _queryRef$current13.includeMetaArray, replyType: (_queryRef$current14 = queryRef.current) === null || _queryRef$current14 === void 0 ? void 0 : _queryRef$current14.replyType, customTypesFilter: (_queryRef$current15 = queryRef.current) === null || _queryRef$current15 === void 0 ? void 0 : _queryRef$current15.customTypesFilter, messageTypeFilter: (_queryRef$current16 = queryRef.current) === null || _queryRef$current16 === void 0 ? void 0 : _queryRef$current16.messageTypeFilter, senderUserIdsFilter: (_queryRef$current17 = queryRef.current) === null || _queryRef$current17 === void 0 ? void 0 : _queryRef$current17.senderUserIdsFilter, showSubchannelMessagesOnly: (_queryRef$current18 = queryRef.current) === null || _queryRef$current18 === void 0 ? void 0 : _queryRef$current18.showSubchannelMessagesOnly }; const queriedMessages = await channel.getMessagesByTimestamp(lastMessage.createdAt, messageQueryParams); messageQueryContext.hasMore = queriedMessages.length > 0; if (messageQueryContext.hasMore) { messageQueryContext.timestamp = queriedMessages[0].createdAt; messageContext.addedMessages.unshift(...queriedMessages); } while (messageQueryContext.hasMore) { const queriedMessages = await channel.getMessagesByTimestamp(messageQueryContext.timestamp, messageQueryParams); messageQueryContext.hasMore = queriedMessages.length > 0; if (messageQueryContext.hasMore) { messageQueryContext.timestamp = queriedMessages[0].createdAt; messageContext.addedMessages.unshift(...queriedMessages); } } // Update to View updateMessages([...messageContext.addedMessages, ...messageContext.updatedMessages], false, (_sdk$currentUser3 = sdk.currentUser) === null || _sdk$currentUser3 === void 0 ? void 0 : _sdk$currentUser3.userId); deleteMessages(messageContext.deletedMessageIds, []); if (messageContext.addedMessages.length > 0) { var _options$shouldCountN; if (options !== null && options !== void 0 && (_options$shouldCountN = options.shouldCountNewMessages) !== null && _options$shouldCountN !== void 0 && _options$shouldCountN.call(options)) { var _sdk$currentUser4; updateNewMessages(messageContext.addedMessages, false, (_sdk$currentUser4 = sdk.currentUser) === null || _sdk$currentUser4 === void 0 ? void 0 : _sdk$currentUser4.userId); } if (options !== null && options !== void 0 && options.onMessagesReceived) { options.onMessagesReceived(messageContext.addedMessages); } } } }); useChannelHandler(sdk, handlerId, { // Messages onMessageReceived(eventChannel, message) { var _sdk$currentUser5, _sdk$currentUser6, _options$shouldCountN2; if (isDifferentChannel(channel, eventChannel)) return; if (isMyMessage(message, (_sdk$currentUser5 = sdk.currentUser) === null || _sdk$currentUser5 === void 0 ? void 0 : _sdk$currentUser5.userId)) return; updateMessages([message], false, (_sdk$currentUser6 = sdk.currentUser) === null || _sdk$currentUser6 === void 0 ? void 0 : _sdk$currentUser6.userId); if (options !== null && options !== void 0 && (_options$shouldCountN2 = options.shouldCountNewMessages) !== null && _options$shouldCountN2 !== void 0 && _options$shouldCountN2.call(options)) { var _sdk$currentUser7; updateNewMessages([message], false, (_sdk$currentUser7 = sdk.currentUser) === null || _sdk$currentUser7 === void 0 ? void 0 : _sdk$currentUser7.userId); } if (options !== null && options !== void 0 && options.onMessagesReceived) { options.onMessagesReceived([message]); } }, onMessageUpdated(eventChannel, message) { var _sdk$currentUser8, _sdk$currentUser9; if (isDifferentChannel(channel, eventChannel)) return; if (isMyMessage(message, (_sdk$currentUser8 = sdk.currentUser) === null || _sdk$currentUser8 === void 0 ? void 0 : _sdk$currentUser8.userId)) return; updateMessages([message], false, (_sdk$currentUser9 = sdk.currentUser) === null || _sdk$currentUser9 === void 0 ? void 0 : _sdk$currentUser9.userId); }, onMessageDeleted(eventChannel, messageId) { if (isDifferentChannel(channel, eventChannel)) return; deleteMessages([messageId], []); deleteNewMessages([messageId], []); }, // Channels onChannelChanged: channelUpdater, onChannelFrozen: channelUpdater, onChannelUnfrozen: channelUpdater, onChannelParticipantCountChanged(eventChannel) { if (isDifferentChannel(channel, eventChannel)) return; channelUpdater(eventChannel); }, onChannelDeleted(channelUrl, type) { if (channel.url === channelUrl && type === 'open') { var _options$onChannelDel; options === null || options === void 0 || (_options$onChannelDel = options.onChannelDeleted) === null || _options$onChannelDel === void 0 || _options$onChannelDel.call(options); } }, // Users onOperatorUpdated: channelUpdater, onUserUnbanned: channelUpdater, onUserMuted: channelUpdater, onUserUnmuted: channelUpdater, onUserBanned(eventChannel, bannedUser) { var _sdk$currentUser10; if (isDifferentChannel(channel, eventChannel)) return; if (bannedUser.userId === ((_sdk$currentUser10 = sdk.currentUser) === null || _sdk$currentUser10 === void 0 ? void 0 : _sdk$currentUser10.userId)) { var _options$onChannelDel2; options === null || options === void 0 || (_options$onChannelDel2 = options.onChannelDeleted) === null || _options$onChannelDel2 === void 0 || _options$onChannelDel2.call(options); } else { channelUpdater(eventChannel); } } }, 'open'); useAsyncEffect(async () => { updateLoading(true); try { await channel.enter(); await init(userId); } catch (error) { var _options$onError, _options$onChannelDel3; options === null || options === void 0 || (_options$onError = options.onError) === null || _options$onError === void 0 || _options$onError.call(options, error); options === null || options === void 0 || (_options$onChannelDel3 = options.onChannelDeleted) === null || _options$onChannelDel3 === void 0 || _options$onChannelDel3.call(options); } finally { updateLoading(false); } return () => { channel.exit().catch(NOOP); }; }, [channel.url, userId]); const refresh = useFreshCallback(async () => { updateRefreshing(true); await init(userId); updateRefreshing(false); }); const prev = useFreshCallback(async () => { var _queryRef$current19; if (queryRef.current && (_queryRef$current19 = queryRef.current) !== null && _queryRef$current19 !== void 0 && _queryRef$current19.hasNext) { var _queryRef$current20, _sdk$currentUser11; const list = await ((_queryRef$current20 = queryRef.current) === null || _queryRef$current20 === void 0 ? void 0 : _queryRef$current20.load()); updateMessages(list, false, (_sdk$currentUser11 = sdk.currentUser) === null || _sdk$currentUser11 === void 0 ? void 0 : _sdk$currentUser11.userId); } }); const hasPrev = useFreshCallback(() => { var _queryRef$current21; return ((_queryRef$current21 = queryRef.current) === null || _queryRef$current21 === void 0 ? void 0 : _queryRef$current21.hasNext) ?? false; }); const next = useFreshCallback(ASYNC_NOOP); const hasNext = useFreshCallback(() => false); const sendUserMessage = useFreshCallback((params, onPending) => { return new Promise((resolve, reject) => { channel.sendUserMessage(params).onPending(pendingMessage => { if (pendingMessage.isUserMessage()) { var _sdk$currentUser12; updateMessages([pendingMessage], false, (_sdk$currentUser12 = sdk.currentUser) === null || _sdk$currentUser12 === void 0 ? void 0 : _sdk$currentUser12.userId); onPending === null || onPending === void 0 || onPending(pendingMessage); } }).onSucceeded(sentMessage => { if (sentMessage.isUserMessage()) { var _sdk$currentUser13; updateMessages([sentMessage], false, (_sdk$currentUser13 = sdk.currentUser) === null || _sdk$currentUser13 === void 0 ? void 0 : _sdk$currentUser13.userId); resolve(sentMessage); } }).onFailed((err, failedMessage) => { if (failedMessage) { var _sdk$currentUser14; updateMessages([failedMessage], false, (_sdk$currentUser14 = sdk.currentUser) === null || _sdk$currentUser14 === void 0 ? void 0 : _sdk$currentUser14.userId); } reject(err); }); }); }); const sendFileMessage = useFreshCallback((params, onPending) => { return new Promise((resolve, reject) => { channel.sendFileMessage(params).onPending(pendingMessage => { if (pendingMessage.isFileMessage()) { var _sdk$currentUser15; updateMessages([pendingMessage], false, (_sdk$currentUser15 = sdk.currentUser) === null || _sdk$currentUser15 === void 0 ? void 0 : _sdk$currentUser15.userId); onPending === null || onPending === void 0 || onPending(pendingMessage); } }).onSucceeded(sentMessage => { if (sentMessage.isFileMessage()) { var _sdk$currentUser16; updateMessages([sentMessage], false, (_sdk$currentUser16 = sdk.currentUser) === null || _sdk$currentUser16 === void 0 ? void 0 : _sdk$currentUser16.userId); resolve(sentMessage); } }).onFailed((err, failedMessage) => { if (failedMessage) { var _sdk$currentUser17; updateMessages([failedMessage], false, (_sdk$currentUser17 = sdk.currentUser) === null || _sdk$currentUser17 === void 0 ? void 0 : _sdk$currentUser17.userId); } reject(err); }); }); }); const updateUserMessage = useFreshCallback(async (messageId, params) => { var _sdk$currentUser18; const updatedMessage = await channel.updateUserMessage(messageId, params); updateMessages([updatedMessage], false, (_sdk$currentUser18 = sdk.currentUser) === null || _sdk$currentUser18 === void 0 ? void 0 : _sdk$currentUser18.userId); return updatedMessage; }); const updateFileMessage = useFreshCallback(async (messageId, params) => { var _sdk$currentUser19; const updatedMessage = await channel.updateFileMessage(messageId, params); updateMessages([updatedMessage], false, (_sdk$currentUser19 = sdk.currentUser) === null || _sdk$currentUser19 === void 0 ? void 0 : _sdk$currentUser19.userId); return updatedMessage; }); const resendMessage = useFreshCallback(async failedMessage => { var _sdk$currentUser20; const message = await (() => { if (failedMessage.isUserMessage()) return channel.resendUserMessage(failedMessage); if (failedMessage.isFileMessage()) return channel.resendFileMessage(failedMessage); return null; })(); if (message) updateMessages([message], false, (_sdk$currentUser20 = sdk.currentUser) === null || _sdk$currentUser20 === void 0 ? void 0 : _sdk$currentUser20.userId); }); const deleteMessage = useFreshCallback(async message => { if (message.sendingStatus === 'succeeded') { if (message.isUserMessage()) await channel.deleteMessage(message); if (message.isFileMessage()) await channel.deleteMessage(message); } else { deleteMessages([message.messageId], [message.reqId]); } }); const resetNewMessages = useFreshCallback(() => { var _sdk$currentUser21; updateNewMessages([], true, (_sdk$currentUser21 = sdk.currentUser) === null || _sdk$currentUser21 === void 0 ? void 0 : _sdk$currentUser21.userId); }); return { loading, refreshing, refresh, messages, newMessages, next, hasNext, prev, hasPrev, sendUserMessage, sendFileMessage, updateUserMessage, updateFileMessage, resendMessage, deleteMessage, resetNewMessages }; }; //# sourceMappingURL=useOpenChannelMessagesWithQuery.js.map