UNPKG

@cometchat/chat-uikit-react

Version:

Ready-to-use Chat UI Components for React

596 lines (591 loc) 22.2 kB
import './index.css'; import { CometChatPluginRegistryContext, CometChatMessageBubble, CometChatDate } from './chunk-3WCEOEGP.js'; import { CometChatAvatar } from './chunk-LDXTHTWH.js'; import { useCometChatFrameContext } from './chunk-UTFM6ET6.js'; import { useLocale } from './chunk-UVWJYQJ3.js'; import { createContext, useContext, useMemo, useRef, useCallback, useEffect, useReducer, useId } from 'react'; import { CometChat } from '@cometchat/chat-sdk-javascript'; import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; var CometChatMessageInformationContext = createContext(null); function useCometChatMessageInformationContext() { const ctx = useContext(CometChatMessageInformationContext); if (!ctx) { throw new Error( "useCometChatMessageInformationContext must be used within <CometChatMessageInformation.Root>" ); } return ctx; } async function fetchReceipts(messageId) { return CometChat.getMessageReceipts(messageId); } function attachReceiptListener(listenerId, callbacks) { CometChat.addMessageListener( listenerId, new CometChat.MessageListener({ onMessagesDelivered: callbacks.onMessagesDelivered, onMessagesRead: callbacks.onMessagesRead }) ); return () => { CometChat.removeMessageListener(listenerId); }; } function attachConnectionListener(listenerId, onConnected) { CometChat.addConnectionListener( listenerId, new CometChat.ConnectionListener({ onConnected, onDisconnected: () => { } }) ); return () => { CometChat.removeConnectionListener(listenerId); }; } // src/components/CometChatMessageInformation/CometChatMessageInformation.reducer.ts var initialState = { fetchState: "idle", userReceipts: [], oneOnOneReadAt: 0, oneOnOneDeliveredAt: 0, error: null }; function messageInformationReducer(state, action) { switch (action.type) { case "FETCH_START": return { ...state, fetchState: "loading", error: null }; case "FETCH_SUCCESS_GROUP": return { ...state, fetchState: action.userReceipts.length === 0 ? "empty" : "loaded", userReceipts: action.userReceipts }; case "FETCH_SUCCESS_ONE_ON_ONE": return { ...state, fetchState: "loaded", oneOnOneReadAt: action.readAt, oneOnOneDeliveredAt: action.deliveredAt }; case "FETCH_ERROR": return { ...state, fetchState: "error", error: action.error }; case "UPDATE_RECEIPT": { const updated = state.userReceipts.map( (r) => r.user.getUid() === action.uid ? { ...r, readAt: action.readAt ?? r.readAt, deliveredAt: action.deliveredAt ?? r.deliveredAt } : r ); return { ...state, userReceipts: updated }; } case "UPDATE_ONE_ON_ONE_RECEIPT": return { ...state, oneOnOneReadAt: action.readAt ?? state.oneOnOneReadAt, oneOnOneDeliveredAt: action.deliveredAt ?? state.oneOnOneDeliveredAt }; case "RESET": return initialState; default: return state; } } // src/components/CometChatMessageInformation/useCometChatMessageInformation.ts function useCometChatMessageInformation(options) { const { message, onError } = options; const [state, dispatch] = useReducer(messageInformationReducer, initialState); const fetchIdRef = useRef(""); const loggedInUserRef = useRef(""); const instanceId = useId(); const isGroupMessage = message.getReceiverType() === CometChat.RECEIVER_TYPE.GROUP; const handleError = useCallback( (error) => { onError?.(error); const errorMessage = error instanceof Error ? error.message : "Unknown error"; dispatch({ type: "FETCH_ERROR", error: errorMessage }); }, [onError] ); const doFetchReceipts = useCallback(async () => { const currentFetchId = `fetch_${String(Date.now())}`; fetchIdRef.current = currentFetchId; dispatch({ type: "FETCH_START" }); try { if (!loggedInUserRef.current) { const user = await CometChat.getLoggedinUser(); if (user) { loggedInUserRef.current = user.getUid(); } } if (isGroupMessage) { const receipts = await fetchReceipts(message.getId()); if (fetchIdRef.current !== currentFetchId) return; if (receipts.length === 0) { dispatch({ type: "FETCH_SUCCESS_GROUP", userReceipts: [] }); return; } const userReceiptMap = /* @__PURE__ */ new Map(); for (const receipt of receipts) { const sender = receipt.getSender(); if (sender.getUid() === loggedInUserRef.current) { continue; } const uid = sender.getUid(); const readAt = receipt.getReadAt(); const deliveredAt = receipt.getDeliveredAt(); const existing = userReceiptMap.get(uid); if (existing) { if (readAt > existing.readAt) existing.readAt = readAt; if (deliveredAt > existing.deliveredAt) existing.deliveredAt = deliveredAt; } else { userReceiptMap.set(uid, { user: sender, readAt, deliveredAt }); } } dispatch({ type: "FETCH_SUCCESS_GROUP", userReceipts: Array.from(userReceiptMap.values()) }); } else { if (fetchIdRef.current !== currentFetchId) return; dispatch({ type: "FETCH_SUCCESS_ONE_ON_ONE", readAt: message.getReadAt(), deliveredAt: message.getDeliveredAt() }); } } catch (error) { if (fetchIdRef.current !== currentFetchId) return; handleError(error); } }, [message, isGroupMessage, handleError]); useEffect(() => { dispatch({ type: "RESET" }); void doFetchReceipts(); }, [doFetchReceipts]); useEffect(() => { const listenerId = `CometChatMessageInformation_receipt_${instanceId}`; let cleanup; try { cleanup = attachReceiptListener(listenerId, { onMessagesDelivered: (receipt) => { const sender = receipt.getSender(); if (isGroupMessage) { if (sender.getUid() !== loggedInUserRef.current) { dispatch({ type: "UPDATE_RECEIPT", uid: sender.getUid(), deliveredAt: receipt.getDeliveredAt() }); } } else { dispatch({ type: "UPDATE_ONE_ON_ONE_RECEIPT", deliveredAt: receipt.getDeliveredAt() }); } }, onMessagesRead: (receipt) => { const sender = receipt.getSender(); if (isGroupMessage) { if (sender.getUid() !== loggedInUserRef.current) { dispatch({ type: "UPDATE_RECEIPT", uid: sender.getUid(), readAt: receipt.getReadAt() }); } } else { dispatch({ type: "UPDATE_ONE_ON_ONE_RECEIPT", readAt: receipt.getReadAt() }); } } }); } catch { } return () => { cleanup?.(); }; }, [instanceId, isGroupMessage]); useEffect(() => { const listenerId = `CometChatMessageInformation_conn_${instanceId}`; let cleanup; try { cleanup = attachConnectionListener(listenerId, () => { dispatch({ type: "RESET" }); void doFetchReceipts(); }); } catch { } return () => { cleanup?.(); }; }, [instanceId, doFetchReceipts]); const retry = useCallback(() => { dispatch({ type: "RESET" }); void doFetchReceipts(); }, [doFetchReceipts]); return { ...state, isGroupMessage, retry }; } var CometChatMessageInformationHeader = ({ className }) => { const { onClose } = useCometChatMessageInformationContext(); const { getLocalizedString } = useLocale(); const headerClass = ["cometchat-message-information__header", className ?? ""].filter(Boolean).join(" "); return /* @__PURE__ */ jsxs("div", { className: headerClass, children: [ /* @__PURE__ */ jsx( "h2", { id: "cometchat-message-info-title", className: "cometchat-message-information__header-title", children: getLocalizedString("message_information_title") } ), /* @__PURE__ */ jsx( "button", { type: "button", className: "cometchat-message-information__header-close-button", "aria-label": getLocalizedString("message_information_close_hover"), "data-cometchat-message-info-close": true, onClick: onClose, children: /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__header-close-icon", "aria-hidden": "true" }) } ) ] }); }; var CometChatMessageInformationMessagePreview = ({ className }) => { const { message, showScrollbar } = useCometChatMessageInformationContext(); const { getLocalizedString } = useLocale(); const registry = useContext(CometChatPluginRegistryContext); const contentView = useMemo(() => { if (!registry) return null; const plugin = registry.findPlugin(message); if (!plugin) return null; const sender = message.getSender(); const pluginContext = { loggedInUser: sender, alignment: "right", // Always show as outgoing in the info panel theme: "light", getLocalizedString }; try { return plugin.renderBubble(message, pluginContext); } catch { return null; } }, [registry, message, getLocalizedString]); const sectionClass = [ "cometchat-message-information__message-preview", !showScrollbar ? "cometchat-message-information__message-preview--hide-scrollbar" : "", className ?? "" ].filter(Boolean).join(" "); if (contentView) { return /* @__PURE__ */ jsx("div", { className: sectionClass, children: /* @__PURE__ */ jsx( CometChatMessageBubble, { message, alignment: "right", contentView, hideAvatar: true, hideSenderName: true, hideReceipts: true, hideThreadView: true, disableInteraction: true } ) }); } const messageType = message.getType(); let previewText = ""; if (messageType === "text") { const textMsg = message; previewText = textMsg.getText(); } else if (messageType === "image") { previewText = "\u{1F4F7} Photo"; } else if (messageType === "video") { previewText = "\u{1F3A5} Video"; } else if (messageType === "audio") { previewText = "\u{1F3B5} Audio"; } else if (messageType === "file") { previewText = "\u{1F4CE} File"; } else { previewText = getLocalizedString("message_fallback_text"); } return /* @__PURE__ */ jsx("div", { className: sectionClass, children: /* @__PURE__ */ jsx("div", { className: "cometchat-message-information__message-preview-content", children: previewText }) }); }; var CometChatMessageInformationLoadingState = ({ className }) => { const loadingClass = ["cometchat-message-information__loading", className ?? ""].filter(Boolean).join(" "); return /* @__PURE__ */ jsx("div", { className: loadingClass, role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsx("div", { className: "cometchat-message-information__spinner" }) }); }; var CometChatMessageInformationErrorState = ({ className }) => { const { retry } = useCometChatMessageInformationContext(); const { getLocalizedString } = useLocale(); const errorClass = ["cometchat-message-information__error", className ?? ""].filter(Boolean).join(" "); return /* @__PURE__ */ jsxs("div", { className: errorClass, role: "alert", children: [ /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__error-text", children: getLocalizedString("message_information_error") }), /* @__PURE__ */ jsx( "button", { type: "button", className: "cometchat-message-information__error-retry-button", onClick: retry, children: getLocalizedString("retry") } ) ] }); }; var CometChatMessageInformationEmptyState = ({ className }) => { const { getLocalizedString } = useLocale(); const emptyClass = ["cometchat-message-information__empty", className ?? ""].filter(Boolean).join(" "); return /* @__PURE__ */ jsx("div", { className: emptyClass, children: /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__empty-text", children: getLocalizedString("message_information_group_message_receipt_empty") }) }); }; var CometChatMessageInformationReceiptList = ({ className }) => { const { fetchState, userReceipts, oneOnOneReadAt, oneOnOneDeliveredAt, isGroupMessage, showScrollbar, messageInfoDateTimeFormat } = useCometChatMessageInformationContext(); const { getLocalizedString } = useLocale(); const contentClass = [ "cometchat-message-information__content", !showScrollbar ? "cometchat-message-information__content--hide-scrollbar" : "", className ?? "" ].filter(Boolean).join(" "); if (fetchState === "loading" && userReceipts.length === 0 && !oneOnOneReadAt && !oneOnOneDeliveredAt) { return /* @__PURE__ */ jsx("div", { className: contentClass, children: /* @__PURE__ */ jsx(CometChatMessageInformationLoadingState, {}) }); } if (fetchState === "error" && userReceipts.length === 0 && !oneOnOneReadAt && !oneOnOneDeliveredAt) { return /* @__PURE__ */ jsx("div", { className: contentClass, children: /* @__PURE__ */ jsx(CometChatMessageInformationErrorState, {}) }); } return /* @__PURE__ */ jsxs("div", { className: contentClass, children: [ isGroupMessage && fetchState !== "loading" && /* @__PURE__ */ jsx(Fragment, { children: userReceipts.length > 0 ? /* @__PURE__ */ jsx( "ul", { className: "cometchat-message-information__user-list", role: "list", "aria-label": getLocalizedString("message_information_title"), children: userReceipts.map((receipt) => /* @__PURE__ */ jsxs( "li", { className: "cometchat-message-information__user-item", role: "listitem", children: [ /* @__PURE__ */ jsx( "div", { className: "cometchat-message-information__user-item-avatar", "aria-hidden": "true", children: /* @__PURE__ */ jsx( CometChatAvatar, { image: receipt.user.getAvatar(), name: receipt.user.getName(), size: "large" } ) } ), /* @__PURE__ */ jsxs("div", { className: "cometchat-message-information__user-item-content", children: [ /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__user-item-name", children: receipt.user.getName() }), receipt.readAt > 0 && /* @__PURE__ */ jsxs("div", { className: "cometchat-message-information__user-item-receipt-row", children: [ /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__user-item-receipt-label", children: getLocalizedString("message_information_read") }), /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__user-item-receipt-time", children: /* @__PURE__ */ jsx( CometChatDate, { timestamp: receipt.readAt, formatConfig: messageInfoDateTimeFormat } ) }) ] }), receipt.deliveredAt > 0 && /* @__PURE__ */ jsxs("div", { className: "cometchat-message-information__user-item-receipt-row", children: [ /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__user-item-receipt-label", children: getLocalizedString("message_information_delivered") }), /* @__PURE__ */ jsx("span", { className: "cometchat-message-information__user-item-receipt-time", children: /* @__PURE__ */ jsx( CometChatDate, { timestamp: receipt.deliveredAt, formatConfig: messageInfoDateTimeFormat } ) }) ] }) ] }) ] }, receipt.user.getUid() )) } ) : fetchState !== "error" && /* @__PURE__ */ jsx(CometChatMessageInformationEmptyState, {}) }), !isGroupMessage && fetchState !== "loading" && /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsxs("div", { className: "cometchat-message-information__section", children: [ /* @__PURE__ */ jsx("h3", { className: "cometchat-message-information__section-title", children: getLocalizedString("message_information_read") }), oneOnOneReadAt > 0 ? /* @__PURE__ */ jsx("div", { className: "cometchat-message-information__section-time", children: /* @__PURE__ */ jsx( CometChatDate, { timestamp: oneOnOneReadAt, formatConfig: messageInfoDateTimeFormat } ) }) : /* @__PURE__ */ jsx( "span", { className: "cometchat-message-information__section-empty-dash", "aria-label": getLocalizedString("message_information_not_read"), children: "\u2014" } ) ] }), /* @__PURE__ */ jsxs("div", { className: "cometchat-message-information__section", children: [ /* @__PURE__ */ jsx("h3", { className: "cometchat-message-information__section-title", children: getLocalizedString("message_information_delivered") }), oneOnOneDeliveredAt > 0 ? /* @__PURE__ */ jsx("div", { className: "cometchat-message-information__section-time", children: /* @__PURE__ */ jsx( CometChatDate, { timestamp: oneOnOneDeliveredAt, formatConfig: messageInfoDateTimeFormat } ) }) : /* @__PURE__ */ jsx( "span", { className: "cometchat-message-information__section-empty-dash", "aria-label": getLocalizedString("message_information_not_delivered"), children: "\u2014" } ) ] }) ] }) ] }); }; var DEFAULT_DATE_FORMAT = { today: "DD MMM, hh:mm A", yesterday: "DD MMM, hh:mm A", otherDays: "DD MMM, hh:mm A" }; var CometChatMessageInformationRoot = ({ message, onClose, onError, messageInfoDateTimeFormat, messageSentAtDateTimeFormat, textFormatters, showScrollbar = false, children, className }) => { const panelRef = useRef(null); const previousFocusRef = useRef(null); const IframeContext = useCometChatFrameContext(); const getCurrentDocument = useCallback(() => { return IframeContext.iframeDocument ?? document; }, [IframeContext.iframeDocument]); const { fetchState, userReceipts, oneOnOneReadAt, oneOnOneDeliveredAt, error, isGroupMessage, retry } = useCometChatMessageInformation({ message, onError }); const handleClose = useCallback(() => { onClose?.(); }, [onClose]); useEffect(() => { previousFocusRef.current = getCurrentDocument().activeElement; panelRef.current?.focus(); return () => { previousFocusRef.current?.focus(); }; }, [getCurrentDocument]); const handleKeyDown = useCallback( (event) => { if (event.key === "Escape") { event.preventDefault(); handleClose(); return; } if (event.key === "Tab" && panelRef.current) { const focusableElements = panelRef.current.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); if (focusableElements.length === 0) return; const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; if (event.shiftKey) { if (getCurrentDocument().activeElement === firstElement) { event.preventDefault(); lastElement?.focus(); } } else { if (getCurrentDocument().activeElement === lastElement) { event.preventDefault(); firstElement?.focus(); } } } }, [handleClose, getCurrentDocument] ); const contextValue = useMemo( () => ({ message, fetchState, userReceipts, oneOnOneReadAt, oneOnOneDeliveredAt, error, isGroupMessage, messageInfoDateTimeFormat: messageInfoDateTimeFormat ?? DEFAULT_DATE_FORMAT, messageSentAtDateTimeFormat, textFormatters: textFormatters ?? [], showScrollbar, onClose: handleClose, retry }), [ message, fetchState, userReceipts, oneOnOneReadAt, oneOnOneDeliveredAt, error, isGroupMessage, messageInfoDateTimeFormat, messageSentAtDateTimeFormat, textFormatters, showScrollbar, handleClose, retry ] ); const rootClass = ["cometchat-message-information", className ?? ""].filter(Boolean).join(" "); return /* @__PURE__ */ jsx(CometChatMessageInformationContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx( "div", { ref: panelRef, className: rootClass, tabIndex: -1, role: "dialog", "aria-modal": "true", "aria-labelledby": "cometchat-message-info-title", onKeyDown: handleKeyDown, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(CometChatMessageInformationHeader, {}), /* @__PURE__ */ jsx(CometChatMessageInformationMessagePreview, {}), /* @__PURE__ */ jsx(CometChatMessageInformationReceiptList, {}) ] }) } ) }); }; export { CometChatMessageInformationEmptyState, CometChatMessageInformationErrorState, CometChatMessageInformationHeader, CometChatMessageInformationLoadingState, CometChatMessageInformationMessagePreview, CometChatMessageInformationReceiptList, CometChatMessageInformationRoot, useCometChatMessageInformationContext };