/* * Copyright (c) 2019 - 2020 Arnaud Vergnet. * * This file is part of Campus INSAT. * * Campus INSAT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Campus INSAT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Campus INSAT. If not, see . */ import React, { useCallback, useEffect, useLayoutEffect, useRef, useState, } from 'react'; import WebView, { WebViewNavigation } from 'react-native-webview'; import { Divider, HiddenItem, OverflowMenu, } from 'react-navigation-header-buttons'; import i18n from 'i18n-js'; import { Animated, BackHandler, Linking, NativeScrollEvent, NativeSyntheticEvent, StyleSheet, } from 'react-native'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import { useTheme } from 'react-native-paper'; import { useCollapsibleHeader } from 'react-navigation-collapsible'; import MaterialHeaderButtons, { Item } from '../Overrides/CustomHeaderButton'; import { ERROR_TYPE } from '../../utils/WebData'; import ErrorView from './ErrorView'; import BasicLoadingScreen from './BasicLoadingScreen'; import { useFocusEffect, useNavigation } from '@react-navigation/core'; import { useCollapsible } from '../../utils/CollapsibleContext'; type Props = { url: string; onMessage?: (event: { nativeEvent: { data: string } }) => void; onScroll?: (event: NativeSyntheticEvent) => void; initialJS?: string; injectJS?: string; customPaddingFunction?: null | ((padding: number) => string); showAdvancedControls?: boolean; }; const AnimatedWebView = Animated.createAnimatedComponent(WebView); const styles = StyleSheet.create({ overflow: { marginHorizontal: 10, }, }); /** * Class defining a webview screen. */ function WebViewScreen(props: Props) { const [navState, setNavState] = useState({ canGoBack: false, canGoForward: false, loading: true, url: props.url, lockIdentifier: 0, navigationType: 'click', title: '', }); const navigation = useNavigation(); const theme = useTheme(); const webviewRef = useRef(); const { setCollapsible } = useCollapsible(); const collapsible = useCollapsibleHeader({ config: { collapsedColor: theme.colors.surface, useNativeDriver: false }, }); const { containerPaddingTop, onScrollWithListener } = collapsible; const [currentInjectedJS, setCurrentInjectedJS] = useState(props.injectJS); useFocusEffect( useCallback(() => { setCollapsible(collapsible); BackHandler.addEventListener( 'hardwareBackPress', onBackButtonPressAndroid ); return () => { BackHandler.removeEventListener( 'hardwareBackPress', onBackButtonPressAndroid ); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [collapsible, setCollapsible]) ); useLayoutEffect(() => { navigation.setOptions({ headerRight: props.showAdvancedControls ? getAdvancedButtons : getBasicButton, }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [navigation, props.showAdvancedControls, navState?.url]); useEffect(() => { if (props.injectJS && props.injectJS !== currentInjectedJS) { injectJavaScript(props.injectJS); setCurrentInjectedJS(props.injectJS); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.injectJS]); const onBackButtonPressAndroid = () => { if (navState?.canGoBack) { onGoBackClicked(); return true; } return false; }; const getBasicButton = () => { return ( ); }; const getAdvancedButtons = () => { return ( } > ); }; const getRenderLoading = () => ; /** * Gets the javascript needed to generate a padding on top of the page * This adds padding to the body and runs the custom padding function given in props * * @param padding The padding to add in pixels * @returns {string} */ const getJavascriptPadding = (padding: number) => { const customPadding = props.customPaddingFunction != null ? props.customPaddingFunction(padding) : ''; return `document.getElementsByTagName('body')[0].style.paddingTop = '${padding}px';${customPadding}true;`; }; const onRefreshClicked = () => { //@ts-ignore if (webviewRef.current) { //@ts-ignore webviewRef.current.reload(); } }; const onGoBackClicked = () => { //@ts-ignore if (webviewRef.current) { //@ts-ignore webviewRef.current.goBack(); } }; const onGoForwardClicked = () => { //@ts-ignore if (webviewRef.current) { //@ts-ignore webviewRef.current.goForward(); } }; const onOpenClicked = () => navState ? Linking.openURL(navState.url) : undefined; const onScroll = (event: NativeSyntheticEvent) => { if (props.onScroll) { props.onScroll(event); } }; const injectJavaScript = (script: string) => { //@ts-ignore if (webviewRef.current) { //@ts-ignore webviewRef.current.injectJavaScript(script); } }; return ( ( )} onNavigationStateChange={setNavState} onMessage={props.onMessage} onLoad={() => injectJavaScript(getJavascriptPadding(containerPaddingTop))} // Animations onScroll={onScrollWithListener(onScroll)} /> ); } export default WebViewScreen;