UNPKG

@sendbird/uikit-chat-hooks

Version:

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

335 lines (333 loc) 17.2 kB
import { useEffect, useRef } from 'react'; import { CollectionEventSource } from '@sendbird/chat'; import { MessageCollectionInitPolicy, MessageFilter } from '@sendbird/chat/groupChannel'; import { Logger, confirmAndMarkAsRead, isDifferentChannel, isMyMessage, isSendableMessage, useForceUpdate, useFreshCallback, useUniqHandlerId } from '@sendbird/uikit-utils'; import { useChannelHandler } from '../../handler/useChannelHandler'; import { useChannelMessagesReducer } from '../useChannelMessagesReducer'; const MESSAGE_LIMIT = { DEFAULT: 50, SEARCH: 20 }; const createMessageCollection = (channel, limit, options) => { if (options !== null && options !== void 0 && options.collectionCreator) return options === null || options === void 0 ? void 0 : options.collectionCreator({ startingPoint: options === null || options === void 0 ? void 0 : options.startingPoint }); const filter = new MessageFilter(); if (options.replyType) filter.replyType = options.replyType; return channel.createMessageCollection({ filter, limit, startingPoint: options === null || options === void 0 ? void 0 : options.startingPoint }); }; function isNotEmpty(arr) { if (!arr) return false; return arr.length !== 0; } function shouldUseSearchLimit(startingPoint) { return startingPoint < Date.now(); } /** * @deprecated This hook is deprecated and will be replaced by the '@sendbird/uikit-tools' package. * */ export const useGroupChannelMessagesWithCollection = (sdk, channel, userId, options) => { const initialStartingPoint = (options === null || options === void 0 ? void 0 : options.startingPoint) ?? Number.MAX_SAFE_INTEGER; const initialLimit = shouldUseSearchLimit(initialStartingPoint) ? MESSAGE_LIMIT.SEARCH : MESSAGE_LIMIT.DEFAULT; const forceUpdate = useForceUpdate(); const collectionRef = useRef(); const collectionInitializedRef = useRef(false); const handlerId = useUniqHandlerId('useGroupChannelMessagesWithCollection'); const { loading, refreshing, messages, newMessages, updateMessages, updateNewMessages, deleteNewMessages, deleteMessages, updateLoading, updateRefreshing } = useChannelMessagesReducer(options === null || options === void 0 ? void 0 : options.sortComparator); const channelMarkAsRead = source => { switch (source) { case CollectionEventSource.EVENT_MESSAGE_RECEIVED: case CollectionEventSource.EVENT_MESSAGE_SENT_SUCCESS: case CollectionEventSource.SYNC_MESSAGE_FILL: case undefined: confirmAndMarkAsRead([channel]); break; } }; const updateNewMessagesReceived = (source, messages) => { const incomingMessages = messages.filter(it => { var _sdk$currentUser; return !isMyMessage(it, (_sdk$currentUser = sdk.currentUser) === null || _sdk$currentUser === void 0 ? void 0 : _sdk$currentUser.userId); }); if (incomingMessages.length > 0) { switch (source) { case CollectionEventSource.EVENT_MESSAGE_RECEIVED: case CollectionEventSource.SYNC_MESSAGE_FILL: { var _options$shouldCountN, _sdk$currentUser2, _options$onMessagesRe; if (options !== null && options !== void 0 && (_options$shouldCountN = options.shouldCountNewMessages) !== null && _options$shouldCountN !== void 0 && _options$shouldCountN.call(options)) updateNewMessages(incomingMessages, false, (_sdk$currentUser2 = sdk.currentUser) === null || _sdk$currentUser2 === void 0 ? void 0 : _sdk$currentUser2.userId); options === null || options === void 0 || (_options$onMessagesRe = options.onMessagesReceived) === null || _options$onMessagesRe === void 0 || _options$onMessagesRe.call(options, incomingMessages); break; } } } }; const updateUnsentMessages = () => { var _sdk$currentUser3, _sdk$currentUser4; const { pendingMessages, failedMessages } = collectionRef.current ?? {}; if (isNotEmpty(pendingMessages)) updateMessages(pendingMessages, false, (_sdk$currentUser3 = sdk.currentUser) === null || _sdk$currentUser3 === void 0 ? void 0 : _sdk$currentUser3.userId); if (isNotEmpty(failedMessages)) updateMessages(failedMessages, false, (_sdk$currentUser4 = sdk.currentUser) === null || _sdk$currentUser4 === void 0 ? void 0 : _sdk$currentUser4.userId); }; const init = useFreshCallback((startingPoint, limit, callback) => { var _collectionRef$curren, _sdk$currentUser5, _collectionRef$curren2; if (collectionRef.current) (_collectionRef$curren = collectionRef.current) === null || _collectionRef$curren === void 0 || _collectionRef$curren.dispose(); channelMarkAsRead(); updateNewMessages([], true, (_sdk$currentUser5 = sdk.currentUser) === null || _sdk$currentUser5 === void 0 ? void 0 : _sdk$currentUser5.userId); collectionInitializedRef.current = false; collectionRef.current = createMessageCollection(channel, limit, { ...options, startingPoint }); (_collectionRef$curren2 = collectionRef.current) === null || _collectionRef$curren2 === void 0 || _collectionRef$curren2.setMessageCollectionHandler({ onMessagesAdded: (ctx, __, messages) => { var _sdk$currentUser6; channelMarkAsRead(ctx.source); updateNewMessagesReceived(ctx.source, messages); updateMessages(messages, false, (_sdk$currentUser6 = sdk.currentUser) === null || _sdk$currentUser6 === void 0 ? void 0 : _sdk$currentUser6.userId); }, onMessagesUpdated: (ctx, __, messages) => { var _sdk$currentUser7; channelMarkAsRead(ctx.source); updateNewMessagesReceived(ctx.source, messages); // NOTE: admin message is not added via onMessagesAdded handler, not checked yet is this a bug. updateMessages(messages, false, (_sdk$currentUser7 = sdk.currentUser) === null || _sdk$currentUser7 === void 0 ? void 0 : _sdk$currentUser7.userId); if (ctx.source === CollectionEventSource.EVENT_MESSAGE_UPDATED) { var _options$onMessagesUp; options === null || options === void 0 || (_options$onMessagesUp = options.onMessagesUpdated) === null || _options$onMessagesUp === void 0 || _options$onMessagesUp.call(options, messages); } }, onMessagesDeleted: (_, __, ___, messages) => { const msgIds = messages.map(it => it.messageId); const reqIds = messages.filter(isSendableMessage).map(it => it.reqId); deleteMessages(msgIds, reqIds); deleteNewMessages(msgIds, reqIds); }, onChannelDeleted: () => { var _options$onChannelDel; options === null || options === void 0 || (_options$onChannelDel = options.onChannelDeleted) === null || _options$onChannelDel === void 0 || _options$onChannelDel.call(options); }, onChannelUpdated: (_, eventChannel) => { if (eventChannel.isGroupChannel() && !isDifferentChannel(eventChannel, channel)) { forceUpdate(); } }, onHugeGapDetected: () => { init(Number.MAX_SAFE_INTEGER, MESSAGE_LIMIT.DEFAULT); } }); collectionRef.current.initialize(MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API).onCacheResult((err, messages) => { if (err) sdk.isCacheEnabled && Logger.error('[useGroupChannelMessagesWithCollection/onCacheResult]', err);else if (messages) { var _sdk$currentUser8; Logger.debug('[useGroupChannelMessagesWithCollection/onCacheResult]', 'message length:', messages.length); updateMessages(messages, true, (_sdk$currentUser8 = sdk.currentUser) === null || _sdk$currentUser8 === void 0 ? void 0 : _sdk$currentUser8.userId); updateUnsentMessages(); } callback === null || callback === void 0 || callback(); }).onApiResult((err, messages) => { if (err) Logger.warn('[useGroupChannelMessagesWithCollection/onApiResult]', err);else if (messages) { var _sdk$currentUser9, _options$onMessagesRe2; Logger.debug('[useGroupChannelMessagesWithCollection/onApiResult]', 'message length:', messages.length); updateMessages(messages, true, (_sdk$currentUser9 = sdk.currentUser) === null || _sdk$currentUser9 === void 0 ? void 0 : _sdk$currentUser9.userId); if (!(options !== null && options !== void 0 && options.startingPoint)) options === null || options === void 0 || (_options$onMessagesRe2 = options.onMessagesReceived) === null || _options$onMessagesRe2 === void 0 || _options$onMessagesRe2.call(options, messages); if (sdk.isCacheEnabled) updateUnsentMessages(); } collectionInitializedRef.current = true; callback === null || callback === void 0 || callback(); }); }); useChannelHandler(sdk, handlerId, { onUserBanned(channel, bannedUser) { if (channel.isGroupChannel() && !isDifferentChannel(channel, channel)) { var _sdk$currentUser10; 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 { forceUpdate(); } } } }); useEffect(() => { // NOTE: Cache read is heavy task, and it prevents smooth ui transition setTimeout(async () => { updateLoading(true); init(initialStartingPoint, initialLimit, () => updateLoading(false)); }, 0); }, [channel.url, userId, options === null || options === void 0 ? void 0 : options.replyType]); useEffect(() => { return () => { var _collectionRef$curren3; if (collectionRef.current) (_collectionRef$curren3 = collectionRef.current) === null || _collectionRef$curren3 === void 0 || _collectionRef$curren3.dispose(); }; }, []); const refresh = useFreshCallback(async () => { updateRefreshing(true); init(Number.MAX_SAFE_INTEGER, MESSAGE_LIMIT.DEFAULT, () => updateRefreshing(false)); }); const prev = useFreshCallback(async () => { var _collectionRef$curren4; if (collectionRef.current && (_collectionRef$curren4 = collectionRef.current) !== null && _collectionRef$curren4 !== void 0 && _collectionRef$curren4.hasPrevious) { try { var _collectionRef$curren5, _sdk$currentUser11; const list = await ((_collectionRef$curren5 = collectionRef.current) === null || _collectionRef$curren5 === void 0 ? void 0 : _collectionRef$curren5.loadPrevious()); updateMessages(list, false, (_sdk$currentUser11 = sdk.currentUser) === null || _sdk$currentUser11 === void 0 ? void 0 : _sdk$currentUser11.userId); } catch {} } }); const hasPrev = useFreshCallback(() => { if (collectionInitializedRef.current && collectionRef.current) { return collectionRef.current.hasPrevious; } else { return false; } }); const next = useFreshCallback(async () => { var _collectionRef$curren6; if (collectionRef.current && (_collectionRef$curren6 = collectionRef.current) !== null && _collectionRef$curren6 !== void 0 && _collectionRef$curren6.hasNext) { try { var _collectionRef$curren7, _sdk$currentUser12; const fetchedList = await ((_collectionRef$curren7 = collectionRef.current) === null || _collectionRef$curren7 === void 0 ? void 0 : _collectionRef$curren7.loadNext()); updateMessages(fetchedList, false, (_sdk$currentUser12 = sdk.currentUser) === null || _sdk$currentUser12 === void 0 ? void 0 : _sdk$currentUser12.userId); } catch {} } }); const hasNext = useFreshCallback(() => { if (collectionInitializedRef.current && collectionRef.current) { return collectionRef.current.hasNext; } else { return false; } }); const sendUserMessage = useFreshCallback((params, onPending) => { return new Promise((resolve, reject) => { channel.sendUserMessage(params).onPending(pendingMessage => { if (pendingMessage.isUserMessage()) { var _sdk$currentUser13; onPending === null || onPending === void 0 || onPending(pendingMessage); updateMessages([pendingMessage], false, (_sdk$currentUser13 = sdk.currentUser) === null || _sdk$currentUser13 === void 0 ? void 0 : _sdk$currentUser13.userId); } }).onSucceeded(sentMessage => { if (sentMessage.isUserMessage()) { var _sdk$currentUser14; updateMessages([sentMessage], false, (_sdk$currentUser14 = sdk.currentUser) === null || _sdk$currentUser14 === void 0 ? void 0 : _sdk$currentUser14.userId); resolve(sentMessage); } }).onFailed((err, failedMessage) => { if (failedMessage) { var _sdk$currentUser15; updateMessages([failedMessage], false, (_sdk$currentUser15 = sdk.currentUser) === null || _sdk$currentUser15 === void 0 ? void 0 : _sdk$currentUser15.userId); } reject(err); }); }); }); const sendFileMessage = useFreshCallback((params, onPending) => { return new Promise((resolve, reject) => { channel.sendFileMessage(params).onPending(pendingMessage => { if (pendingMessage.isFileMessage()) { var _sdk$currentUser16; updateMessages([pendingMessage], false, (_sdk$currentUser16 = sdk.currentUser) === null || _sdk$currentUser16 === void 0 ? void 0 : _sdk$currentUser16.userId); onPending === null || onPending === void 0 || onPending(pendingMessage); } }).onSucceeded(sentMessage => { if (sentMessage.isFileMessage()) { var _sdk$currentUser17; updateMessages([sentMessage], false, (_sdk$currentUser17 = sdk.currentUser) === null || _sdk$currentUser17 === void 0 ? void 0 : _sdk$currentUser17.userId); resolve(sentMessage); } }).onFailed((err, failedMessage) => { if (failedMessage) { var _sdk$currentUser18; updateMessages([failedMessage], false, (_sdk$currentUser18 = sdk.currentUser) === null || _sdk$currentUser18 === void 0 ? void 0 : _sdk$currentUser18.userId); } reject(err); }); }); }); const updateUserMessage = useFreshCallback(async (messageId, params) => { var _sdk$currentUser19; const updatedMessage = await channel.updateUserMessage(messageId, params); updateMessages([updatedMessage], false, (_sdk$currentUser19 = sdk.currentUser) === null || _sdk$currentUser19 === void 0 ? void 0 : _sdk$currentUser19.userId); return updatedMessage; }); const updateFileMessage = useFreshCallback(async (messageId, params) => { var _sdk$currentUser20; const updatedMessage = await channel.updateFileMessage(messageId, params); updateMessages([updatedMessage], false, (_sdk$currentUser20 = sdk.currentUser) === null || _sdk$currentUser20 === void 0 ? void 0 : _sdk$currentUser20.userId); return updatedMessage; }); const resendMessage = useFreshCallback(async failedMessage => { var _sdk$currentUser21; const resentMessage = await (() => { if (failedMessage.isUserMessage()) return channel.resendUserMessage(failedMessage); if (failedMessage.isFileMessage()) return channel.resendFileMessage(failedMessage); return null; })(); if (resentMessage) updateMessages([resentMessage], false, (_sdk$currentUser21 = sdk.currentUser) === null || _sdk$currentUser21 === void 0 ? void 0 : _sdk$currentUser21.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 { try { var _collectionRef$curren8; await ((_collectionRef$curren8 = collectionRef.current) === null || _collectionRef$curren8 === void 0 ? void 0 : _collectionRef$curren8.removeFailedMessage(message.reqId)); } finally { deleteMessages([message.messageId], [message.reqId]); } } }); const resetNewMessages = useFreshCallback(() => { var _sdk$currentUser22; updateNewMessages([], true, (_sdk$currentUser22 = sdk.currentUser) === null || _sdk$currentUser22 === void 0 ? void 0 : _sdk$currentUser22.userId); }); const resetWithStartingPoint = useFreshCallback((startingPoint, callback) => { var _sdk$currentUser23; const limit = shouldUseSearchLimit(startingPoint) ? MESSAGE_LIMIT.SEARCH : MESSAGE_LIMIT.DEFAULT; updateLoading(true); updateMessages([], true, (_sdk$currentUser23 = sdk.currentUser) === null || _sdk$currentUser23 === void 0 ? void 0 : _sdk$currentUser23.userId); init(startingPoint, limit, () => { updateLoading(false); callback === null || callback === void 0 || callback(); }); }); return { loading, refreshing, refresh, messages, next, hasNext, prev, hasPrev, newMessages, resetNewMessages, sendUserMessage, sendFileMessage, updateUserMessage, updateFileMessage, resendMessage, deleteMessage, resetWithStartingPoint }; }; //# sourceMappingURL=useGroupChannelMessagesWithCollection.js.map