@talkjs/react-native
Version:
Official TalkJS SDK for React Native
287 lines (281 loc) • 11 kB
JavaScript
;
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