From be1f61b671903462b670020d24b1e170158797c6 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Sat, 1 Aug 2020 20:59:59 +0200 Subject: [PATCH] Fix eslint errors First files rewritten to match the new eslint config --- App.js | 369 ++++++++++++++++--------------- src/managers/DashboardManager.js | 38 ++-- src/utils/URLHandler.js | 329 ++++++++++++++------------- src/utils/Utils.js | 60 +++-- 4 files changed, 415 insertions(+), 381 deletions(-) diff --git a/App.js b/App.js index 1f253d0..15a91ba 100644 --- a/App.js +++ b/App.js @@ -1,210 +1,211 @@ // @flow import * as React from 'react'; -import {LogBox, Platform, SafeAreaView, StatusBar, View} from 'react-native'; -import LocaleManager from './src/managers/LocaleManager'; -import AsyncStorageManager from "./src/managers/AsyncStorageManager"; -import CustomIntroSlider from "./src/components/Overrides/CustomIntroSlider"; -import type {CustomTheme} from "./src/managers/ThemeManager"; -import ThemeManager from './src/managers/ThemeManager'; +import {LogBox, Platform, SafeAreaView, View} from 'react-native'; import {NavigationContainer} from '@react-navigation/native'; -import MainNavigator from './src/navigation/MainNavigator'; import {Provider as PaperProvider} from 'react-native-paper'; -import AprilFoolsManager from "./src/managers/AprilFoolsManager"; -import Update from "./src/constants/Update"; -import ConnectionManager from "./src/managers/ConnectionManager"; -import URLHandler from "./src/utils/URLHandler"; -import {setSafeBounceHeight} from "react-navigation-collapsible"; -import SplashScreen from 'react-native-splash-screen' -import {OverflowMenuProvider} from "react-navigation-header-buttons"; +import {setSafeBounceHeight} from 'react-navigation-collapsible'; +import SplashScreen from 'react-native-splash-screen'; +import {OverflowMenuProvider} from 'react-navigation-header-buttons'; +import LocaleManager from './src/managers/LocaleManager'; +import AsyncStorageManager from './src/managers/AsyncStorageManager'; +import CustomIntroSlider from './src/components/Overrides/CustomIntroSlider'; +import type {CustomTheme} from './src/managers/ThemeManager'; +import ThemeManager from './src/managers/ThemeManager'; +import MainNavigator from './src/navigation/MainNavigator'; +import AprilFoolsManager from './src/managers/AprilFoolsManager'; +import Update from './src/constants/Update'; +import ConnectionManager from './src/managers/ConnectionManager'; +import type {ParsedUrlDataType} from './src/utils/URLHandler'; +import URLHandler from './src/utils/URLHandler'; +import {setupStatusBar} from './src/utils/Utils'; // Native optimizations https://reactnavigation.org/docs/react-native-screens // Crashes app when navigating away from webview on android 9+ // enableScreens(true); - -LogBox.ignoreLogs([ // collapsible headers cause this warning, just ignore as it is not an issue - 'Non-serializable values were found in the navigation state', - 'Cannot update a component from inside the function body of a different component', +LogBox.ignoreLogs([ + // collapsible headers cause this warning, just ignore as it is not an issue + 'Non-serializable values were found in the navigation state', + 'Cannot update a component from inside the function body of a different component', ]); -type Props = {}; - -type State = { - isLoading: boolean, - showIntro: boolean, - showUpdate: boolean, - showAprilFools: boolean, - currentTheme: CustomTheme | null, +type StateType = { + isLoading: boolean, + showIntro: boolean, + showUpdate: boolean, + showAprilFools: boolean, + currentTheme: CustomTheme | null, }; -export default class App extends React.Component { +export default class App extends React.Component { + navigatorRef: {current: null | NavigationContainer}; - state = { - isLoading: true, - showIntro: true, - showUpdate: true, - showAprilFools: false, - currentTheme: null, + defaultHomeRoute: string | null; + + defaultHomeData: {[key: string]: string}; + + urlHandler: URLHandler; + + constructor() { + super(); + this.state = { + isLoading: true, + showIntro: true, + showUpdate: true, + showAprilFools: false, + currentTheme: null, }; + LocaleManager.initTranslations(); + this.navigatorRef = React.createRef(); + this.defaultHomeRoute = null; + this.defaultHomeData = {}; + this.urlHandler = new URLHandler(this.onInitialURLParsed, this.onDetectURL); + this.urlHandler.listen(); + setSafeBounceHeight(Platform.OS === 'ios' ? 100 : 20); + this.loadAssetsAsync().finally(() => { + this.onLoadFinished(); + }); + } - navigatorRef: { current: null | NavigationContainer }; + /** + * The app has been started by an url, and it has been parsed. + * Set a new default start route based on the data parsed. + * + * @param parsedData The data parsed from the url + */ + onInitialURLParsed = (parsedData: ParsedUrlDataType) => { + this.defaultHomeRoute = parsedData.route; + this.defaultHomeData = parsedData.data; + }; - defaultHomeRoute: string | null; - defaultHomeData: { [key: string]: any } - - createDrawerNavigator: () => React.Node; - - urlHandler: URLHandler; - - constructor() { - super(); - LocaleManager.initTranslations(); - this.navigatorRef = React.createRef(); - this.defaultHomeRoute = null; - this.defaultHomeData = {}; - this.urlHandler = new URLHandler(this.onInitialURLParsed, this.onDetectURL); - this.urlHandler.listen(); - setSafeBounceHeight(Platform.OS === 'ios' ? 100 : 20); - this.loadAssetsAsync().then(() => { - this.onLoadFinished(); - }); + /** + * An url has been opened and parsed while the app was active. + * Redirect the user to the screen according to parsed data. + * + * @param parsedData The data parsed from the url + */ + onDetectURL = (parsedData: ParsedUrlDataType) => { + // Navigate to nested navigator and pass data to the index screen + const nav = this.navigatorRef.current; + if (nav != null) { + nav.navigate('home', { + screen: 'index', + params: {nextScreen: parsedData.route, data: parsedData.data}, + }); } + }; - /** - * The app has been started by an url, and it has been parsed. - * Set a new default start route based on the data parsed. - * - * @param parsedData The data parsed from the url - */ - onInitialURLParsed = (parsedData: { route: string, data: { [key: string]: any } }) => { - this.defaultHomeRoute = parsedData.route; - this.defaultHomeData = parsedData.data; - }; + /** + * Updates the current theme + */ + onUpdateTheme = () => { + this.setState({ + currentTheme: ThemeManager.getCurrentTheme(), + }); + setupStatusBar(); + }; - /** - * An url has been opened and parsed while the app was active. - * Redirect the user to the screen according to parsed data. - * - * @param parsedData The data parsed from the url - */ - onDetectURL = (parsedData: { route: string, data: { [key: string]: any } }) => { - // Navigate to nested navigator and pass data to the index screen - if (this.navigatorRef.current != null) { - this.navigatorRef.current.navigate('home', { - screen: 'index', - params: {nextScreen: parsedData.route, data: parsedData.data} - }); - } - }; + /** + * Callback when user ends the intro. Save in preferences to avoid showing back the introSlides + */ + onIntroDone = () => { + this.setState({ + showIntro: false, + showUpdate: false, + showAprilFools: false, + }); + AsyncStorageManager.set( + AsyncStorageManager.PREFERENCES.showIntro.key, + false, + ); + AsyncStorageManager.set( + AsyncStorageManager.PREFERENCES.updateNumber.key, + Update.number, + ); + AsyncStorageManager.set( + AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key, + false, + ); + }; - /** - * Updates the current theme - */ - onUpdateTheme = () => { - this.setState({ - currentTheme: ThemeManager.getCurrentTheme() - }); - this.setupStatusBar(); - }; + /** + * Async loading is done, finish processing startup data + */ + onLoadFinished() { + // Only show intro if this is the first time starting the app + ThemeManager.getInstance().setUpdateThemeCallback(this.onUpdateTheme); + // Status bar goes dark if set too fast on ios + if (Platform.OS === 'ios') setTimeout(setupStatusBar, 1000); + else setupStatusBar(); - /** - * Updates status bar content color if on iOS only, - * as the android status bar is always set to black. - */ - setupStatusBar() { - if (ThemeManager.getNightMode()) { - StatusBar.setBarStyle('light-content', true); - } else { - StatusBar.setBarStyle('dark-content', true); - } - if (Platform.OS === "android") - StatusBar.setBackgroundColor(ThemeManager.getCurrentTheme().colors.surface, true); + this.setState({ + isLoading: false, + currentTheme: ThemeManager.getCurrentTheme(), + showIntro: AsyncStorageManager.getBool( + AsyncStorageManager.PREFERENCES.showIntro.key, + ), + showUpdate: + AsyncStorageManager.getNumber( + AsyncStorageManager.PREFERENCES.updateNumber.key, + ) !== Update.number, + showAprilFools: + AprilFoolsManager.getInstance().isAprilFoolsEnabled() && + AsyncStorageManager.getBool( + AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key, + ), + }); + SplashScreen.hide(); + } + + /** + * Loads every async data + * + * @returns {Promise} + */ + loadAssetsAsync = async () => { + await AsyncStorageManager.getInstance().loadPreferences(); + await ConnectionManager.getInstance().recoverLogin(); + }; + + /** + * Renders the app based on loading state + */ + render(): React.Node { + const {state} = this; + if (state.isLoading) { + return null; } - - /** - * Callback when user ends the intro. Save in preferences to avoid showing back the introSlides - */ - onIntroDone = () => { - this.setState({ - showIntro: false, - showUpdate: false, - showAprilFools: false, - }); - AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.showIntro.key, false); - AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.updateNumber.key, Update.number); - AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key, false); - }; - - /** - * Loads every async data - * - * @returns {Promise} - */ - loadAssetsAsync = async () => { - await AsyncStorageManager.getInstance().loadPreferences(); - try { - await ConnectionManager.getInstance().recoverLogin(); - } catch (e) { - } - } - - /** - * Async loading is done, finish processing startup data - */ - onLoadFinished() { - // Only show intro if this is the first time starting the app - this.createDrawerNavigator = () => ; - ThemeManager.getInstance().setUpdateThemeCallback(this.onUpdateTheme); - // Status bar goes dark if set too fast on ios - if (Platform.OS === 'ios') - setTimeout(this.setupStatusBar, 1000); - else - this.setupStatusBar(); - this.setState({ - isLoading: false, - currentTheme: ThemeManager.getCurrentTheme(), - showIntro: AsyncStorageManager.getBool(AsyncStorageManager.PREFERENCES.showIntro.key), - showUpdate: AsyncStorageManager.getNumber(AsyncStorageManager.PREFERENCES.updateNumber.key) - !== Update.number, - showAprilFools: AprilFoolsManager.getInstance().isAprilFoolsEnabled() - && AsyncStorageManager.getBool(AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key), - }); - SplashScreen.hide(); - } - - /** - * Renders the app based on loading state - */ - render() { - if (this.state.isLoading) { - return null; - } else if (this.state.showIntro || this.state.showUpdate || this.state.showAprilFools) { - return ; - } else { - return ( - - - - - - - - - - - - ); - } + if (state.showIntro || state.showUpdate || state.showAprilFools) { + return ( + + ); } + return ( + + + + + + + + + + + + ); + } } diff --git a/src/managers/DashboardManager.js b/src/managers/DashboardManager.js index 603566e..36aa9cb 100644 --- a/src/managers/DashboardManager.js +++ b/src/managers/DashboardManager.js @@ -1,27 +1,21 @@ // @flow -import type {ServiceItem} from "./ServicesManager"; -import ServicesManager from "./ServicesManager"; -import {StackNavigationProp} from "@react-navigation/stack"; -import {getSublistWithIds} from "../utils/Utils"; -import AsyncStorageManager from "./AsyncStorageManager"; - +import type {ServiceItem} from './ServicesManager'; +import ServicesManager from './ServicesManager'; +import {getSublistWithIds} from '../utils/Utils'; +import AsyncStorageManager from './AsyncStorageManager'; export default class DashboardManager extends ServicesManager { - - constructor(nav: StackNavigationProp) { - super(nav) - } - - getCurrentDashboard(): Array { - const dashboardIdList = AsyncStorageManager - .getObject(AsyncStorageManager.PREFERENCES.dashboardItems.key); - const allDatasets = [ - ...this.amicaleDataset, - ...this.studentsDataset, - ...this.insaDataset, - ...this.specialDataset, - ]; - return getSublistWithIds(dashboardIdList, allDatasets); - } + getCurrentDashboard(): Array { + const dashboardIdList = AsyncStorageManager.getObject( + AsyncStorageManager.PREFERENCES.dashboardItems.key, + ); + const allDatasets = [ + ...this.amicaleDataset, + ...this.studentsDataset, + ...this.insaDataset, + ...this.specialDataset, + ]; + return getSublistWithIds(dashboardIdList, allDatasets); + } } diff --git a/src/utils/URLHandler.js b/src/utils/URLHandler.js index c060967..5b8efc7 100644 --- a/src/utils/URLHandler.js +++ b/src/utils/URLHandler.js @@ -2,174 +2,199 @@ import {Linking} from 'react-native'; +export type ParsedUrlDataType = { + route: string, + data: {[key: string]: string}, +}; + +export type ParsedUrlCallbackType = (parsedData: ParsedUrlDataType) => void; + +type RawParsedUrlDataType = { + path: string, + queryParams: {[key: string]: string}, +}; + /** * Class use to handle depp links scanned or clicked. */ export default class URLHandler { + static SCHEME = 'campus-insat://'; // Urls beginning with this string will be opened in the app - static SCHEME = "campus-insat://"; // Urls beginning with this string will be opened in the app + static CLUB_INFO_URL_PATH = 'club'; - static CLUB_INFO_URL_PATH = "club"; - static EVENT_INFO_URL_PATH = "event"; + static EVENT_INFO_URL_PATH = 'event'; - static CLUB_INFO_ROUTE = "club-information"; - static EVENT_INFO_ROUTE = "planning-information"; + static CLUB_INFO_ROUTE = 'club-information'; - onInitialURLParsed: Function; - onDetectURL: Function; + static EVENT_INFO_ROUTE = 'planning-information'; - constructor(onInitialURLParsed: Function, onDetectURL: Function) { - this.onInitialURLParsed = onInitialURLParsed; - this.onDetectURL = onDetectURL; + onInitialURLParsed: ParsedUrlCallbackType; + + onDetectURL: ParsedUrlCallbackType; + + constructor( + onInitialURLParsed: ParsedUrlCallbackType, + onDetectURL: ParsedUrlCallbackType, + ) { + this.onInitialURLParsed = onInitialURLParsed; + this.onDetectURL = onDetectURL; + } + + /** + * Parses the given url to retrieve the corresponding app path and associated arguments. + * + * @param url The url to parse + * @returns {{path: string, queryParams: {}}} + */ + static parseUrl(url: string): RawParsedUrlDataType | null { + let parsedData: RawParsedUrlDataType | null = null; + const urlNoScheme = url.replace(URLHandler.SCHEME, ''); + if (urlNoScheme != null) { + const params = {}; + const [path, fullParamsString] = urlNoScheme.split('?'); + if (fullParamsString != null) { + const paramsStringArray = fullParamsString.split('&'); + paramsStringArray.forEach((paramString: string) => { + const [key, value] = paramString.split('='); + if (value != null) { + params[key] = value; + } + }); + } + if (path != null) parsedData = {path, queryParams: params}; + } + return parsedData; + } + + /** + * Gets routing data corresponding to the given url. + * If the url does not match any existing route, null will be returned. + * + * @param rawParsedUrlData The data just parsed + * @returns {null} + */ + static getUrlData( + rawParsedUrlData: RawParsedUrlDataType | null, + ): ParsedUrlDataType | null { + let parsedData: null | ParsedUrlDataType = null; + if (rawParsedUrlData != null) { + const {path} = rawParsedUrlData; + const {queryParams} = rawParsedUrlData; + if (URLHandler.isClubInformationLink(path)) + parsedData = URLHandler.generateClubInformationData(queryParams); + else if (URLHandler.isPlanningInformationLink(path)) + parsedData = URLHandler.generatePlanningInformationData(queryParams); } - /** - * Parses the given url to retrieve the corresponding app path and associated arguments. - * - * @param url The url to parse - * @returns {{path: string, queryParams: {}}} - */ - static parseUrl(url: string) { - let params = {}; - let path = ""; - let temp = url.replace(URLHandler.SCHEME, ""); - if (temp != null) { - let array = temp.split("?"); - if (array != null && array.length > 0) { - path = array[0]; - } - if (array != null && array.length > 1) { - let tempParams = array[1].split("&"); - for (let i = 0; i < tempParams.length; i++) { - let paramsArray = tempParams[i].split("="); - if (paramsArray.length > 1) { - params[paramsArray[0]] = paramsArray[1]; - } - } - } - } - return {path: path, queryParams: params}; + return parsedData; + } + + /** + * Checks if the given url is in a valid format + * + * @param url The url to check + * @returns {boolean} + */ + static isUrlValid(url: string): boolean { + return this.getUrlData(URLHandler.parseUrl(url)) !== null; + } + + /** + * Check if the given path links to the club information screen + * + * @param path The url to check + * @returns {boolean} + */ + static isClubInformationLink(path: string): boolean { + return path === URLHandler.CLUB_INFO_URL_PATH; + } + + /** + * Check if the given path links to the planning information screen + * + * @param path The url to check + * @returns {boolean} + */ + static isPlanningInformationLink(path: string): boolean { + return path === URLHandler.EVENT_INFO_URL_PATH; + } + + /** + * Generates data formatted for the club information screen from the url parameters. + * + * @param params Url parameters to convert + * @returns {null|{route: string, data: {clubId: number}}} + */ + static generateClubInformationData(params: { + [key: string]: string, + }): ParsedUrlDataType | null { + if (params.id != null) { + const id = parseInt(params.id, 10); + if (!Number.isNaN(id)) + return { + route: URLHandler.CLUB_INFO_ROUTE, + data: {clubId: id.toString()}, + }; } + return null; + } - /** - * Gets routing data corresponding to the given url. - * If the url does not match any existing route, null will be returned. - * - * @param path Url path - * @param queryParams Url parameters - * @returns {null} - */ - static getUrlData({path, queryParams}: { path: string, queryParams: { [key: string]: string } }) { - let data = null; - if (path !== null) { - if (URLHandler.isClubInformationLink(path)) - data = URLHandler.generateClubInformationData(queryParams); - else if (URLHandler.isPlanningInformationLink(path)) - data = URLHandler.generatePlanningInformationData(queryParams); - } - return data; + /** + * Generates data formatted for the planning information screen from the url parameters. + * + * @param params Url parameters to convert + * @returns {null|{route: string, data: {clubId: number}}} + */ + static generatePlanningInformationData(params: { + [key: string]: string, + }): ParsedUrlDataType | null { + if (params.id != null) { + const id = parseInt(params.id, 10); + if (!Number.isNaN(id)) { + return { + route: URLHandler.EVENT_INFO_ROUTE, + data: {eventId: id.toString()}, + }; + } } + return null; + } - /** - * Checks if the given url is in a valid format - * - * @param url The url to check - * @returns {boolean} - */ - static isUrlValid(url: string) { - return this.getUrlData(URLHandler.parseUrl(url)) !== null; + /** + * Starts listening to events. + * + * There are 2 types of event. + * + * A classic event, triggered while the app is active. + * An initial event, called when the app was opened by clicking on a link + * + */ + listen() { + Linking.addEventListener('url', this.onUrl); + Linking.getInitialURL().then(this.onInitialUrl); + } + + /** + * Gets data from the given url and calls the classic callback with it. + * + * @param url The url detected + */ + onUrl = ({url}: {url: string}) => { + if (url != null) { + const data = URLHandler.getUrlData(URLHandler.parseUrl(url)); + if (data != null) this.onDetectURL(data); } + }; - /** - * Check if the given path links to the club information screen - * - * @param path The url to check - * @returns {boolean} - */ - static isClubInformationLink(path: string) { - return path === URLHandler.CLUB_INFO_URL_PATH; + /** + * Gets data from the given url and calls the initial callback with it. + * + * @param url The url detected + */ + onInitialUrl = (url: ?string) => { + if (url != null) { + const data = URLHandler.getUrlData(URLHandler.parseUrl(url)); + if (data != null) this.onInitialURLParsed(data); } - - /** - * Check if the given path links to the planning information screen - * - * @param path The url to check - * @returns {boolean} - */ - static isPlanningInformationLink(path: string) { - return path === URLHandler.EVENT_INFO_URL_PATH; - } - - /** - * Generates data formatted for the club information screen from the url parameters. - * - * @param params Url parameters to convert - * @returns {null|{route: string, data: {clubId: number}}} - */ - static generateClubInformationData(params: Object): Object | null { - if (params !== undefined && params.id !== undefined) { - let id = parseInt(params.id); - if (!isNaN(id)) { - return {route: URLHandler.CLUB_INFO_ROUTE, data: {clubId: id}}; - } - } - return null; - } - - /** - * Generates data formatted for the planning information screen from the url parameters. - * - * @param params Url parameters to convert - * @returns {null|{route: string, data: {clubId: number}}} - */ - static generatePlanningInformationData(params: Object): Object | null { - if (params !== undefined && params.id !== undefined) { - let id = parseInt(params.id); - if (!isNaN(id)) { - return {route: URLHandler.EVENT_INFO_ROUTE, data: {eventId: id}}; - } - } - return null; - } - - /** - * Starts listening to events. - * - * There are 2 types of event. - * - * A classic event, triggered while the app is active. - * An initial event, called when the app was opened by clicking on a link - * - */ - listen() { - Linking.addEventListener('url', this.onUrl); - Linking.getInitialURL().then(this.onInitialUrl); - } - - /** - * Gets data from the given url and calls the classic callback with it. - * - * @param url The url detected - */ - onUrl = ({url}: { url: string }) => { - if (url != null) { - let data = URLHandler.getUrlData(URLHandler.parseUrl(url)); - if (data !== null) - this.onDetectURL(data); - } - }; - - /** - * Gets data from the given url and calls the initial callback with it. - * - * @param url The url detected - */ - onInitialUrl = (url: ?string) => { - if (url != null) { - let data = URLHandler.getUrlData(URLHandler.parseUrl(url)); - if (data !== null) - this.onInitialURLParsed(data); - } - }; - + }; } diff --git a/src/utils/Utils.js b/src/utils/Utils.js index 60d1162..d8a16c1 100644 --- a/src/utils/Utils.js +++ b/src/utils/Utils.js @@ -1,5 +1,7 @@ // @flow +import {Platform, StatusBar} from 'react-native'; +import ThemeManager from '../managers/ThemeManager'; /** * Gets a sublist of the given list with items of the given ids only @@ -10,28 +12,40 @@ * @param originalList The original list * @returns {[]} */ -export function getSublistWithIds( - idList: Array, - originalList: Array<{ key: string, [key: string]: any }> -): Array<{ key: string, [key: string]: any }> { - let subList = []; - for (let i = 0; i < subList.length; i++) { - subList.push(null); +export function getSublistWithIds( + idList: Array, + originalList: Array<{key: string, ...T}>, +): Array<{key: string, ...T} | null> { + const subList = []; + for (let i = 0; i < idList.length; i += 1) { + subList.push(null); + } + let itemsAdded = 0; + for (let i = 0; i < originalList.length; i += 1) { + const item = originalList[i]; + if (idList.includes(item.key)) { + subList[idList.indexOf(item.key)] = item; + itemsAdded += 1; + if (itemsAdded === idList.length) break; } - let itemsAdded = 0; - for (let i = 0; i < originalList.length; i++) { - const item = originalList[i]; - if (idList.includes(item.key)) { - subList[idList.indexOf(item.key)] = item; - itemsAdded++; - if (itemsAdded === idList.length) - break; - } - } - for (let i = 0; i < subList.length; i++) { - if (subList[i] == null) - subList.splice(i, 1); - } - - return subList; + } + return subList; +} + +/** + * Updates status bar content color if on iOS only, + * as the android status bar is always set to black. + */ +export function setupStatusBar() { + if (ThemeManager.getNightMode()) { + StatusBar.setBarStyle('light-content', true); + } else { + StatusBar.setBarStyle('dark-content', true); + } + if (Platform.OS === 'android') { + StatusBar.setBackgroundColor( + ThemeManager.getCurrentTheme().colors.surface, + true, + ); + } }