diff --git a/index.d.ts b/index.d.ts index dbebe49..372f341 100644 --- a/index.d.ts +++ b/index.d.ts @@ -17,6 +17,7 @@ declare module '@chatwoot/react-native-widget' { }; // This can actually be any object customAttributes?: Record; + conversationCustomAttributes?: Record; } class ChatWootWidget extends React.Component {} diff --git a/src/App.js b/src/App.js index 0df600d..b5fb797 100644 --- a/src/App.js +++ b/src/App.js @@ -17,10 +17,12 @@ const propTypes = { avatar_url: PropTypes.string, email: PropTypes.string, identifier_hash: PropTypes.string, + user_id: PropTypes.number, }), locale: PropTypes.string, colorScheme: PropTypes.oneOf(['dark', 'light', 'auto']), customAttributes: PropTypes.shape({}), + conversationCustomAttributes: PropTypes.shape({}), closeModal: PropTypes.func, }; @@ -32,6 +34,7 @@ const ChatWootWidget = ({ locale = 'en', colorScheme = 'light', customAttributes = {}, + conversationCustomAttributes = {}, closeModal, }) => { const [cwCookie, setCookie] = useState(''); @@ -50,15 +53,6 @@ const ChatWootWidget = ({ appColorScheme, }); return ( - - - - - ); }; diff --git a/src/WebView.js b/src/WebView.js index 2f9de2d..5ac4b3c 100644 --- a/src/WebView.js +++ b/src/WebView.js @@ -1,5 +1,5 @@ -import React from 'react'; -import { StyleSheet, Linking } from 'react-native'; +import React, { useState, useMemo } from 'react'; +import { StyleSheet, Linking, View, ActivityIndicator, Text } from 'react-native'; import { WebView } from 'react-native-webview'; import PropTypes from 'prop-types'; import { isJsonString, storeHelper, generateScripts, getMessage } from './utils'; @@ -12,7 +12,8 @@ const propTypes = { name: PropTypes.string, avatar_url: PropTypes.string, email: PropTypes.string, - identifier_hash: PropTypes.string, + identifier_hash: PropTypes.string,, + user_id: PropTypes.number, }), locale: PropTypes.string, customAttributes: PropTypes.shape({}), @@ -30,6 +31,7 @@ const WebViewComponent = ({ closeModal, }) => { const [currentUrl, setCurrentUrl] = React.useState(null); + const [loading, setLoading] = useState(true); let widgetUrl = `${baseUrl}/widget?website_token=${websiteToken}&locale=${locale}`; if (cwCookie) { @@ -59,43 +61,72 @@ const WebViewComponent = ({ setCurrentUrl(newNavState.url); }; + const opacity = useMemo(() => { + if (loading) { + return { + opacity: 0, + }; + } + return { + opacity: 1, + }; + }, [loading]); + + const renderLoadingComponent = () => { + return ( + + + Loading... + + ); + }; + return ( - { - const { data } = event.nativeEvent; - const message = getMessage(data); - if (isJsonString(message)) { - const parsedMessage = JSON.parse(message); - const { event: eventType, type } = parsedMessage; - if (eventType === 'loaded') { - const { - config: { authToken }, - } = parsedMessage; - storeHelper.storeCookie(authToken); - } - if (type === 'close-widget') { - closeModal(); + + { + const { data } = event.nativeEvent; + const message = getMessage(data); + if (isJsonString(message)) { + const parsedMessage = JSON.parse(message); + const { event: eventType, type } = parsedMessage; + if (eventType === 'loaded') { + const { + config: { authToken }, + } = parsedMessage; + storeHelper.storeCookie(authToken); + } + if (type === 'close-widget') { + closeModal(); + } } - } - }} - scalesPageToFit - useWebKit - sharedCookiesEnabled - javaScriptEnabled={true} - domStorageEnabled={true} - style={styles.WebViewStyle} - injectedJavaScript={injectedJavaScript} - onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} - onNavigationStateChange={handleWebViewNavigationStateChange} - scrollEnabled - /> + }} + scalesPageToFit + useWebKit + sharedCookiesEnabled + javaScriptEnabled={true} + domStorageEnabled={true} + style={[styles.WebViewStyle, opacity]} + injectedJavaScript={injectedJavaScript} + onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} + onNavigationStateChange={handleWebViewNavigationStateChange} + onLoadStart={() => setLoading(true)} + onLoadProgress={() => setLoading(true)} + onLoadEnd={() => setLoading(false)} + scrollEnabled + /> + {loading && renderLoadingComponent()} + ); }; const styles = StyleSheet.create({ + container: { + flex: 1, + }, modal: { flex: 1, borderRadius: 4, @@ -104,6 +135,24 @@ const styles = StyleSheet.create({ webViewContainer: { flex: 1, }, + WebViewStyle: { + flex: 1, + }, + loadingContainer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'rgba(255, 255, 255, 0.9)', + }, + loadingText: { + marginTop: 10, + fontSize: 16, + color: '#666', + }, }); WebViewComponent.propTypes = propTypes; export default WebViewComponent; diff --git a/src/constants.js b/src/constants.js index d4240b6..c54cf01 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,6 +2,7 @@ export const WOOT_PREFIX = 'chatwoot-widget:'; export const POST_MESSAGE_EVENTS = { SET_LOCALE: 'set-locale', SET_CUSTOM_ATTRIBUTES: 'set-custom-attributes', + SET_CONVERSATION_CUSTOM_ATTRIBUTES: 'set-conversation-custom-attributes', SET_USER: 'set-user', SET_COLOR_SCHEME: 'set-color-scheme', }; diff --git a/src/utils.js b/src/utils.js index 2a0fd87..7872b39 100644 --- a/src/utils.js +++ b/src/utils.js @@ -25,7 +25,7 @@ export const createWootPostMessage = (object) => { export const getMessage = (data) => data.replace(WOOT_PREFIX, ''); -export const generateScripts = ({ colorScheme, user, locale, customAttributes }) => { +export const generateScripts = ({ colorScheme, user, locale, customAttributes, conversationCustomAttributes }) => { let script = ''; if (user) { const userObject = { @@ -46,6 +46,13 @@ export const generateScripts = ({ colorScheme, user, locale, customAttributes }) }; script += createWootPostMessage(attributeObject); } + if (conversationCustomAttributes) { + const attributeObject = { + event: POST_MESSAGE_EVENTS.SET_CONVERSATION_CUSTOM_ATTRIBUTES, + conversationCustomAttributes, + }; + script += createWootPostMessage(attributeObject); + } if (colorScheme) { const themeObject = { event: POST_MESSAGE_EVENTS.SET_COLOR_SCHEME, darkMode: colorScheme }; script += createWootPostMessage(themeObject);