@sendbird/uikit-chat-hooks
Version:
A set of React hooks for integrating Sendbird chat functionality into your React app.
284 lines (259 loc) • 9.81 kB
text/typescript
import { useRef } from 'react';
import type { BaseMessage, PreviousMessageListQueryParams } from '@sendbird/chat/message';
import type {
SendbirdBaseChannel,
SendbirdGroupChannel,
SendbirdPreviousMessageListQuery,
} from '@sendbird/uikit-utils';
import {
ASYNC_NOOP,
Logger,
confirmAndMarkAsRead,
isDifferentChannel,
isMyMessage,
useAsyncEffect,
useForceUpdate,
useFreshCallback,
useUniqHandlerId,
} from '@sendbird/uikit-utils';
import { useChannelHandler } from '../../handler/useChannelHandler';
import type { UseGroupChannelMessages, UseGroupChannelMessagesOptions } from '../../types';
import { useChannelMessagesReducer } from '../useChannelMessagesReducer';
const createMessageQuery = (channel: SendbirdGroupChannel, options?: UseGroupChannelMessagesOptions) => {
if (options?.queryCreator) return options.queryCreator();
const params: PreviousMessageListQueryParams = { limit: 100, reverse: true };
if (options?.replyType) params.replyType = options.replyType;
return channel.createPreviousMessageListQuery(params);
};
/**
* @deprecated This hook is deprecated and will be replaced by the '@sendbird/uikit-tools' package.
* */
export const useGroupChannelMessagesWithQuery: UseGroupChannelMessages = (sdk, channel, userId, options) => {
const queryRef = useRef<SendbirdPreviousMessageListQuery>();
const handlerId = useUniqHandlerId('useGroupChannelMessagesWithQuery');
const forceUpdate = useForceUpdate();
const {
loading,
refreshing,
messages,
newMessages,
updateMessages,
deleteMessages,
updateNewMessages,
deleteNewMessages,
updateLoading,
updateRefreshing,
} = useChannelMessagesReducer(options?.sortComparator);
const init = useFreshCallback(async (uid?: string) => {
if (uid) {
confirmAndMarkAsRead([channel]);
updateNewMessages([], true, sdk.currentUser?.userId);
queryRef.current = createMessageQuery(channel, options);
if (queryRef.current?.hasNext) {
const list = await queryRef.current?.load();
updateMessages(list, true, sdk.currentUser?.userId);
}
}
});
const channelUpdater = (channel: SendbirdBaseChannel) => {
if (channel.isGroupChannel() && !isDifferentChannel(channel, channel)) {
forceUpdate();
}
};
useChannelHandler(sdk, handlerId, {
// Messages
onMessageReceived(eventChannel, message) {
if (isDifferentChannel(channel, eventChannel)) return;
if (isMyMessage(message, sdk.currentUser?.userId)) return;
confirmAndMarkAsRead([channel]);
updateMessages([message], false, sdk.currentUser?.userId);
if (options?.shouldCountNewMessages?.()) {
updateNewMessages([message], false, sdk.currentUser?.userId);
}
if (options?.onMessagesReceived) {
options.onMessagesReceived([message]);
}
},
onMessageUpdated(eventChannel, message) {
if (isDifferentChannel(channel, eventChannel)) return;
if (isMyMessage(message, sdk.currentUser?.userId)) return;
updateMessages([message], false, sdk.currentUser?.userId);
},
onMessageDeleted(eventChannel, messageId) {
if (isDifferentChannel(channel, eventChannel)) return;
deleteMessages([messageId], []);
deleteNewMessages([messageId], []);
},
async onReactionUpdated(eventChannel, reactionEvent) {
if (isDifferentChannel(channel, eventChannel)) return;
const message = await sdk.message.getMessage({
messageId: reactionEvent.messageId,
includeReactions: true,
includeParentMessageInfo: true,
includeThreadInfo: true,
includeMetaArray: true,
channelUrl: channel.url,
channelType: channel.channelType,
});
if (message) updateMessages([message as BaseMessage], false, sdk.currentUser?.userId);
},
// Channels
onChannelChanged: channelUpdater,
onChannelFrozen: channelUpdater,
onChannelUnfrozen: channelUpdater,
onChannelHidden: channelUpdater,
onChannelMemberCountChanged(channels) {
const foundChannel = channels.find((c) => !isDifferentChannel(c, channel));
if (foundChannel) channelUpdater(foundChannel);
},
onChannelDeleted(channelUrl: string) {
if (channel.url === channelUrl) options?.onChannelDeleted?.();
},
// Users
onOperatorUpdated: channelUpdater,
onUserLeft: channelUpdater,
// onUserEntered: channelUpdater,
// onUserExited: channelUpdater,
onUserJoined: channelUpdater,
onUserUnbanned: channelUpdater,
onUserMuted: channelUpdater,
onUserUnmuted: channelUpdater,
onUserBanned(eventChannel, bannedUser) {
if (isDifferentChannel(channel, eventChannel)) return;
if (bannedUser.userId === sdk.currentUser?.userId) {
options?.onChannelDeleted?.();
} else {
channelUpdater(eventChannel);
}
},
});
useAsyncEffect(async () => {
updateLoading(true);
await init(userId);
updateLoading(false);
}, [channel.url, userId]);
const refresh: ReturnType<UseGroupChannelMessages>['refresh'] = useFreshCallback(async () => {
updateRefreshing(true);
await init(userId);
updateRefreshing(false);
});
const prev: ReturnType<UseGroupChannelMessages>['prev'] = useFreshCallback(async () => {
if (queryRef.current && queryRef.current?.hasNext) {
const list = await queryRef.current?.load();
updateMessages(list, false, sdk.currentUser?.userId);
}
});
const hasPrev: ReturnType<UseGroupChannelMessages>['hasPrev'] = useFreshCallback(
() => queryRef.current?.hasNext ?? false,
);
const next: ReturnType<UseGroupChannelMessages>['next'] = useFreshCallback(ASYNC_NOOP);
const hasNext: ReturnType<UseGroupChannelMessages>['hasNext'] = useFreshCallback(() => false);
const sendUserMessage: ReturnType<UseGroupChannelMessages>['sendUserMessage'] = useFreshCallback(
(params, onPending) => {
return new Promise((resolve, reject) => {
channel
.sendUserMessage(params)
.onPending((pendingMessage) => {
if (pendingMessage.isUserMessage()) {
updateMessages([pendingMessage], false, sdk.currentUser?.userId);
onPending?.(pendingMessage);
}
})
.onSucceeded((sentMessage) => {
if (sentMessage.isUserMessage()) {
updateMessages([sentMessage], false, sdk.currentUser?.userId);
resolve(sentMessage);
}
})
.onFailed((err, failedMessage) => {
if (failedMessage) {
updateMessages([failedMessage], false, sdk.currentUser?.userId);
}
reject(err);
});
});
},
);
const sendFileMessage: ReturnType<UseGroupChannelMessages>['sendFileMessage'] = useFreshCallback(
(params, onPending) => {
return new Promise((resolve, reject) => {
channel
.sendFileMessage(params)
.onPending((pendingMessage) => {
if (pendingMessage.isFileMessage()) {
updateMessages([pendingMessage], false, sdk.currentUser?.userId);
onPending?.(pendingMessage);
}
})
.onSucceeded((sentMessage) => {
if (sentMessage.isFileMessage()) {
updateMessages([sentMessage], false, sdk.currentUser?.userId);
resolve(sentMessage);
}
})
.onFailed((err, failedMessage) => {
if (failedMessage) {
updateMessages([failedMessage], false, sdk.currentUser?.userId);
}
reject(err);
});
});
},
);
const updateUserMessage: ReturnType<UseGroupChannelMessages>['updateUserMessage'] = useFreshCallback(
async (messageId, params) => {
const updatedMessage = await channel.updateUserMessage(messageId, params);
updateMessages([updatedMessage], false, sdk.currentUser?.userId);
return updatedMessage;
},
);
const updateFileMessage: ReturnType<UseGroupChannelMessages>['updateFileMessage'] = useFreshCallback(
async (messageId, params) => {
const updatedMessage = await channel.updateFileMessage(messageId, params);
updateMessages([updatedMessage], false, sdk.currentUser?.userId);
return updatedMessage;
},
);
const resendMessage: ReturnType<UseGroupChannelMessages>['resendMessage'] = useFreshCallback(
async (failedMessage) => {
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.currentUser?.userId);
},
);
const deleteMessage: ReturnType<UseGroupChannelMessages>['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: ReturnType<UseGroupChannelMessages>['resetNewMessages'] = useFreshCallback(() => {
updateNewMessages([], true, sdk.currentUser?.userId);
});
return {
loading,
refreshing,
refresh,
messages,
newMessages,
resetNewMessages,
next,
hasNext,
prev,
hasPrev,
sendUserMessage,
sendFileMessage,
updateUserMessage,
updateFileMessage,
resendMessage,
deleteMessage,
resetWithStartingPoint() {
Logger.warn('resetWithStartingPoint is not supported in Query, please use Collection instead.');
},
};
};