Skip to content

Instantly share code, notes, and snippets.

@hemantasapkota
Last active January 4, 2025 04:58
Show Gist options
  • Save hemantasapkota/2450abfe4641915dddebd1a74400dc65 to your computer and use it in GitHub Desktop.
Save hemantasapkota/2450abfe4641915dddebd1a74400dc65 to your computer and use it in GitHub Desktop.
Chatwoot React Native Widget Loading Indicator Fix
import React, {useMemo, useState} from 'react';
import {StyleSheet, Linking, Text, View} from 'react-native';
import {isJsonString, storeHelper, generateScripts, getMessage} from './utils';
import {WebView} from 'react-native-webview';
export interface ChatWootWidgetProps {
websiteToken: string;
locale?: string;
baseUrl: string;
cwCookie: string;
colorScheme?: 'light' | 'auto' | 'dark';
closeModal: () => void;
isLoading?: boolean;
user?: {
identifier?: string;
name?: string;
avatar_url?: string;
email?: string;
identifier_hash?: string;
};
// This can actually be any object
customAttributes?: Record<string, unknown>;
}
export const ChatwootWidget = ({
baseUrl,
websiteToken,
cwCookie = '',
locale = 'en',
colorScheme = 'light',
user = {},
customAttributes = {},
isLoading = false,
closeModal,
}: ChatWootWidgetProps) => {
const [loading, setLoading] = useState(isLoading);
const [currentUrl, setCurrentUrl] = React.useState(null);
let widgetUrl = `${baseUrl}/widget?website_token=${websiteToken}&locale=${locale}`;
if (cwCookie) {
widgetUrl = `${widgetUrl}&cw_conversation=${cwCookie}`;
}
const injectedJavaScript = generateScripts({
user,
locale,
customAttributes,
colorScheme,
});
const onShouldStartLoadWithRequest = request => {
const isMessageView = currentUrl && currentUrl.includes('#/messages');
const isAttachmentUrl = !widgetUrl.includes(request.url);
// Open the attachments only in the external browser
const shouldRedirectToBrowser = isMessageView && isAttachmentUrl;
if (shouldRedirectToBrowser) {
Linking.openURL(request.url);
return false;
}
return true;
};
const handleWebViewNavigationStateChange = (newNavState: any) => {
setCurrentUrl(newNavState.url);
};
const opacity = useMemo(() => {
if (loading) {
return {
opacity: 1,
};
}
return {
opacity: 1,
};
}, [loading]);
return (
<>
{loading && (
<View className="flex-1 items-center justify-center">
<Text>Please wait...</Text>
</View>
)}
<WebView
source={{
uri: widgetUrl,
}}
onMessage={(event: any) => {
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.webViewContainer, opacity]}
injectedJavaScript={injectedJavaScript}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
onNavigationStateChange={handleWebViewNavigationStateChange}
onLoadStart={() => setLoading(true)}
onLoadProgress={() => setLoading(true)}
onLoadEnd={() => setLoading(false)}
scrollEnabled
/>
</>
);
};
const styles = StyleSheet.create({
modal: {
flex: 1,
borderRadius: 4,
overflow: 'hidden',
},
webViewContainer: {
flex: 1,
backgroundColor: 'transparent',
},
});
@hemantasapkota
Copy link
Author

@losh11 The remaining source code can be found in the chatwoot-react-native-widget repository. For your reference:

  1. utils.js
  2. constants.js
  3. style.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment