Skip to content

Instantly share code, notes, and snippets.

@rachidelaid
Forked from mshivam019/ScreenWebview.jsx
Last active March 10, 2025 15:09
Show Gist options
  • Save rachidelaid/bc608982aa3d6a2a770e3ab499b729a5 to your computer and use it in GitHub Desktop.
Save rachidelaid/bc608982aa3d6a2a770e3ab499b729a5 to your computer and use it in GitHub Desktop.
A feature rich webview for react native
import {useState, useEffect, useRef, useCallback} from 'react';
import {View,Text,BackHandler, Share as RNshare, Platform} from 'react-native';
import {WebView} from 'react-native-webview';
import {useRoute, useNavigation} from '@react-navigation/native';
import {useFocusEffect, useIsFocused} from '@react-navigation/native';
import Share from 'react-native-share';
import ReactNativeBlobUtil from 'react-native-blob-util';
import styled from 'styled-components/native';
import axios from 'axios';
import HeaderComponentWithBackButton from '../components/HeaderComponentWithBackButton';
import NetworkErrorModalWithRetryButton from '../components/NetworkErrorModalWithRetryButton';
import Loader from '../components/Loader';
import WarningIcon from '../icon/WarningIcon';
const Toast = styled.View`
position: absolute;
bottom: 20px;
padding: 10px;
border-radius: 8px;
align-self: center;
background-color: #000;
justify-content: center;
align-items: center;
z-index: 999;
`;
const LoaderContainer = styled(View)`
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
background-color: #fff;
`;
export default function ScreenWebview({}) {
const route = useRoute();
const navigation = useNavigation();
const webviewRef = useRef(null);
const isFocused = useIsFocused();
const [url, setUrl] = useState(route.params?.url ? route.params.url : '');
const [showNetworkModal, setShowNetworkModal] = useState(false);
const [showToast, setShowToast] = useState(false);
// State to trigger a re-render of the webview
const [webviewKey, setWebviewKey] = useState(false);
const canGoBackRef = useRef(false);
const currentUrlRef = useRef('');
const injectedJs = `
(function(){
console.log('injected js');
})();
`;
const onMessage = async (event) => {
let eventData = {};
try {
eventData = JSON.parse(event.nativeEvent.data);
} catch (error) {
// not a json string
}
if (event.nativeEvent.data === 'someEvent') {
//action for that event
} else if (event.nativeEvent.data === 'someOtherEvent') {
//action for that event
} else if (eventData?.type === 'open-share-modal') {
//example of how to open a share modal
//do this in your frontend
// window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'open-share-modal', data: { message: `some message: \n${window.location.href}` } }));
const shareData = eventData?.data;
RNshare.share({
message: shareData?.message,
url: shareData?.url,
title: shareData?.title,
});
} else if (eventData?.type === 'open-external-link') {
setUrl(eventData?.url);
} else if (eventData?.type === 'open-internal-link') {
//example of how to navigate to a different screen
//do this in your frontend
// window.ReactNativeWebView.postMessage(JSON.stringify({ data:{screen:"screenName",params:{screen:"nestedScreenName",params:{paramKey:paramValue}}},type:"open-internal-link" }));
navigation.navigate(
eventData?.data?.screen,
eventData?.data?.params,
);
} else if (eventData?.type === 'download') {
//example of how to download a file
//do this in your frontend
//window.ReactNativeWebView.postMessage(JSON.stringify({type:"download",url:url, name:"filename",nameWithExtension:"fileName.pdf",mimeType:"application/pdf"}))
if (Platform.OS === 'ios') {
try {
let dirs = ReactNativeBlobUtil.fs.dirs;
ReactNativeBlobUtil.config({
fileCache: true,
path:
dirs.DocumentDir +
`/${eventData.nameWithExtension}`,
})
.fetch('GET', eventData?.url)
.then(async (res) => {
await Share.open({
url: res.path(),
saveToFiles: true,
filename: eventData?.name,
type: eventData?.mimeType,
});
await ReactNativeBlobUtil.fs
.unlink(res.path())
.catch((err) => {
console.log(err);
});
})
.catch((err) => {
console.log(err);
});
} catch (err) {
console.log(err);
}
} else {
try {
ReactNativeBlobUtil.config({
fileCache: true,
})
.fetch('GET', eventData?.url)
.then(async (res) => {
await ReactNativeBlobUtil.MediaCollection.copyToMediaStore(
{
name: eventData?.name,
parentFolder: '',
mimeType: eventData?.mimeType,
},
'Download',
res.path(),
);
await ReactNativeBlobUtil.fs
.unlink(res.path())
.catch((err) => {
console.log(err);
});
setShowToast(true);
setTimeout(() => {
setShowToast(false);
}, 2000);
})
.catch((err) => {
console.log(err);
});
} catch (err) {
console.log(err);
}
}
} else if (eventData?.type === 'share') {
//example of how to share a file
//do this in your frontend
//window.ReactNativeWebView.postMessage(JSON.stringify({type:"share",url:url, name:"filename",nameWithExtension:"filename.pdf",mimeType:"application/pdf"}))
try {
let dirs = ReactNativeBlobUtil.fs.dirs;
ReactNativeBlobUtil.config({
fileCache: true,
path: dirs.DocumentDir + `/${eventData.nameWithExtension}`,
})
.fetch('GET', eventData?.url)
.then(async (res) => {
await Share.open({
url:
Platform.OS === 'ios'
? res.path()
: `file://${res.path()}`,
filename: eventData?.name,
type: eventData?.mimeType,
});
await ReactNativeBlobUtil.fs
.unlink(res.path())
.catch((err) => {
console.log(err);
});
})
.catch((err) => {
console.log(err);
});
} catch (err) {
console.log(err);
}
} else if (eventData?.type === 'download-base64') {
//example of how to download a base64 file
//do this in your frontend
// window.ReactNativeWebView.postMessage(JSON.stringify({type: 'download-base64', url: url, name: 'fileName',nameWithExtension: 'fileName.pdf',mimeType: 'application/pdf'}));
if (Platform.OS === 'ios') {
try {
Share.open({
url: `data:${eventData.mimeType};base64${eventData?.url}`,
saveToFiles: true,
filename: eventData?.name,
type: eventData?.mimeType,
});
} catch (err) {
console.log(err);
}
} else {
try {
const path =
ReactNativeBlobUtil.fs.dirs.DownloadDir +
`/${eventData.nameWithExtension}`;
ReactNativeBlobUtil.fs
.writeFile(path, eventData?.url, 'base64')
.then(async () => {
await ReactNativeBlobUtil.MediaCollection.copyToMediaStore(
{
name: eventData?.name,
parentFolder: '',
mimeType: eventData?.mimeType,
},
'Download',
path,
);
setShowToast(true);
setTimeout(() => {
setShowToast(false);
}, 2000);
});
ReactNativeBlobUtil.fs.unlink(path).catch((err) => {
console.log(err);
});
} catch (err) {
console.log(err);
}
}
}
};
useEffect(() => {
if (isFocused) {
const checkInternet = async () => {
try {
const res = await axios.get('https://google.com');
if (res.status === 200) {
setShowNetworkModal(false);
} else {
setShowNetworkModal(true);
}
} catch (err) {
console.log('err', err);
setShowNetworkModal(true);
}
};
checkInternet();
}
}, [isFocused]);
const onBackPress = () => {
if (canGoBackRef.current) {
if (currentUrlRef.current === url) {
return false;
} else {
webviewRef.current.goBack();
return true;
}
}
};
useFocusEffect(
useCallback(() => {
const subscription = BackHandler.addEventListener(
'hardwareBackPress',
onBackPress,
);
navigation.setOptions({
headerShown: true,
header: () => (
//header compoenent must have a back button due to ios back limitations
<HeaderComponentWithBackButton onPress={() => onBackPress()} showBack={true} />
),
});
return () => subscription.remove();
}, [currentUrlRef.current, url]),
);
return (
<View style={{flex: 1, backgroundColor: '#fff'}}>
<NetworkErrorModalWithRetryButton
showModal={showNetworkModal}
setShowModal={setShowNetworkModal}
RetryFetch={() => {
setShowNetworkModal(false);
setWebviewKey((prev) => !prev);
}}
/>
{showNetworkModal && (
<LoaderContainer>
<WarningIcon />
</LoaderContainer>
)}
{showToast && (
<Toast>
<Text>File successfully saved to downloads</Text>
</Toast>
)}
{!showNetworkModal && (
<WebView
ref={webviewRef}
source={{
uri: url,
}}
startInLoadingState={true}
renderLoading={() => (
<LoaderContainer>
<Loader />
</LoaderContainer>
)}
allowsBackForwardNavigationGestures={true}
allowsInlineMediaPlayback={true}
mediaPlaybackRequiresUserAction={false}
onError={() => {
setShowNetworkModal(true);
}}
key={webviewKey}
style={{flex: 1}}
onMessage={onMessage}
onNavigationStateChange={(navState) => {
canGoBackRef.current = navState.canGoBack;
currentUrlRef.current = navState.url;
}}
onContentProcessDidTerminate={() => {
setWebviewKey((prev) => !prev);
}}
onRenderProcessGone={() => {
setWebviewKey((prev) => !prev);
}}
injectedJavaScript={injectedJs}
/>
)}
</View>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment