forked from vergnet/application-amicale
Improve basic screen components to match linter
This commit is contained in:
parent
4db4516296
commit
0117b25cd8
3 changed files with 425 additions and 411 deletions
|
@ -3,6 +3,7 @@
|
|||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {ActivityIndicator, withTheme} from 'react-native-paper';
|
||||
import type {CustomTheme} from '../../managers/ThemeManager';
|
||||
|
||||
/**
|
||||
* Component used to display a header button
|
||||
|
@ -10,28 +11,29 @@ import {ActivityIndicator, withTheme} from 'react-native-paper';
|
|||
* @param props Props to pass to the component
|
||||
* @return {*}
|
||||
*/
|
||||
function BasicLoadingScreen(props) {
|
||||
const {colors} = props.theme;
|
||||
let position = undefined;
|
||||
if (props.isAbsolute !== undefined && props.isAbsolute)
|
||||
position = 'absolute';
|
||||
function BasicLoadingScreen(props: {
|
||||
theme: CustomTheme,
|
||||
isAbsolute: boolean,
|
||||
}): React.Node {
|
||||
const {theme, isAbsolute} = props;
|
||||
const {colors} = theme;
|
||||
let position;
|
||||
if (isAbsolute != null && isAbsolute) position = 'absolute';
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
backgroundColor: colors.background,
|
||||
position: position,
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<ActivityIndicator
|
||||
animating={true}
|
||||
size={'large'}
|
||||
color={colors.primary}/>
|
||||
</View>
|
||||
);
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.background,
|
||||
position,
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<ActivityIndicator animating size="large" color={colors.primary} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default withTheme(BasicLoadingScreen);
|
||||
|
|
|
@ -2,191 +2,189 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import {Button, Subheading, withTheme} from 'react-native-paper';
|
||||
import {StyleSheet, View} from "react-native";
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import i18n from 'i18n-js';
|
||||
import {ERROR_TYPE} from "../../utils/WebData";
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import {ERROR_TYPE} from '../../utils/WebData';
|
||||
import type {CustomTheme} from '../../managers/ThemeManager';
|
||||
|
||||
type Props = {
|
||||
navigation: Object,
|
||||
route: Object,
|
||||
errorCode: number,
|
||||
onRefresh: Function,
|
||||
icon: string,
|
||||
message: string,
|
||||
showRetryButton: boolean,
|
||||
}
|
||||
|
||||
type State = {
|
||||
refreshing: boolean,
|
||||
}
|
||||
|
||||
class ErrorView extends React.PureComponent<Props, State> {
|
||||
|
||||
colors: Object;
|
||||
|
||||
message: string;
|
||||
icon: string;
|
||||
|
||||
showLoginButton: boolean;
|
||||
|
||||
static defaultProps = {
|
||||
errorCode: 0,
|
||||
icon: '',
|
||||
message: '',
|
||||
showRetryButton: true,
|
||||
}
|
||||
|
||||
state = {
|
||||
refreshing: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.colors = props.theme.colors;
|
||||
this.icon = "";
|
||||
}
|
||||
|
||||
generateMessage() {
|
||||
this.showLoginButton = false;
|
||||
if (this.props.errorCode !== 0) {
|
||||
switch (this.props.errorCode) {
|
||||
case ERROR_TYPE.BAD_CREDENTIALS:
|
||||
this.message = i18n.t("errors.badCredentials");
|
||||
this.icon = "account-alert-outline";
|
||||
break;
|
||||
case ERROR_TYPE.BAD_TOKEN:
|
||||
this.message = i18n.t("errors.badToken");
|
||||
this.icon = "account-alert-outline";
|
||||
this.showLoginButton = true;
|
||||
break;
|
||||
case ERROR_TYPE.NO_CONSENT:
|
||||
this.message = i18n.t("errors.noConsent");
|
||||
this.icon = "account-remove-outline";
|
||||
break;
|
||||
case ERROR_TYPE.TOKEN_SAVE:
|
||||
this.message = i18n.t("errors.tokenSave");
|
||||
this.icon = "alert-circle-outline";
|
||||
break;
|
||||
case ERROR_TYPE.BAD_INPUT:
|
||||
this.message = i18n.t("errors.badInput");
|
||||
this.icon = "alert-circle-outline";
|
||||
break;
|
||||
case ERROR_TYPE.FORBIDDEN:
|
||||
this.message = i18n.t("errors.forbidden");
|
||||
this.icon = "lock";
|
||||
break;
|
||||
case ERROR_TYPE.CONNECTION_ERROR:
|
||||
this.message = i18n.t("errors.connectionError");
|
||||
this.icon = "access-point-network-off";
|
||||
break;
|
||||
case ERROR_TYPE.SERVER_ERROR:
|
||||
this.message = i18n.t("errors.serverError");
|
||||
this.icon = "server-network-off";
|
||||
break;
|
||||
default:
|
||||
this.message = i18n.t("errors.unknown");
|
||||
this.icon = "alert-circle-outline";
|
||||
break;
|
||||
}
|
||||
this.message += "\n\nCode " + this.props.errorCode;
|
||||
} else {
|
||||
this.message = this.props.message;
|
||||
this.icon = this.props.icon;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getRetryButton() {
|
||||
return <Button
|
||||
mode={'contained'}
|
||||
icon={'refresh'}
|
||||
onPress={this.props.onRefresh}
|
||||
style={styles.button}
|
||||
>
|
||||
{i18n.t("general.retry")}
|
||||
</Button>;
|
||||
}
|
||||
|
||||
goToLogin = () => {
|
||||
this.props.navigation.navigate("login",
|
||||
{
|
||||
screen: 'login',
|
||||
params: {nextScreen: this.props.route.name}
|
||||
})
|
||||
};
|
||||
|
||||
getLoginButton() {
|
||||
return <Button
|
||||
mode={'contained'}
|
||||
icon={'login'}
|
||||
onPress={this.goToLogin}
|
||||
style={styles.button}
|
||||
>
|
||||
{i18n.t("screens.login.title")}
|
||||
</Button>;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.generateMessage();
|
||||
return (
|
||||
<Animatable.View
|
||||
style={{
|
||||
...styles.outer,
|
||||
backgroundColor: this.colors.background
|
||||
}}
|
||||
animation={"zoomIn"}
|
||||
duration={200}
|
||||
useNativeDriver
|
||||
>
|
||||
<View style={styles.inner}>
|
||||
<View style={styles.iconContainer}>
|
||||
<MaterialCommunityIcons
|
||||
name={this.icon}
|
||||
size={150}
|
||||
color={this.colors.textDisabled}/>
|
||||
</View>
|
||||
<Subheading style={{
|
||||
...styles.subheading,
|
||||
color: this.colors.textDisabled
|
||||
}}>
|
||||
{this.message}
|
||||
</Subheading>
|
||||
{this.props.showRetryButton
|
||||
? (this.showLoginButton
|
||||
? this.getLoginButton()
|
||||
: this.getRetryButton())
|
||||
: null}
|
||||
</View>
|
||||
</Animatable.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomTheme,
|
||||
route: {name: string},
|
||||
onRefresh?: () => void,
|
||||
errorCode?: number,
|
||||
icon?: string,
|
||||
message?: string,
|
||||
showRetryButton?: boolean,
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
height: '100%',
|
||||
},
|
||||
inner: {
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
},
|
||||
iconContainer: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginBottom: 20
|
||||
},
|
||||
subheading: {
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 20
|
||||
},
|
||||
button: {
|
||||
marginTop: 10,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
}
|
||||
outer: {
|
||||
height: '100%',
|
||||
},
|
||||
inner: {
|
||||
marginTop: 'auto',
|
||||
marginBottom: 'auto',
|
||||
},
|
||||
iconContainer: {
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
marginBottom: 20,
|
||||
},
|
||||
subheading: {
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
button: {
|
||||
marginTop: 10,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
class ErrorView extends React.PureComponent<PropsType> {
|
||||
static defaultProps = {
|
||||
onRefresh: (): void => null,
|
||||
errorCode: 0,
|
||||
icon: '',
|
||||
message: '',
|
||||
showRetryButton: true,
|
||||
};
|
||||
|
||||
message: string;
|
||||
|
||||
icon: string;
|
||||
|
||||
showLoginButton: boolean;
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this.icon = '';
|
||||
}
|
||||
|
||||
getRetryButton(): React.Node {
|
||||
const {props} = this;
|
||||
return (
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="refresh"
|
||||
onPress={props.onRefresh}
|
||||
style={styles.button}>
|
||||
{i18n.t('general.retry')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
getLoginButton(): React.Node {
|
||||
return (
|
||||
<Button
|
||||
mode="contained"
|
||||
icon="login"
|
||||
onPress={this.goToLogin}
|
||||
style={styles.button}>
|
||||
{i18n.t('screens.login.title')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
goToLogin = () => {
|
||||
const {props} = this;
|
||||
props.navigation.navigate('login', {
|
||||
screen: 'login',
|
||||
params: {nextScreen: props.route.name},
|
||||
});
|
||||
};
|
||||
|
||||
generateMessage() {
|
||||
const {props} = this;
|
||||
this.showLoginButton = false;
|
||||
if (props.errorCode !== 0) {
|
||||
switch (props.errorCode) {
|
||||
case ERROR_TYPE.BAD_CREDENTIALS:
|
||||
this.message = i18n.t('errors.badCredentials');
|
||||
this.icon = 'account-alert-outline';
|
||||
break;
|
||||
case ERROR_TYPE.BAD_TOKEN:
|
||||
this.message = i18n.t('errors.badToken');
|
||||
this.icon = 'account-alert-outline';
|
||||
this.showLoginButton = true;
|
||||
break;
|
||||
case ERROR_TYPE.NO_CONSENT:
|
||||
this.message = i18n.t('errors.noConsent');
|
||||
this.icon = 'account-remove-outline';
|
||||
break;
|
||||
case ERROR_TYPE.TOKEN_SAVE:
|
||||
this.message = i18n.t('errors.tokenSave');
|
||||
this.icon = 'alert-circle-outline';
|
||||
break;
|
||||
case ERROR_TYPE.BAD_INPUT:
|
||||
this.message = i18n.t('errors.badInput');
|
||||
this.icon = 'alert-circle-outline';
|
||||
break;
|
||||
case ERROR_TYPE.FORBIDDEN:
|
||||
this.message = i18n.t('errors.forbidden');
|
||||
this.icon = 'lock';
|
||||
break;
|
||||
case ERROR_TYPE.CONNECTION_ERROR:
|
||||
this.message = i18n.t('errors.connectionError');
|
||||
this.icon = 'access-point-network-off';
|
||||
break;
|
||||
case ERROR_TYPE.SERVER_ERROR:
|
||||
this.message = i18n.t('errors.serverError');
|
||||
this.icon = 'server-network-off';
|
||||
break;
|
||||
default:
|
||||
this.message = i18n.t('errors.unknown');
|
||||
this.icon = 'alert-circle-outline';
|
||||
break;
|
||||
}
|
||||
this.message += `\n\nCode ${props.errorCode}`;
|
||||
} else {
|
||||
this.message = props.message;
|
||||
this.icon = props.icon;
|
||||
}
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
this.generateMessage();
|
||||
let button;
|
||||
if (this.showLoginButton) button = this.getLoginButton();
|
||||
else if (props.showRetryButton) button = this.getRetryButton();
|
||||
else button = null;
|
||||
|
||||
return (
|
||||
<Animatable.View
|
||||
style={{
|
||||
...styles.outer,
|
||||
backgroundColor: props.theme.colors.background,
|
||||
}}
|
||||
animation="zoomIn"
|
||||
duration={200}
|
||||
useNativeDriver>
|
||||
<View style={styles.inner}>
|
||||
<View style={styles.iconContainer}>
|
||||
<MaterialCommunityIcons
|
||||
name={this.icon}
|
||||
size={150}
|
||||
color={props.theme.colors.textDisabled}
|
||||
/>
|
||||
</View>
|
||||
<Subheading
|
||||
style={{
|
||||
...styles.subheading,
|
||||
color: props.theme.colors.textDisabled,
|
||||
}}>
|
||||
{this.message}
|
||||
</Subheading>
|
||||
{button}
|
||||
</View>
|
||||
</Animatable.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTheme(ErrorView);
|
||||
|
|
|
@ -1,233 +1,247 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import WebView from "react-native-webview";
|
||||
import BasicLoadingScreen from "./BasicLoadingScreen";
|
||||
import ErrorView from "./ErrorView";
|
||||
import {ERROR_TYPE} from "../../utils/WebData";
|
||||
import MaterialHeaderButtons, {Item} from '../Overrides/CustomHeaderButton';
|
||||
import {Divider, HiddenItem, OverflowMenu} from "react-navigation-header-buttons";
|
||||
import WebView from 'react-native-webview';
|
||||
import {
|
||||
Divider,
|
||||
HiddenItem,
|
||||
OverflowMenu,
|
||||
} from 'react-navigation-header-buttons';
|
||||
import i18n from 'i18n-js';
|
||||
import {Animated, BackHandler, Linking} from "react-native";
|
||||
import {withCollapsible} from "../../utils/withCollapsible";
|
||||
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import {withTheme} from "react-native-paper";
|
||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
||||
import {StackNavigationProp} from "@react-navigation/stack";
|
||||
import {Collapsible} from "react-navigation-collapsible";
|
||||
import {Animated, BackHandler, Linking} from 'react-native';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import {withTheme} from 'react-native-paper';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import {Collapsible} from 'react-navigation-collapsible';
|
||||
import type {CustomTheme} from '../../managers/ThemeManager';
|
||||
import {withCollapsible} from '../../utils/withCollapsible';
|
||||
import MaterialHeaderButtons, {Item} from '../Overrides/CustomHeaderButton';
|
||||
import {ERROR_TYPE} from '../../utils/WebData';
|
||||
import ErrorView from './ErrorView';
|
||||
import BasicLoadingScreen from './BasicLoadingScreen';
|
||||
|
||||
type Props = {
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomTheme,
|
||||
url: string,
|
||||
customJS: string,
|
||||
customPaddingFunction: null | (padding: number) => string,
|
||||
collapsibleStack: Collapsible,
|
||||
onMessage: Function,
|
||||
onScroll: Function,
|
||||
showAdvancedControls: boolean,
|
||||
}
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
theme: CustomTheme,
|
||||
url: string,
|
||||
collapsibleStack: Collapsible,
|
||||
onMessage: (event: {nativeEvent: {data: string}}) => void,
|
||||
onScroll: (event: SyntheticEvent<EventTarget>) => void,
|
||||
customJS?: string,
|
||||
customPaddingFunction?: null | ((padding: number) => string),
|
||||
showAdvancedControls?: boolean,
|
||||
};
|
||||
|
||||
const AnimatedWebView = Animated.createAnimatedComponent(WebView);
|
||||
|
||||
/**
|
||||
* Class defining a webview screen.
|
||||
*/
|
||||
class WebViewScreen extends React.PureComponent<Props> {
|
||||
class WebViewScreen extends React.PureComponent<PropsType> {
|
||||
static defaultProps = {
|
||||
customJS: '',
|
||||
showAdvancedControls: true,
|
||||
customPaddingFunction: null,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
customJS: '',
|
||||
showAdvancedControls: true,
|
||||
customPaddingFunction: null,
|
||||
};
|
||||
webviewRef: {current: null | WebView};
|
||||
|
||||
webviewRef: { current: null | WebView };
|
||||
canGoBack: boolean;
|
||||
|
||||
canGoBack: boolean;
|
||||
constructor() {
|
||||
super();
|
||||
this.webviewRef = React.createRef();
|
||||
this.canGoBack = false;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.webviewRef = React.createRef();
|
||||
this.canGoBack = false;
|
||||
/**
|
||||
* Creates header buttons and listens to events after mounting
|
||||
*/
|
||||
componentDidMount() {
|
||||
const {props} = this;
|
||||
props.navigation.setOptions({
|
||||
headerRight: props.showAdvancedControls
|
||||
? this.getAdvancedButtons
|
||||
: this.getBasicButton,
|
||||
});
|
||||
props.navigation.addListener('focus', () => {
|
||||
BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid,
|
||||
);
|
||||
});
|
||||
props.navigation.addListener('blur', () => {
|
||||
BackHandler.removeEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes back on the webview or on the navigation stack if we cannot go back anymore
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
onBackButtonPressAndroid = (): boolean => {
|
||||
if (this.canGoBack) {
|
||||
this.onGoBackClicked();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates header buttons and listens to events after mounting
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.navigation.setOptions({
|
||||
headerRight: this.props.showAdvancedControls
|
||||
? this.getAdvancedButtons
|
||||
: this.getBasicButton,
|
||||
});
|
||||
this.props.navigation.addListener(
|
||||
'focus',
|
||||
() =>
|
||||
BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid
|
||||
)
|
||||
);
|
||||
this.props.navigation.addListener(
|
||||
'blur',
|
||||
() =>
|
||||
BackHandler.removeEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid
|
||||
)
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Gets header refresh and open in browser buttons
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getBasicButton = (): React.Node => {
|
||||
return (
|
||||
<MaterialHeaderButtons>
|
||||
<Item
|
||||
title="refresh"
|
||||
iconName="refresh"
|
||||
onPress={this.onRefreshClicked}
|
||||
/>
|
||||
<Item
|
||||
title={i18n.t('general.openInBrowser')}
|
||||
iconName="open-in-new"
|
||||
onPress={this.onOpenClicked}
|
||||
/>
|
||||
</MaterialHeaderButtons>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Goes back on the webview or on the navigation stack if we cannot go back anymore
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
onBackButtonPressAndroid = () => {
|
||||
if (this.canGoBack) {
|
||||
this.onGoBackClicked();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets header refresh and open in browser buttons
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getBasicButton = () => {
|
||||
return (
|
||||
<MaterialHeaderButtons>
|
||||
<Item
|
||||
title="refresh"
|
||||
iconName="refresh"
|
||||
onPress={this.onRefreshClicked}/>
|
||||
<Item
|
||||
title={i18n.t("general.openInBrowser")}
|
||||
iconName="open-in-new"
|
||||
onPress={this.onOpenClicked}/>
|
||||
</MaterialHeaderButtons>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates advanced header control buttons.
|
||||
* These buttons allows the user to refresh, go back, go forward and open in the browser.
|
||||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
getAdvancedButtons = () => {
|
||||
return (
|
||||
<MaterialHeaderButtons>
|
||||
<Item
|
||||
title="refresh"
|
||||
iconName="refresh"
|
||||
onPress={this.onRefreshClicked}
|
||||
/>
|
||||
<OverflowMenu
|
||||
style={{marginHorizontal: 10}}
|
||||
OverflowIcon={
|
||||
<MaterialCommunityIcons
|
||||
name="dots-vertical"
|
||||
size={26}
|
||||
color={this.props.theme.colors.text}
|
||||
/>}
|
||||
>
|
||||
<HiddenItem
|
||||
title={i18n.t("general.goBack")}
|
||||
onPress={this.onGoBackClicked}/>
|
||||
<HiddenItem
|
||||
title={i18n.t("general.goForward")}
|
||||
onPress={this.onGoForwardClicked}/>
|
||||
<Divider/>
|
||||
<HiddenItem
|
||||
title={i18n.t("general.openInBrowser")}
|
||||
onPress={this.onOpenClicked}/>
|
||||
</OverflowMenu>
|
||||
</MaterialHeaderButtons>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to use when refresh button is clicked. Reloads the webview.
|
||||
*/
|
||||
onRefreshClicked = () => {
|
||||
if (this.webviewRef.current != null)
|
||||
this.webviewRef.current.reload();
|
||||
}
|
||||
onGoBackClicked = () => {
|
||||
if (this.webviewRef.current != null)
|
||||
this.webviewRef.current.goBack();
|
||||
}
|
||||
onGoForwardClicked = () => {
|
||||
if (this.webviewRef.current != null)
|
||||
this.webviewRef.current.goForward();
|
||||
}
|
||||
onOpenClicked = () => Linking.openURL(this.props.url);
|
||||
|
||||
/**
|
||||
* Injects the given javascript string into the web page
|
||||
*
|
||||
* @param script The script to inject
|
||||
*/
|
||||
injectJavaScript = (script: string) => {
|
||||
if (this.webviewRef.current != null)
|
||||
this.webviewRef.current.injectJavaScript(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the loading indicator
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderLoading = () => <BasicLoadingScreen isAbsolute={true}/>;
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
getJavascriptPadding(padding: number) {
|
||||
const customPadding = this.props.customPaddingFunction != null ? this.props.customPaddingFunction(padding) : "";
|
||||
return (
|
||||
"document.getElementsByTagName('body')[0].style.paddingTop = '" + padding + "px';" +
|
||||
customPadding +
|
||||
"true;"
|
||||
);
|
||||
}
|
||||
|
||||
onScroll = (event: Object) => {
|
||||
if (this.props.onScroll)
|
||||
this.props.onScroll(event);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {containerPaddingTop, onScrollWithListener} = this.props.collapsibleStack;
|
||||
return (
|
||||
<AnimatedWebView
|
||||
ref={this.webviewRef}
|
||||
source={{uri: this.props.url}}
|
||||
startInLoadingState={true}
|
||||
injectedJavaScript={this.props.customJS}
|
||||
javaScriptEnabled={true}
|
||||
renderLoading={this.getRenderLoading}
|
||||
renderError={() => <ErrorView
|
||||
errorCode={ERROR_TYPE.CONNECTION_ERROR}
|
||||
onRefresh={this.onRefreshClicked}
|
||||
/>}
|
||||
onNavigationStateChange={navState => {
|
||||
this.canGoBack = navState.canGoBack;
|
||||
}}
|
||||
onMessage={this.props.onMessage}
|
||||
onLoad={() => this.injectJavaScript(this.getJavascriptPadding(containerPaddingTop))}
|
||||
// Animations
|
||||
onScroll={onScrollWithListener(this.onScroll)}
|
||||
/**
|
||||
* Creates advanced header control buttons.
|
||||
* These buttons allows the user to refresh, go back, go forward and open in the browser.
|
||||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
getAdvancedButtons = (): React.Node => {
|
||||
const {props} = this;
|
||||
return (
|
||||
<MaterialHeaderButtons>
|
||||
<Item
|
||||
title="refresh"
|
||||
iconName="refresh"
|
||||
onPress={this.onRefreshClicked}
|
||||
/>
|
||||
<OverflowMenu
|
||||
style={{marginHorizontal: 10}}
|
||||
OverflowIcon={
|
||||
<MaterialCommunityIcons
|
||||
name="dots-vertical"
|
||||
size={26}
|
||||
color={props.theme.colors.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}>
|
||||
<HiddenItem
|
||||
title={i18n.t('general.goBack')}
|
||||
onPress={this.onGoBackClicked}
|
||||
/>
|
||||
<HiddenItem
|
||||
title={i18n.t('general.goForward')}
|
||||
onPress={this.onGoForwardClicked}
|
||||
/>
|
||||
<Divider />
|
||||
<HiddenItem
|
||||
title={i18n.t('general.openInBrowser')}
|
||||
onPress={this.onOpenClicked}
|
||||
/>
|
||||
</OverflowMenu>
|
||||
</MaterialHeaderButtons>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the loading indicator
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderLoading = (): React.Node => <BasicLoadingScreen isAbsolute />;
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
getJavascriptPadding(padding: number): string {
|
||||
const {props} = this;
|
||||
const customPadding =
|
||||
props.customPaddingFunction != null
|
||||
? props.customPaddingFunction(padding)
|
||||
: '';
|
||||
return `document.getElementsByTagName('body')[0].style.paddingTop = '${padding}px';${customPadding}true;`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to use when refresh button is clicked. Reloads the webview.
|
||||
*/
|
||||
onRefreshClicked = () => {
|
||||
if (this.webviewRef.current != null) this.webviewRef.current.reload();
|
||||
};
|
||||
|
||||
onGoBackClicked = () => {
|
||||
if (this.webviewRef.current != null) this.webviewRef.current.goBack();
|
||||
};
|
||||
|
||||
onGoForwardClicked = () => {
|
||||
if (this.webviewRef.current != null) this.webviewRef.current.goForward();
|
||||
};
|
||||
|
||||
onOpenClicked = () => {
|
||||
const {url} = this.props;
|
||||
Linking.openURL(url);
|
||||
};
|
||||
|
||||
onScroll = (event: SyntheticEvent<EventTarget>) => {
|
||||
const {onScroll} = this.props;
|
||||
if (onScroll) onScroll(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Injects the given javascript string into the web page
|
||||
*
|
||||
* @param script The script to inject
|
||||
*/
|
||||
injectJavaScript = (script: string) => {
|
||||
if (this.webviewRef.current != null)
|
||||
this.webviewRef.current.injectJavaScript(script);
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
const {containerPaddingTop, onScrollWithListener} = props.collapsibleStack;
|
||||
return (
|
||||
<AnimatedWebView
|
||||
ref={this.webviewRef}
|
||||
source={{uri: props.url}}
|
||||
startInLoadingState
|
||||
injectedJavaScript={props.customJS}
|
||||
javaScriptEnabled
|
||||
renderLoading={this.getRenderLoading}
|
||||
renderError={(): React.Node => (
|
||||
<ErrorView
|
||||
errorCode={ERROR_TYPE.CONNECTION_ERROR}
|
||||
onRefresh={this.onRefreshClicked}
|
||||
/>
|
||||
)}
|
||||
onNavigationStateChange={(navState: {canGoBack: boolean}) => {
|
||||
this.canGoBack = navState.canGoBack;
|
||||
}}
|
||||
onMessage={props.onMessage}
|
||||
onLoad={() => {
|
||||
this.injectJavaScript(this.getJavascriptPadding(containerPaddingTop));
|
||||
}}
|
||||
// Animations
|
||||
onScroll={onScrollWithListener(this.onScroll)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withCollapsible(withTheme(WebViewScreen));
|
||||
|
|
Loading…
Reference in a new issue