/*
* 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;