forked from vergnet/application-amicale
Improved error handling
This commit is contained in:
parent
49dced05ab
commit
c70bafe273
7 changed files with 104 additions and 97 deletions
|
@ -3,8 +3,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {withTheme} from 'react-native-paper';
|
import {withTheme} from 'react-native-paper';
|
||||||
import ConnectionManager, {ERROR_TYPE} from "../../managers/ConnectionManager";
|
import ConnectionManager, {ERROR_TYPE} from "../../managers/ConnectionManager";
|
||||||
import NetworkErrorComponent from "../Custom/NetworkErrorComponent";
|
import ErrorView from "../Custom/ErrorView";
|
||||||
import i18n from 'i18n-js';
|
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -98,32 +97,9 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getErrorRender() {
|
getErrorRender() {
|
||||||
let message;
|
|
||||||
let icon;
|
|
||||||
switch (this.errorCode) {
|
|
||||||
case ERROR_TYPE.BAD_CREDENTIALS:
|
|
||||||
message = i18n.t("loginScreen.errors.credentials");
|
|
||||||
icon = "account-alert-outline";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.BAD_TOKEN:
|
|
||||||
message = "BAD TOKEN"; // TODO translate
|
|
||||||
icon = "access-point-network-off";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.CONNECTION_ERROR:
|
|
||||||
message = i18n.t("loginScreen.errors.connection");
|
|
||||||
icon = "access-point-network-off";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
message = i18n.t("loginScreen.errors.unknown");
|
|
||||||
icon = "alert-circle-outline";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NetworkErrorComponent
|
<ErrorView
|
||||||
{...this.props}
|
errorCode={this.errorCode}
|
||||||
icon={icon}
|
|
||||||
message={message}
|
|
||||||
onRefresh={this.fetchData}
|
onRefresh={this.fetchData}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,10 +5,10 @@ import {Button, Subheading, withTheme} from 'react-native-paper';
|
||||||
import {StyleSheet, View} from "react-native";
|
import {StyleSheet, View} from "react-native";
|
||||||
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
|
import {ERROR_TYPE} from "../../managers/ConnectionManager";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
message: string,
|
errorCode: number,
|
||||||
icon: string,
|
|
||||||
onRefresh: Function,
|
onRefresh: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,10 +16,13 @@ type State = {
|
||||||
refreshing: boolean,
|
refreshing: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
class NetworkErrorComponent extends React.PureComponent<Props, State> {
|
class ErrorView extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
colors: Object;
|
colors: Object;
|
||||||
|
|
||||||
|
message: string;
|
||||||
|
icon: string;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
};
|
};
|
||||||
|
@ -29,7 +32,45 @@ class NetworkErrorComponent extends React.PureComponent<Props, State> {
|
||||||
this.colors = props.theme.colors;
|
this.colors = props.theme.colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateMessage() {
|
||||||
|
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";
|
||||||
|
break;
|
||||||
|
case ERROR_TYPE.NO_CONSENT:
|
||||||
|
this.message = i18n.t("errors.noConsent");
|
||||||
|
this.icon = "account-remove-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
this.generateMessage();
|
||||||
return (
|
return (
|
||||||
<View style={{
|
<View style={{
|
||||||
...styles.outer,
|
...styles.outer,
|
||||||
|
@ -38,7 +79,7 @@ class NetworkErrorComponent extends React.PureComponent<Props, State> {
|
||||||
<View style={styles.inner}>
|
<View style={styles.inner}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name={this.props.icon}
|
name={this.icon}
|
||||||
size={150}
|
size={150}
|
||||||
color={this.colors.textDisabled}/>
|
color={this.colors.textDisabled}/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -46,7 +87,7 @@ class NetworkErrorComponent extends React.PureComponent<Props, State> {
|
||||||
...styles.subheading,
|
...styles.subheading,
|
||||||
color: this.colors.textDisabled
|
color: this.colors.textDisabled
|
||||||
}}>
|
}}>
|
||||||
{this.props.message}
|
{this.message}
|
||||||
</Subheading>
|
</Subheading>
|
||||||
<Button
|
<Button
|
||||||
mode={'contained'}
|
mode={'contained'}
|
||||||
|
@ -86,4 +127,4 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default withTheme(NetworkErrorComponent);
|
export default withTheme(ErrorView);
|
|
@ -15,22 +15,31 @@ class ErrorDialog extends React.PureComponent<Props> {
|
||||||
message: string;
|
message: string;
|
||||||
|
|
||||||
generateMessage() {
|
generateMessage() {
|
||||||
this.title = i18n.t("loginScreen.errors.title");
|
this.title = i18n.t("errors.title");
|
||||||
switch (this.props.errorCode) {
|
switch (this.props.errorCode) {
|
||||||
case ERROR_TYPE.BAD_CREDENTIALS:
|
case ERROR_TYPE.BAD_CREDENTIALS:
|
||||||
this.message = i18n.t("loginScreen.errors.credentials");
|
this.message = i18n.t("errors.badCredentials");
|
||||||
|
break;
|
||||||
|
case ERROR_TYPE.BAD_TOKEN:
|
||||||
|
this.message = i18n.t("errors.badToken");
|
||||||
break;
|
break;
|
||||||
case ERROR_TYPE.NO_CONSENT:
|
case ERROR_TYPE.NO_CONSENT:
|
||||||
this.message = i18n.t("loginScreen.errors.consent");
|
this.message = i18n.t("errors.noConsent");
|
||||||
|
break;
|
||||||
|
case ERROR_TYPE.BAD_INPUT:
|
||||||
|
this.message = i18n.t("errors.badInput");
|
||||||
|
break;
|
||||||
|
case ERROR_TYPE.FORBIDDEN:
|
||||||
|
this.message = i18n.t("errors.forbidden");
|
||||||
break;
|
break;
|
||||||
case ERROR_TYPE.CONNECTION_ERROR:
|
case ERROR_TYPE.CONNECTION_ERROR:
|
||||||
this.message = i18n.t("loginScreen.errors.connection");
|
this.message = i18n.t("errors.connectionError");
|
||||||
break;
|
break;
|
||||||
case ERROR_TYPE.SERVER_ERROR:
|
case ERROR_TYPE.SERVER_ERROR:
|
||||||
this.message = "SERVER ERROR"; // TODO translate
|
this.message = i18n.t("errors.serverError");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.message = i18n.t("loginScreen.errors.unknown");
|
this.message = i18n.t("errors.unknown");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,9 @@ import {readData} from "../../utils/WebData";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import {Snackbar} from 'react-native-paper';
|
import {Snackbar} from 'react-native-paper';
|
||||||
import {RefreshControl, SectionList, View} from "react-native";
|
import {RefreshControl, SectionList, View} from "react-native";
|
||||||
import NetworkErrorComponent from "../Custom/NetworkErrorComponent";
|
import ErrorView from "../Custom/ErrorView";
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
|
import {ERROR_TYPE} from "../../managers/ConnectionManager";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
@ -192,9 +193,8 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
|
||||||
style={{minHeight: '100%'}}
|
style={{minHeight: '100%'}}
|
||||||
ListEmptyComponent={this.state.refreshing
|
ListEmptyComponent={this.state.refreshing
|
||||||
? <BasicLoadingScreen/>
|
? <BasicLoadingScreen/>
|
||||||
: <NetworkErrorComponent
|
: <ErrorView
|
||||||
message={i18n.t('general.networkError')}
|
errorCode={ERROR_TYPE.CONNECTION_ERROR}
|
||||||
icon={"access-point-network-off"}
|
|
||||||
onRefresh={this.onRefresh}/>
|
onRefresh={this.onRefresh}/>
|
||||||
}
|
}
|
||||||
getItemLayout={this.props.itemHeight !== undefined ? this.itemLayout : undefined}
|
getItemLayout={this.props.itemHeight !== undefined ? this.itemLayout : undefined}
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import WebView from "react-native-webview";
|
import WebView from "react-native-webview";
|
||||||
import {withTheme} from 'react-native-paper';
|
|
||||||
import HeaderButton from "../Custom/HeaderButton";
|
import HeaderButton from "../Custom/HeaderButton";
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
import NetworkErrorComponent from "../Custom/NetworkErrorComponent";
|
import ErrorView from "../Custom/ErrorView";
|
||||||
import i18n from "i18n-js";
|
import {ERROR_TYPE} from "../../managers/ConnectionManager";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
@ -32,20 +31,12 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
hasSideMenu: true,
|
hasSideMenu: true,
|
||||||
hasFooter: true,
|
hasFooter: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
webviewRef: Object;
|
webviewRef: Object;
|
||||||
|
|
||||||
onRefreshClicked: Function;
|
constructor() {
|
||||||
onWebviewRef: Function;
|
super();
|
||||||
getRenderLoading: Function;
|
this.webviewRef = React.createRef();
|
||||||
|
|
||||||
colors: Object;
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.onRefreshClicked = this.onRefreshClicked.bind(this);
|
|
||||||
this.onWebviewRef = this.onWebviewRef.bind(this);
|
|
||||||
this.getRenderLoading = this.getRenderLoading.bind(this);
|
|
||||||
this.colors = props.theme.colors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,33 +61,19 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
/**
|
/**
|
||||||
* Callback to use when refresh button is clicked. Reloads the webview.
|
* Callback to use when refresh button is clicked. Reloads the webview.
|
||||||
*/
|
*/
|
||||||
onRefreshClicked() {
|
onRefreshClicked = () => this.webviewRef.current.reload();
|
||||||
if (this.webviewRef !== null)
|
|
||||||
this.webviewRef.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback used when receiving the webview ref. Stores the ref for later use
|
|
||||||
*
|
|
||||||
* @param ref
|
|
||||||
*/
|
|
||||||
onWebviewRef(ref: Object) {
|
|
||||||
this.webviewRef = ref
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the loading indicator
|
* Gets the loading indicator
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getRenderLoading() {
|
getRenderLoading = () => <BasicLoadingScreen/>;
|
||||||
return <BasicLoadingScreen/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<WebView
|
<WebView
|
||||||
ref={this.onWebviewRef}
|
ref={this.webviewRef}
|
||||||
source={{uri: this.props.data[0]['url']}}
|
source={{uri: this.props.data[0]['url']}}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
@ -106,15 +83,13 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
injectedJavaScript={this.props.data[0]['customJS']}
|
injectedJavaScript={this.props.data[0]['customJS']}
|
||||||
javaScriptEnabled={true}
|
javaScriptEnabled={true}
|
||||||
renderLoading={this.getRenderLoading}
|
renderLoading={this.getRenderLoading}
|
||||||
renderError={() => <NetworkErrorComponent
|
renderError={() => <ErrorView
|
||||||
{...this.props}
|
errorCode={ERROR_TYPE.CONNECTION_ERROR}
|
||||||
onRefresh={this.onRefreshClicked}
|
onRefresh={this.onRefreshClicked}
|
||||||
message={i18n.t("loginScreen.errors.connection")}
|
|
||||||
icon={'access-point-network-off'}
|
|
||||||
/>}
|
/>}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(WebViewScreen);
|
export default WebViewScreen;
|
||||||
|
|
|
@ -237,14 +237,17 @@
|
||||||
"whyAccountParagraph": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!",
|
"whyAccountParagraph": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!",
|
||||||
"whyAccountParagraph2": "Logging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!",
|
"whyAccountParagraph2": "Logging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!",
|
||||||
"noAccount": "No Account? Go to the Amicale's building during open hours to create one.",
|
"noAccount": "No Account? Go to the Amicale's building during open hours to create one.",
|
||||||
"errors": {
|
},
|
||||||
"title": "Error!",
|
"errors": {
|
||||||
"connection": "Network error. Please check your internet connection.",
|
"title": "Error!",
|
||||||
"credentials": "Email or password invalid.",
|
"badCredentials": "Email or password invalid.",
|
||||||
"saveToken": "Failed to save connection information, please contact support.",
|
"badToken": "Session expired, please login again.",
|
||||||
"consent": "You did not give your consent for data processing to the Amicale.",
|
"noConsent": "You did not give your consent for data processing to the Amicale.",
|
||||||
"unknown": "Unknown error, please contact support."
|
"badInput": "Invalid input. Please try again.",
|
||||||
}
|
"forbidden": "You do not have access to this data.",
|
||||||
|
"connectionError": "Network error. Please check your internet connection.",
|
||||||
|
"serverError": "Server error. Please contact support.",
|
||||||
|
"unknown": "Unknown error. Please contact support."
|
||||||
},
|
},
|
||||||
"clubs": {
|
"clubs": {
|
||||||
"clubList": "Club list",
|
"clubList": "Club list",
|
||||||
|
|
|
@ -236,15 +236,18 @@
|
||||||
"whyAccountSub": "Ce que vous pouvez faire avec un compte",
|
"whyAccountSub": "Ce que vous pouvez faire avec un compte",
|
||||||
"whyAccountParagraph": "Un compte Amicale vous donne la possibilité de participer à diverses activités sur le campus. Vous pouvez rejoindre des clubs ou même créer le votre !",
|
"whyAccountParagraph": "Un compte Amicale vous donne la possibilité de participer à diverses activités sur le campus. Vous pouvez rejoindre des clubs ou même créer le votre !",
|
||||||
"whyAccountParagraph2": "Vous connecter à votre compte Amicale sur l'appli vous permettra de voir tous les clubs en activité, de voter pour les prochaines élections, et plus à venir !",
|
"whyAccountParagraph2": "Vous connecter à votre compte Amicale sur l'appli vous permettra de voir tous les clubs en activité, de voter pour les prochaines élections, et plus à venir !",
|
||||||
"noAccount": "Pas de compte ? Passez à l'Amicale pendant une perm pour en créer un.",
|
"noAccount": "Pas de compte ? Passez à l'Amicale pendant une perm pour en créer un."
|
||||||
"errors": {
|
},
|
||||||
"title": "Erreur !",
|
"errors": {
|
||||||
"connection": "Erreur de réseau. Merci de vérifier votre connexion Internet.",
|
"title": "Erreur !",
|
||||||
"credentials": "Email ou mot de passe invalide.",
|
"badCredentials": "Email ou mot de passe invalide.",
|
||||||
"saveToken": "Erreur de sauvegarde des informations de connexion, merci de contacter le support.",
|
"badToken": "Session expirée, merci de vous reconnecter.",
|
||||||
"consent": "Vous n'avez pas donné votre consentement pour l'utilisation de vos données personnelles.",
|
"noConsent": "Vous n'avez pas donné votre consentement pour l'utilisation de vos données personnelles.",
|
||||||
"unknown": "Erreur inconnue, merci de contacter le support."
|
"badInput": "Entrée invalide. Merci de réessayer.",
|
||||||
}
|
"forbidden": "Vous n'avez pas accès à cette information.",
|
||||||
|
"connectionError": "Erreur de réseau. Merci de vérifier votre connexion Internet.",
|
||||||
|
"serverError": "Erreur de serveur. Merci de contacter le support.",
|
||||||
|
"unknown": "Erreur inconnue. Merci de contacter le support."
|
||||||
},
|
},
|
||||||
"clubs": {
|
"clubs": {
|
||||||
"clubList": "Liste des clubs",
|
"clubList": "Liste des clubs",
|
||||||
|
|
Loading…
Reference in a new issue