UNPKG

@talkjs/react-native

Version:

Official TalkJS SDK for React Native

287 lines (281 loc) 11 kB
"use strict"; import React, { useMemo, useImperativeHandle, forwardRef, useContext, useEffect, useState } from 'react'; import { CHATBOX, BLUR_EVENT, FOCUS_EVENT, SEND_MESSAGE_EVENT, TRANSLATION_TOGGLED_EVENT, GET_TEXT_EVENT, KEYUP_EVENT, SEND_MESSAGE_PROMISE_EVENT, LEAVE_PROMISE_EVENT, CURRENT_CONVERSATION_EVENT, LEAVE_CONVERSATION_EVENT, CUSTOM_MESSAGE_ACTION_EVENT, CUSTOM_CONVERSATION_ACTION_EVENT, CONTAINER, SESSION, UI_MOUNTED_MESSAGE } from './constants'; import { getRandomWord } from './utils'; import { SessionContext } from './Session'; import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; class MessageField { #injectJavaScript; #messageField = `${CHATBOX}.messageField`; constructor(injectJavaScript) { this.#injectJavaScript = injectJavaScript; } focus() { this.#injectJavaScript(`${this.#messageField}.focus(); true;`); } typeText(text) { this.#injectJavaScript(`${this.#messageField}.typeText('${text.replaceAll("'", "\\'")}'); true;`); } getText() { return new Promise(resolve => { this.getTextResolver = resolve; this.#injectJavaScript(` ${this.#messageField}.getText().then((result) => { sendToReactNative('${GET_TEXT_EVENT}', result); }); true;`); }); } setText(text) { this.#injectJavaScript(`${this.#messageField}.setText('${text.replaceAll("'", "\\'")}'); true;`); } setVisible(visible) { this.#injectJavaScript(`${this.#messageField}.setVisible(${JSON.stringify(visible)}); true;`); } } function Chatbox(props, ref) { const { conversationBuilder } = props; const { session, injectJavaScript, talkHandlers, customHandlers, setDisableZoom, setKeyboardVerticalOffset, setHideKeyboardAccessoryView } = useContext(SessionContext); const [isLoading, setIsLoading] = useState(true); let getCurrentConversationResolver; useEffect(() => { if (props.disableZoom !== undefined) { setDisableZoom(props.disableZoom); } }, [props.disableZoom]); useEffect(() => { if (typeof props.keyboardVerticalOffset === 'number') { setKeyboardVerticalOffset(props.keyboardVerticalOffset); } }, [props.keyboardVerticalOffset]); useEffect(() => { if (props.hideKeyboardAccessoryView !== undefined) { setHideKeyboardAccessoryView(props.hideKeyboardAccessoryView); } }, [props.hideKeyboardAccessoryView]); useEffect(() => { customHandlers.add(UI_MOUNTED_MESSAGE, () => { setTimeout(() => setIsLoading(false), 150); }); customHandlers.add(CURRENT_CONVERSATION_EVENT, currentConversation => { getCurrentConversationResolver(currentConversation); getCurrentConversationResolver = undefined; }); }, []); useEffect(() => { // This script helps ensure we mark conversations as read when the webview i // visible and also to ensure that a user is noted as being offline when the // app is in the background. injectJavaScript(` document.addEventListener("visibilitychange", () => { if (document.visibilityState === "visible") { ${CHATBOX}.onWindowVisibleChanged(true); } else { ${CHATBOX}.onWindowVisibleChanged(false); } }); true; `); }, []); useEffect(() => { conversationBuilder._setInjectJavaScript(injectJavaScript); customHandlers.add(SEND_MESSAGE_PROMISE_EVENT, ({ key }) => conversationBuilder._resolve(SEND_MESSAGE_PROMISE_EVENT, key)); customHandlers.add(LEAVE_PROMISE_EVENT, ({ key, result }) => conversationBuilder._resolve(LEAVE_PROMISE_EVENT, key, result)); }, [conversationBuilder]); const options = useMemo(() => JSON.stringify({ customEmojis: props.customEmojis, chatSubtitleMode: props.chatSubtitleMode, chatTitleMode: props.chatTitleMode, dir: props.dir, messageField: props.messageField, messageFilter: props.messageFilter, showChatHeader: props.showChatHeader, theme: props.theme, thirdparties: props.thirdparties, translateConversations: props.translateConversations, showTranslationToggle: props.showTranslationToggle, presence: props.presence, captureKeyboardEvents: props.captureKeyboardEvents }), [props.customEmojis, props.chatSubtitleMode, props.chatTitleMode, props.dir, props.messageField, props.messageFilter, props.showChatHeader, props.theme, props.thirdparties, props.translateConversations, props.showTranslationToggle, props.presence, props.captureKeyboardEvents]); useEffect(() => { injectJavaScript(` if (${CHATBOX}) { ${CHATBOX}.destroy(); } ${CHATBOX} = ${SESSION}.createChatbox(${options}); true; `); }, [session, options]); useEffect(() => { injectJavaScript(`${conversationBuilder.toString()} true;`); }, [conversationBuilder]); useEffect(() => { const selectParams = JSON.stringify({ asGuest: props.asGuest, messageId: props.scrollToMessage }); // Select conversation const usesSync = conversationBuilder._usesSynchronization; const conversation = props.asGuest || usesSync ? conversationBuilder._variableName : JSON.stringify(conversationBuilder); injectJavaScript(`${CHATBOX}.select(${conversation}, ${selectParams});`); }, [session, options, conversationBuilder, props.asGuest, props.scrollToMessage]); useEffect(() => { injectJavaScript(` ${CHATBOX}.mount(document.getElementById('${CONTAINER}')).then( () => { sendToReactNative('${UI_MOUNTED_MESSAGE}'); setTimeout(() => { // On initial load the "visibilitychange" event handler is not called // hence the need for this. // The timeout is necessary due to the internal debounce we have for // setting focus. So we add an appropriate amount of delay to get // round it. if (document.visibilityState === "visible") { ${CHATBOX}.onWindowVisibleChanged(true); } }, 1500); } ); true; `); }, [session, options]); // All useEffects below need the session and option objects as dependencies // because if the Session or Chatbox are recreated, we want to add // these listeners afresh useEffect(() => { if (props.enableTranslation !== undefined) { injectJavaScript(` ${CHATBOX}.setTranslationEnabledForConversation( ${JSON.stringify(conversationBuilder)}, ${JSON.stringify(props.enableTranslation)} ); true; `); } }, [session, options, conversationBuilder, props.enableTranslation]); useEffect(() => { if (props.highlightedWords) { injectJavaScript(` ${CHATBOX}.setHighlightedWords(${JSON.stringify(props.highlightedWords)}); true; `); } }, [session, options, conversationBuilder, props.highlightedWords]); useEffect(() => { if (props.onBlur) { const subscription = talkHandlers.add(BLUR_EVENT, CHATBOX, props.onBlur); return () => subscription.unsubscribe(); } return; }, [session, options, props.onBlur]); useEffect(() => { if (props.onFocus) { const subscription = talkHandlers.add(FOCUS_EVENT, CHATBOX, props.onFocus); return () => subscription.unsubscribe(); } return; }, [session, options, props.onFocus]); useEffect(() => { if (props.onKeyup) { const subscription = talkHandlers.add(KEYUP_EVENT, CHATBOX, props.onKeyup); return () => subscription.unsubscribe(); } return; }, [session, options, props.onKeyup]); useEffect(() => { if (props.onSendMessage) { const subscription = talkHandlers.add(SEND_MESSAGE_EVENT, CHATBOX, props.onSendMessage); return () => subscription.unsubscribe(); } return; }, [session, options, props.onSendMessage]); useEffect(() => { if (props.onTranslationToggled) { const subscription = talkHandlers.add(TRANSLATION_TOGGLED_EVENT, CHATBOX, props.onTranslationToggled); return () => subscription.unsubscribe(); } return; }, [session, options, props.onTranslationToggled]); useEffect(() => { if (props.onLeaveConversation) { const subscription = talkHandlers.add(LEAVE_CONVERSATION_EVENT, CHATBOX, props.onLeaveConversation); return () => subscription.unsubscribe(); } return; }, [session, options, props.onLeaveConversation]); useEffect(() => { if (props.onCustomMessageAction) { const subscription = talkHandlers.add(CUSTOM_MESSAGE_ACTION_EVENT, CHATBOX, props.onCustomMessageAction); return () => subscription.unsubscribe(); } return; }, [session, options, props.onCustomMessageAction]); useEffect(() => { if (props.onCustomConversationAction) { const subscription = talkHandlers.add(CUSTOM_CONVERSATION_ACTION_EVENT, CHATBOX, props.onCustomConversationAction); return () => subscription.unsubscribe(); } return; }, [session, options, props.onCustomConversationAction]); const messageField = useMemo(() => { const _messageField = new MessageField(injectJavaScript); customHandlers.add(GET_TEXT_EVENT, data => { _messageField.getTextResolver(data); _messageField.getTextResolver = undefined; }); return _messageField; }, [injectJavaScript, customHandlers]); useImperativeHandle(ref, () => ({ off: event => talkHandlers.off(event), setTranslationEnabledDefault: enabled => { injectJavaScript(` ${CHATBOX}.setTranslationEnabledDefault(${JSON.stringify(enabled)}); true; `); }, messageField, sendLocation: () => injectJavaScript(`${CHATBOX}.sendLocation(); true;`), onCustomMessageAction: (_action, handler) => talkHandlers.add(CUSTOM_MESSAGE_ACTION_EVENT, CHATBOX, handler), onCustomConversationAction: (_action, handler) => talkHandlers.add(CUSTOM_CONVERSATION_ACTION_EVENT, CHATBOX, handler), onLeaveConversation: handler => talkHandlers.add(LEAVE_CONVERSATION_EVENT, CHATBOX, handler), getCurrentConversation: () => new Promise(resolve => { getCurrentConversationResolver = resolve; const intervalId = getRandomWord(10); injectJavaScript(` let ${intervalId}; ${intervalId} = setInterval(() => { const currentConversation = ${CHATBOX}.currentConversation; if (currentConversation !== undefined) { clearInterval(${intervalId}); ${intervalId} = null; sendToReactNative( '${CURRENT_CONVERSATION_EVENT}', currentConversation ); } }, 100); true; `); }) }), [injectJavaScript]); return /*#__PURE__*/_jsxs(_Fragment, { children: [!!isLoading && props.loadingComponent, props.children] }); } const forwardedChatbox = /*#__PURE__*/forwardRef(Chatbox); export { forwardedChatbox as Chatbox }; //# sourceMappingURL=Chatbox.js.map