Fix eslint errors

First files rewritten to match the new eslint config
This commit is contained in:
Arnaud Vergnet 2020-08-01 20:59:59 +02:00
parent b596f68abe
commit be1f61b671
4 changed files with 415 additions and 381 deletions

205
App.js
View file

@ -1,36 +1,36 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {LogBox, Platform, SafeAreaView, StatusBar, View} from 'react-native'; import {LogBox, Platform, SafeAreaView, 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 {NavigationContainer} from '@react-navigation/native'; import {NavigationContainer} from '@react-navigation/native';
import MainNavigator from './src/navigation/MainNavigator';
import {Provider as PaperProvider} from 'react-native-paper'; import {Provider as PaperProvider} from 'react-native-paper';
import AprilFoolsManager from "./src/managers/AprilFoolsManager"; import {setSafeBounceHeight} from 'react-navigation-collapsible';
import Update from "./src/constants/Update"; import SplashScreen from 'react-native-splash-screen';
import ConnectionManager from "./src/managers/ConnectionManager"; import {OverflowMenuProvider} from 'react-navigation-header-buttons';
import URLHandler from "./src/utils/URLHandler"; import LocaleManager from './src/managers/LocaleManager';
import {setSafeBounceHeight} from "react-navigation-collapsible"; import AsyncStorageManager from './src/managers/AsyncStorageManager';
import SplashScreen from 'react-native-splash-screen' import CustomIntroSlider from './src/components/Overrides/CustomIntroSlider';
import {OverflowMenuProvider} from "react-navigation-header-buttons"; 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 // Native optimizations https://reactnavigation.org/docs/react-native-screens
// Crashes app when navigating away from webview on android 9+ // Crashes app when navigating away from webview on android 9+
// enableScreens(true); // enableScreens(true);
LogBox.ignoreLogs([
LogBox.ignoreLogs([ // collapsible headers cause this warning, just ignore as it is not an issue // collapsible headers cause this warning, just ignore as it is not an issue
'Non-serializable values were found in the navigation state', 'Non-serializable values were found in the navigation state',
'Cannot update a component from inside the function body of a different component', 'Cannot update a component from inside the function body of a different component',
]); ]);
type Props = {}; type StateType = {
type State = {
isLoading: boolean, isLoading: boolean,
showIntro: boolean, showIntro: boolean,
showUpdate: boolean, showUpdate: boolean,
@ -38,27 +38,24 @@ type State = {
currentTheme: CustomTheme | null, currentTheme: CustomTheme | null,
}; };
export default class App extends React.Component<Props, State> { export default class App extends React.Component<null, StateType> {
navigatorRef: {current: null | NavigationContainer};
state = { defaultHomeRoute: string | null;
defaultHomeData: {[key: string]: string};
urlHandler: URLHandler;
constructor() {
super();
this.state = {
isLoading: true, isLoading: true,
showIntro: true, showIntro: true,
showUpdate: true, showUpdate: true,
showAprilFools: false, showAprilFools: false,
currentTheme: null, currentTheme: null,
}; };
navigatorRef: { current: null | NavigationContainer };
defaultHomeRoute: string | null;
defaultHomeData: { [key: string]: any }
createDrawerNavigator: () => React.Node;
urlHandler: URLHandler;
constructor() {
super();
LocaleManager.initTranslations(); LocaleManager.initTranslations();
this.navigatorRef = React.createRef(); this.navigatorRef = React.createRef();
this.defaultHomeRoute = null; this.defaultHomeRoute = null;
@ -66,7 +63,7 @@ export default class App extends React.Component<Props, State> {
this.urlHandler = new URLHandler(this.onInitialURLParsed, this.onDetectURL); this.urlHandler = new URLHandler(this.onInitialURLParsed, this.onDetectURL);
this.urlHandler.listen(); this.urlHandler.listen();
setSafeBounceHeight(Platform.OS === 'ios' ? 100 : 20); setSafeBounceHeight(Platform.OS === 'ios' ? 100 : 20);
this.loadAssetsAsync().then(() => { this.loadAssetsAsync().finally(() => {
this.onLoadFinished(); this.onLoadFinished();
}); });
} }
@ -77,7 +74,7 @@ export default class App extends React.Component<Props, State> {
* *
* @param parsedData The data parsed from the url * @param parsedData The data parsed from the url
*/ */
onInitialURLParsed = (parsedData: { route: string, data: { [key: string]: any } }) => { onInitialURLParsed = (parsedData: ParsedUrlDataType) => {
this.defaultHomeRoute = parsedData.route; this.defaultHomeRoute = parsedData.route;
this.defaultHomeData = parsedData.data; this.defaultHomeData = parsedData.data;
}; };
@ -88,12 +85,13 @@ export default class App extends React.Component<Props, State> {
* *
* @param parsedData The data parsed from the url * @param parsedData The data parsed from the url
*/ */
onDetectURL = (parsedData: { route: string, data: { [key: string]: any } }) => { onDetectURL = (parsedData: ParsedUrlDataType) => {
// Navigate to nested navigator and pass data to the index screen // Navigate to nested navigator and pass data to the index screen
if (this.navigatorRef.current != null) { const nav = this.navigatorRef.current;
this.navigatorRef.current.navigate('home', { if (nav != null) {
nav.navigate('home', {
screen: 'index', screen: 'index',
params: {nextScreen: parsedData.route, data: parsedData.data} params: {nextScreen: parsedData.route, data: parsedData.data},
}); });
} }
}; };
@ -103,25 +101,11 @@ export default class App extends React.Component<Props, State> {
*/ */
onUpdateTheme = () => { onUpdateTheme = () => {
this.setState({ this.setState({
currentTheme: ThemeManager.getCurrentTheme() currentTheme: ThemeManager.getCurrentTheme(),
}); });
this.setupStatusBar(); 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);
}
/** /**
* Callback when user ends the intro. Save in preferences to avoid showing back the introSlides * Callback when user ends the intro. Save in preferences to avoid showing back the introSlides
*/ */
@ -131,11 +115,49 @@ export default class App extends React.Component<Props, State> {
showUpdate: false, showUpdate: false,
showAprilFools: false, showAprilFools: false,
}); });
AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.showIntro.key, false); AsyncStorageManager.set(
AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.updateNumber.key, Update.number); AsyncStorageManager.PREFERENCES.showIntro.key,
AsyncStorageManager.set(AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key, false); false,
);
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.updateNumber.key,
Update.number,
);
AsyncStorageManager.set(
AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key,
false,
);
}; };
/**
* 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();
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 * Loads every async data
* *
@ -143,58 +165,38 @@ export default class App extends React.Component<Props, State> {
*/ */
loadAssetsAsync = async () => { loadAssetsAsync = async () => {
await AsyncStorageManager.getInstance().loadPreferences(); await AsyncStorageManager.getInstance().loadPreferences();
try {
await ConnectionManager.getInstance().recoverLogin(); 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 = () => <MainNavigator
defaultHomeRoute={this.defaultHomeRoute}
defaultHomeData={this.defaultHomeData}
/>;
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 * Renders the app based on loading state
*/ */
render() { render(): React.Node {
if (this.state.isLoading) { const {state} = this;
if (state.isLoading) {
return null; return null;
} else if (this.state.showIntro || this.state.showUpdate || this.state.showAprilFools) { }
return <CustomIntroSlider if (state.showIntro || state.showUpdate || state.showAprilFools) {
onDone={this.onIntroDone}
isUpdate={this.state.showUpdate && !this.state.showIntro}
isAprilFools={this.state.showAprilFools && !this.state.showIntro}
/>;
} else {
return ( return (
<PaperProvider theme={this.state.currentTheme}> <CustomIntroSlider
onDone={this.onIntroDone}
isUpdate={state.showUpdate && !state.showIntro}
isAprilFools={state.showAprilFools && !state.showIntro}
/>
);
}
return (
<PaperProvider theme={state.currentTheme}>
<OverflowMenuProvider> <OverflowMenuProvider>
<View style={{backgroundColor: ThemeManager.getCurrentTheme().colors.background, flex: 1}}> <View
style={{
backgroundColor: ThemeManager.getCurrentTheme().colors.background,
flex: 1,
}}>
<SafeAreaView style={{flex: 1}}> <SafeAreaView style={{flex: 1}}>
<NavigationContainer theme={this.state.currentTheme} ref={this.navigatorRef}> <NavigationContainer
theme={state.currentTheme}
ref={this.navigatorRef}>
<MainNavigator <MainNavigator
defaultHomeRoute={this.defaultHomeRoute} defaultHomeRoute={this.defaultHomeRoute}
defaultHomeData={this.defaultHomeData} defaultHomeData={this.defaultHomeData}
@ -206,5 +208,4 @@ export default class App extends React.Component<Props, State> {
</PaperProvider> </PaperProvider>
); );
} }
}
} }

View file

@ -1,21 +1,15 @@
// @flow // @flow
import type {ServiceItem} from "./ServicesManager"; import type {ServiceItem} from './ServicesManager';
import ServicesManager from "./ServicesManager"; import ServicesManager from './ServicesManager';
import {StackNavigationProp} from "@react-navigation/stack"; import {getSublistWithIds} from '../utils/Utils';
import {getSublistWithIds} from "../utils/Utils"; import AsyncStorageManager from './AsyncStorageManager';
import AsyncStorageManager from "./AsyncStorageManager";
export default class DashboardManager extends ServicesManager { export default class DashboardManager extends ServicesManager {
getCurrentDashboard(): Array<ServiceItem | null> {
constructor(nav: StackNavigationProp) { const dashboardIdList = AsyncStorageManager.getObject(
super(nav) AsyncStorageManager.PREFERENCES.dashboardItems.key,
} );
getCurrentDashboard(): Array<ServiceItem> {
const dashboardIdList = AsyncStorageManager
.getObject(AsyncStorageManager.PREFERENCES.dashboardItems.key);
const allDatasets = [ const allDatasets = [
...this.amicaleDataset, ...this.amicaleDataset,
...this.studentsDataset, ...this.studentsDataset,

View file

@ -2,23 +2,40 @@
import {Linking} from 'react-native'; 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. * Class use to handle depp links scanned or clicked.
*/ */
export default class URLHandler { 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 CLUB_INFO_ROUTE = 'club-information';
static EVENT_INFO_ROUTE = "planning-information";
onInitialURLParsed: Function; static EVENT_INFO_ROUTE = 'planning-information';
onDetectURL: Function;
constructor(onInitialURLParsed: Function, onDetectURL: Function) { onInitialURLParsed: ParsedUrlCallbackType;
onDetectURL: ParsedUrlCallbackType;
constructor(
onInitialURLParsed: ParsedUrlCallbackType,
onDetectURL: ParsedUrlCallbackType,
) {
this.onInitialURLParsed = onInitialURLParsed; this.onInitialURLParsed = onInitialURLParsed;
this.onDetectURL = onDetectURL; this.onDetectURL = onDetectURL;
} }
@ -29,45 +46,47 @@ export default class URLHandler {
* @param url The url to parse * @param url The url to parse
* @returns {{path: string, queryParams: {}}} * @returns {{path: string, queryParams: {}}}
*/ */
static parseUrl(url: string) { static parseUrl(url: string): RawParsedUrlDataType | null {
let params = {}; let parsedData: RawParsedUrlDataType | null = null;
let path = ""; const urlNoScheme = url.replace(URLHandler.SCHEME, '');
let temp = url.replace(URLHandler.SCHEME, ""); if (urlNoScheme != null) {
if (temp != null) { const params = {};
let array = temp.split("?"); const [path, fullParamsString] = urlNoScheme.split('?');
if (array != null && array.length > 0) { if (fullParamsString != null) {
path = array[0]; const paramsStringArray = fullParamsString.split('&');
paramsStringArray.forEach((paramString: string) => {
const [key, value] = paramString.split('=');
if (value != null) {
params[key] = value;
} }
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];
} }
if (path != null) parsedData = {path, queryParams: params};
} }
} return parsedData;
}
return {path: path, queryParams: params};
} }
/** /**
* Gets routing data corresponding to the given url. * Gets routing data corresponding to the given url.
* If the url does not match any existing route, null will be returned. * If the url does not match any existing route, null will be returned.
* *
* @param path Url path * @param rawParsedUrlData The data just parsed
* @param queryParams Url parameters
* @returns {null} * @returns {null}
*/ */
static getUrlData({path, queryParams}: { path: string, queryParams: { [key: string]: string } }) { static getUrlData(
let data = null; rawParsedUrlData: RawParsedUrlDataType | null,
if (path !== null) { ): ParsedUrlDataType | null {
let parsedData: null | ParsedUrlDataType = null;
if (rawParsedUrlData != null) {
const {path} = rawParsedUrlData;
const {queryParams} = rawParsedUrlData;
if (URLHandler.isClubInformationLink(path)) if (URLHandler.isClubInformationLink(path))
data = URLHandler.generateClubInformationData(queryParams); parsedData = URLHandler.generateClubInformationData(queryParams);
else if (URLHandler.isPlanningInformationLink(path)) else if (URLHandler.isPlanningInformationLink(path))
data = URLHandler.generatePlanningInformationData(queryParams); parsedData = URLHandler.generatePlanningInformationData(queryParams);
} }
return data;
return parsedData;
} }
/** /**
@ -76,7 +95,7 @@ export default class URLHandler {
* @param url The url to check * @param url The url to check
* @returns {boolean} * @returns {boolean}
*/ */
static isUrlValid(url: string) { static isUrlValid(url: string): boolean {
return this.getUrlData(URLHandler.parseUrl(url)) !== null; return this.getUrlData(URLHandler.parseUrl(url)) !== null;
} }
@ -86,7 +105,7 @@ export default class URLHandler {
* @param path The url to check * @param path The url to check
* @returns {boolean} * @returns {boolean}
*/ */
static isClubInformationLink(path: string) { static isClubInformationLink(path: string): boolean {
return path === URLHandler.CLUB_INFO_URL_PATH; return path === URLHandler.CLUB_INFO_URL_PATH;
} }
@ -96,7 +115,7 @@ export default class URLHandler {
* @param path The url to check * @param path The url to check
* @returns {boolean} * @returns {boolean}
*/ */
static isPlanningInformationLink(path: string) { static isPlanningInformationLink(path: string): boolean {
return path === URLHandler.EVENT_INFO_URL_PATH; return path === URLHandler.EVENT_INFO_URL_PATH;
} }
@ -106,12 +125,16 @@ export default class URLHandler {
* @param params Url parameters to convert * @param params Url parameters to convert
* @returns {null|{route: string, data: {clubId: number}}} * @returns {null|{route: string, data: {clubId: number}}}
*/ */
static generateClubInformationData(params: Object): Object | null { static generateClubInformationData(params: {
if (params !== undefined && params.id !== undefined) { [key: string]: string,
let id = parseInt(params.id); }): ParsedUrlDataType | null {
if (!isNaN(id)) { if (params.id != null) {
return {route: URLHandler.CLUB_INFO_ROUTE, data: {clubId: id}}; const id = parseInt(params.id, 10);
} if (!Number.isNaN(id))
return {
route: URLHandler.CLUB_INFO_ROUTE,
data: {clubId: id.toString()},
};
} }
return null; return null;
} }
@ -122,11 +145,16 @@ export default class URLHandler {
* @param params Url parameters to convert * @param params Url parameters to convert
* @returns {null|{route: string, data: {clubId: number}}} * @returns {null|{route: string, data: {clubId: number}}}
*/ */
static generatePlanningInformationData(params: Object): Object | null { static generatePlanningInformationData(params: {
if (params !== undefined && params.id !== undefined) { [key: string]: string,
let id = parseInt(params.id); }): ParsedUrlDataType | null {
if (!isNaN(id)) { if (params.id != null) {
return {route: URLHandler.EVENT_INFO_ROUTE, data: {eventId: id}}; const id = parseInt(params.id, 10);
if (!Number.isNaN(id)) {
return {
route: URLHandler.EVENT_INFO_ROUTE,
data: {eventId: id.toString()},
};
} }
} }
return null; return null;
@ -151,11 +179,10 @@ export default class URLHandler {
* *
* @param url The url detected * @param url The url detected
*/ */
onUrl = ({url}: { url: string }) => { onUrl = ({url}: {url: string}) => {
if (url != null) { if (url != null) {
let data = URLHandler.getUrlData(URLHandler.parseUrl(url)); const data = URLHandler.getUrlData(URLHandler.parseUrl(url));
if (data !== null) if (data != null) this.onDetectURL(data);
this.onDetectURL(data);
} }
}; };
@ -166,10 +193,8 @@ export default class URLHandler {
*/ */
onInitialUrl = (url: ?string) => { onInitialUrl = (url: ?string) => {
if (url != null) { if (url != null) {
let data = URLHandler.getUrlData(URLHandler.parseUrl(url)); const data = URLHandler.getUrlData(URLHandler.parseUrl(url));
if (data !== null) if (data != null) this.onInitialURLParsed(data);
this.onInitialURLParsed(data);
} }
}; };
} }

View file

@ -1,5 +1,7 @@
// @flow // @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 * Gets a sublist of the given list with items of the given ids only
@ -10,28 +12,40 @@
* @param originalList The original list * @param originalList The original list
* @returns {[]} * @returns {[]}
*/ */
export function getSublistWithIds( export function getSublistWithIds<T>(
idList: Array<string>, idList: Array<string>,
originalList: Array<{ key: string, [key: string]: any }> originalList: Array<{key: string, ...T}>,
): Array<{ key: string, [key: string]: any }> { ): Array<{key: string, ...T} | null> {
let subList = []; const subList = [];
for (let i = 0; i < subList.length; i++) { for (let i = 0; i < idList.length; i += 1) {
subList.push(null); subList.push(null);
} }
let itemsAdded = 0; let itemsAdded = 0;
for (let i = 0; i < originalList.length; i++) { for (let i = 0; i < originalList.length; i += 1) {
const item = originalList[i]; const item = originalList[i];
if (idList.includes(item.key)) { if (idList.includes(item.key)) {
subList[idList.indexOf(item.key)] = item; subList[idList.indexOf(item.key)] = item;
itemsAdded++; itemsAdded += 1;
if (itemsAdded === idList.length) if (itemsAdded === idList.length) break;
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,
);
}
}