From 17016b645265f7e9ac39e402fc8ab07d6f117014 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Sat, 4 Apr 2020 16:09:04 +0200 Subject: [PATCH] Recover credentials on startup for increased performance and added login/profile button on home --- App.js | 5 ++ components/Amicale/AuthenticatedScreen.js | 30 ++++----- components/Sidebar/Sidebar.js | 5 +- managers/ConnectionManager.js | 81 ++++++++++++----------- screens/Amicale/LoginScreen.js | 4 +- screens/HomeScreen.js | 22 ++++++ 6 files changed, 85 insertions(+), 62 deletions(-) diff --git a/App.js b/App.js index d0962f8..ea42128 100644 --- a/App.js +++ b/App.js @@ -14,6 +14,7 @@ import {initExpoToken} from "./utils/Notifications"; import {Provider as PaperProvider} from 'react-native-paper'; import AprilFoolsManager from "./managers/AprilFoolsManager"; import Update from "./constants/Update"; +import ConnectionManager from "./managers/ConnectionManager"; type Props = {}; @@ -91,6 +92,10 @@ export default class App extends React.Component { await AsyncStorageManager.getInstance().loadPreferences(); ThemeManager.getInstance().setUpdateThemeCallback(this.onUpdateTheme); await initExpoToken(); + try { + await ConnectionManager.getInstance().recoverLogin(); + } catch (e) {} + this.onLoadFinished(); } diff --git a/components/Amicale/AuthenticatedScreen.js b/components/Amicale/AuthenticatedScreen.js index 807687e..dfb06b1 100644 --- a/components/Amicale/AuthenticatedScreen.js +++ b/components/Amicale/AuthenticatedScreen.js @@ -24,7 +24,7 @@ class AuthenticatedScreen extends React.Component { loading: true, }; - currentUserToken: string; + currentUserToken: string | null; connectionManager: ConnectionManager; errorCode: number; data: Object; @@ -35,8 +35,6 @@ class AuthenticatedScreen extends React.Component { this.colors = props.theme.colors; this.connectionManager = ConnectionManager.getInstance(); this.props.navigation.addListener('focus', this.onScreenFocus.bind(this)); - - this.fetchData(); } onScreenFocus() { @@ -47,26 +45,24 @@ class AuthenticatedScreen extends React.Component { fetchData = () => { if (!this.state.loading) this.setState({loading: true}); - this.connectionManager.isLoggedIn() - .then(() => { - this.connectionManager.authenticatedRequest(this.props.link) - .then((data) => { - this.onFinishedLoading(data, -1); - }) - .catch((error) => { - this.onFinishedLoading(undefined, error); - }); - }) - .catch((error) => { - this.onFinishedLoading(undefined, ERROR_TYPE.BAD_CREDENTIALS); - }); + if (this.connectionManager.isLoggedIn()) { + this.connectionManager.authenticatedRequest(this.props.link) + .then((data) => { + this.onFinishedLoading(data, -1); + }) + .catch((error) => { + this.onFinishedLoading(undefined, error); + }); + } else { + this.onFinishedLoading(undefined, ERROR_TYPE.BAD_CREDENTIALS); + } }; onFinishedLoading(data: Object, error: number) { this.data = data; this.currentUserToken = data !== undefined ? this.connectionManager.getToken() - : ''; + : null; this.errorCode = error; this.setState({loading: false}); } diff --git a/components/Sidebar/Sidebar.js b/components/Sidebar/Sidebar.js index cd226df..1dbcc17 100644 --- a/components/Sidebar/Sidebar.js +++ b/components/Sidebar/Sidebar.js @@ -154,13 +154,12 @@ class SideBar extends React.PureComponent { ]; this.getRenderItem = this.getRenderItem.bind(this); this.colors = props.theme.colors; - ConnectionManager.getInstance().setLoginCallback((value) => this.onLoginStateChange(value)); + ConnectionManager.getInstance().addLoginStateListener((value) => this.onLoginStateChange(value)); this.state = { active: 'Home', - isLoggedIn: false, + isLoggedIn: ConnectionManager.getInstance().isLoggedIn(), dialogVisible: false, }; - ConnectionManager.getInstance().isLoggedIn().then(data => undefined).catch(error => undefined); } showDisconnectDialog = () => this.setState({ dialogVisible: true }); diff --git a/managers/ConnectionManager.js b/managers/ConnectionManager.js index e116922..e7eb80e 100644 --- a/managers/ConnectionManager.js +++ b/managers/ConnectionManager.js @@ -16,10 +16,17 @@ export default class ConnectionManager { static instance: ConnectionManager | null = null; #email: string; - #token: string; + #token: string | null; loginCallback: Function; + listeners: Array; + + constructor() { + this.#token = null; + this.listeners = []; + } + /** * Get this class instance or create one if none is found * @returns {ConnectionManager} @@ -35,20 +42,24 @@ export default class ConnectionManager { } onLoginStateChange(newState: boolean) { - this.loginCallback(newState); + for (let i = 0; i < this.listeners.length; i++) { + if (this.listeners[i] !== undefined) + this.listeners[i](newState); + } } - setLoginCallback(callback: Function) { - this.loginCallback = callback; + addLoginStateListener(listener: Function) { + this.listeners.push(listener); } async recoverLogin() { return new Promise((resolve, reject) => { - if (this.#token !== undefined) + if (this.#token !== null) resolve(this.#token); else { SecureStore.getItemAsync('token') .then((token) => { + this.#token = token; if (token !== null) { this.onLoginStateChange(true); resolve(token); @@ -62,16 +73,8 @@ export default class ConnectionManager { }); } - async isLoggedIn() { - return new Promise((resolve, reject) => { - this.recoverLogin() - .then(() => { - resolve(true); - }) - .catch(() => { - reject(false); - }) - }); + isLoggedIn() { + return this.#token !== null; } async saveLogin(email: string, token: string) { @@ -93,6 +96,7 @@ export default class ConnectionManager { return new Promise((resolve, reject) => { SecureStore.deleteItemAsync('token') .then(() => { + this.#token = null; this.onLoginStateChange(false); resolve(true); }) @@ -166,32 +170,29 @@ export default class ConnectionManager { async authenticatedRequest(url: string) { return new Promise((resolve, reject) => { - this.recoverLogin() - .then(token => { - fetch(url, { - method: 'POST', - headers: new Headers({ - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }), - body: JSON.stringify({token: token}) - }).then(async (response) => response.json()) - .then((data) => { - if (this.isRequestResponseValid(data)) { - if (data.state) - resolve(data.data); - else - reject(ERROR_TYPE.BAD_CREDENTIALS); - } else - reject(ERROR_TYPE.CONNECTION_ERROR); - }) - .catch(() => { + if (this.#token !== null) { + fetch(url, { + method: 'POST', + headers: new Headers({ + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }), + body: JSON.stringify({token: this.#token}) + }).then(async (response) => response.json()) + .then((data) => { + if (this.isRequestResponseValid(data)) { + if (data.state) + resolve(data.data); + else + reject(ERROR_TYPE.BAD_CREDENTIALS); + } else reject(ERROR_TYPE.CONNECTION_ERROR); - }); - }) - .catch(() => { - reject(ERROR_TYPE.NO_TOKEN); - }); + }) + .catch(() => { + reject(ERROR_TYPE.CONNECTION_ERROR); + }); + } else + reject(ERROR_TYPE.NO_TOKEN); }); } } diff --git a/screens/Amicale/LoginScreen.js b/screens/Amicale/LoginScreen.js index 7b84082..8967813 100644 --- a/screens/Amicale/LoginScreen.js +++ b/screens/Amicale/LoginScreen.js @@ -32,8 +32,8 @@ const emailRegex = /^.+@.+\..+$/; class LoginScreen extends React.Component { state = { - email: '', - password: '', + email: 'vergnet@etud.insa-toulouse.fr', + password: '3D514ùdsqg', isEmailValidated: false, isPasswordValidated: false, loading: false, diff --git a/screens/HomeScreen.js b/screens/HomeScreen.js index 8831dd9..f39617a 100644 --- a/screens/HomeScreen.js +++ b/screens/HomeScreen.js @@ -12,6 +12,8 @@ import PreviewEventDashboardItem from "../components/Home/PreviewEventDashboardI import {stringToDate} from "../utils/Planning"; import {openBrowser} from "../utils/WebBrowser"; import ActionsDashBoardItem from "../components/Home/ActionsDashboardItem"; +import HeaderButton from "../components/Custom/HeaderButton"; +import ConnectionManager from "../managers/ConnectionManager"; // import DATA from "../dashboard_data.json"; @@ -33,6 +35,7 @@ type Props = { type State = { imageModalVisible: boolean, imageList: Array, + isLoggedIn: boolean, } /** @@ -52,10 +55,12 @@ class HomeScreen extends React.Component { state = { imageModalVisible: false, imageList: [], + isLoggedIn: ConnectionManager.getInstance().isLoggedIn(), }; constructor(props) { super(props); + ConnectionManager.getInstance().addLoginStateListener((value) => this.setState({isLoggedIn: value})); this.onProxiwashClick = this.onProxiwashClick.bind(this); this.onTutorInsaClick = this.onTutorInsaClick.bind(this); this.onMenuClick = this.onMenuClick.bind(this); @@ -76,6 +81,23 @@ class HomeScreen extends React.Component { return date.toLocaleString(); } + componentDidMount() { + this.props.navigation.setOptions({ + headerRight: this.getHeaderButton, + }); + } + + getHeaderButton = () => { + const screen = this.state.isLoggedIn + ? "ProfileScreen" + : "LoginScreen"; + const icon = this.state.isLoggedIn + ? "account" + : "login"; + const onPress = () => this.props.navigation.navigate(screen); + return ; + }; + onProxiwashClick() { this.props.navigation.navigate('Proxiwash'); }