diff --git a/package-lock.json b/package-lock.json index 6a2db43..b88f93a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2501,6 +2501,12 @@ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" }, + "@types/i18n-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/i18n-js/-/i18n-js-3.0.3.tgz", + "integrity": "sha512-GiZzazvxQ5j+EA4Zf4MtDsSaokAR/gW7FxxTlHi2p2xKFUhwAUT0B/MB8WL77P1TcqAO3MefWorFFyZS8F7s0Q==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -2602,6 +2608,16 @@ "@types/react": "*" } }, + "@types/react-native-vector-icons": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.6.tgz", + "integrity": "sha512-lAyxNfMd5L1xZvXWsGcJmNegDf61TAp40uL6ashNNWj9W3IrDJO59L9+9inh0Y2MsEZpLTdxzVU8Unb4/0FQng==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/react-native": "*" + } + }, "@types/react-test-renderer": { "version": "16.9.3", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.3.tgz", diff --git a/package.json b/package.json index 1248aab..f46612a 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,8 @@ "@babel/core": "^7.11.0", "@babel/runtime": "^7.11.0", "@react-native-community/eslint-config": "^1.1.0", + "@types/i18n-js": "^3.0.3", + "@types/react-native-vector-icons": "^6.4.6", "@types/jest": "^25.2.3", "@types/react-native": "^0.63.2", "@types/react-test-renderer": "^16.9.2", diff --git a/src/managers/AprilFoolsManager.js b/src/managers/AprilFoolsManager.ts similarity index 95% rename from src/managers/AprilFoolsManager.js rename to src/managers/AprilFoolsManager.ts index 600bad8..d609db0 100644 --- a/src/managers/AprilFoolsManager.js +++ b/src/managers/AprilFoolsManager.ts @@ -17,10 +17,7 @@ * along with Campus INSAT. If not, see . */ -// @flow - import type {ProxiwashMachineType} from '../screens/Proxiwash/ProxiwashScreen'; -import type {CustomThemeType} from './ThemeManager'; import type {RuFoodCategoryType} from '../screens/Services/SelfMenuScreen'; /** @@ -57,8 +54,9 @@ export default class AprilFoolsManager { * @returns {ThemeManager} */ static getInstance(): AprilFoolsManager { - if (AprilFoolsManager.instance == null) + if (AprilFoolsManager.instance == null) { AprilFoolsManager.instance = new AprilFoolsManager(); + } return AprilFoolsManager.instance; } @@ -130,7 +128,9 @@ export default class AprilFoolsManager { * @param currentTheme * @returns {{colors: {textDisabled: string, agendaDayTextColor: string, surface: string, background: string, dividerBackground: string, accent: string, agendaBackgroundColor: string, tabIcon: string, card: string, primary: string}}} */ - static getAprilFoolsTheme(currentTheme: CustomThemeType): CustomThemeType { + static getAprilFoolsTheme( + currentTheme: ReactNativePaper.Theme, + ): ReactNativePaper.Theme { return { ...currentTheme, colors: { diff --git a/src/managers/AsyncStorageManager.ts b/src/managers/AsyncStorageManager.ts index 483d0c8..f125936 100644 --- a/src/managers/AsyncStorageManager.ts +++ b/src/managers/AsyncStorageManager.ts @@ -198,7 +198,7 @@ export default class AsyncStorageManager { * @param key * @returns {{...}} */ - static getObject(key: string): object | Array { + static getObject(key: string): T { return JSON.parse(AsyncStorageManager.getString(key)); } diff --git a/src/managers/ConnectionManager.js b/src/managers/ConnectionManager.ts similarity index 80% rename from src/managers/ConnectionManager.js rename to src/managers/ConnectionManager.ts index 781b31e..51c160f 100644 --- a/src/managers/ConnectionManager.js +++ b/src/managers/ConnectionManager.ts @@ -20,7 +20,7 @@ // @flow import * as Keychain from 'react-native-keychain'; -import type {ApiDataLoginType, ApiGenericDataType} from '../utils/WebData'; +import type {ApiDataLoginType} from '../utils/WebData'; import {apiRequest, ERROR_TYPE} from '../utils/WebData'; /** @@ -40,12 +40,10 @@ const AUTH_PATH = 'password'; export default class ConnectionManager { static instance: ConnectionManager | null = null; - #email: string; - - #token: string | null; + private token: string | null; constructor() { - this.#token = null; + this.token = null; } /** @@ -54,8 +52,9 @@ export default class ConnectionManager { * @returns {ConnectionManager} */ static getInstance(): ConnectionManager { - if (ConnectionManager.instance == null) + if (ConnectionManager.instance == null) { ConnectionManager.instance = new ConnectionManager(); + } return ConnectionManager.instance; } @@ -65,7 +64,7 @@ export default class ConnectionManager { * @returns {string | null} */ getToken(): string | null { - return this.#token; + return this.token; } /** @@ -77,18 +76,17 @@ export default class ConnectionManager { return new Promise( (resolve: (token: string) => void, reject: () => void) => { const token = this.getToken(); - if (token != null) resolve(token); - else { + if (token != null) { + resolve(token); + } else { Keychain.getInternetCredentials(SERVER_NAME) .then((data: Keychain.UserCredentials | false) => { - if ( - data != null && - data.password != null && - typeof data.password === 'string' - ) { - this.#token = data.password; - resolve(this.#token); - } else reject(); + if (data && data.password != null) { + this.token = data.password; + resolve(this.token); + } else { + reject(); + } }) .catch((): void => reject()); } @@ -116,8 +114,7 @@ export default class ConnectionManager { return new Promise((resolve: () => void, reject: () => void) => { Keychain.setInternetCredentials(SERVER_NAME, 'token', token) .then(() => { - this.#token = token; - this.#email = email; + this.token = token; resolve(); }) .catch((): void => reject()); @@ -133,7 +130,7 @@ export default class ConnectionManager { return new Promise((resolve: () => void, reject: () => void) => { Keychain.resetInternetCredentials(SERVER_NAME) .then(() => { - this.#token = null; + this.token = null; resolve(); }) .catch((): void => reject()); @@ -156,13 +153,15 @@ export default class ConnectionManager { email, password, }; - apiRequest(AUTH_PATH, 'POST', data) + apiRequest(AUTH_PATH, 'POST', data) .then((response: ApiDataLoginType) => { if (response.token != null) { this.saveLogin(email, response.token) .then((): void => resolve()) .catch((): void => reject(ERROR_TYPE.TOKEN_SAVE)); - } else reject(ERROR_TYPE.SERVER_ERROR); + } else { + reject(ERROR_TYPE.SERVER_ERROR); + } }) .catch((error: number): void => reject(error)); }, @@ -176,24 +175,23 @@ export default class ConnectionManager { * @param params * @returns Promise */ - async authenticatedRequest( + async authenticatedRequest( path: string, - params: {...}, - ): Promise { + params: {[key: string]: any}, + ): Promise { return new Promise( - ( - resolve: (response: ApiGenericDataType) => void, - reject: (error: number) => void, - ) => { + (resolve: (response: T) => void, reject: (error: number) => void) => { if (this.getToken() !== null) { const data = { ...params, token: this.getToken(), }; - apiRequest(path, 'POST', data) - .then((response: ApiGenericDataType): void => resolve(response)) + apiRequest(path, 'POST', data) + .then((response: T): void => resolve(response)) .catch((error: number): void => reject(error)); - } else reject(ERROR_TYPE.TOKEN_RETRIEVE); + } else { + reject(ERROR_TYPE.TOKEN_RETRIEVE); + } }, ); } diff --git a/src/managers/DashboardManager.js b/src/managers/DashboardManager.ts similarity index 91% rename from src/managers/DashboardManager.js rename to src/managers/DashboardManager.ts index a813d56..12035a6 100644 --- a/src/managers/DashboardManager.js +++ b/src/managers/DashboardManager.ts @@ -21,12 +21,12 @@ import type {ServiceItemType} from './ServicesManager'; import ServicesManager from './ServicesManager'; -import {getSublistWithIds} from '../utils/Utils'; +import {getSublistWithIds} from '../utils/Services'; import AsyncStorageManager from './AsyncStorageManager'; export default class DashboardManager extends ServicesManager { getCurrentDashboard(): Array { - const dashboardIdList = AsyncStorageManager.getObject( + const dashboardIdList = AsyncStorageManager.getObject>( AsyncStorageManager.PREFERENCES.dashboardItems.key, ); const allDatasets = [ diff --git a/src/managers/DateManager.js b/src/managers/DateManager.ts similarity index 95% rename from src/managers/DateManager.js rename to src/managers/DateManager.ts index ec2ebd0..f699e68 100644 --- a/src/managers/DateManager.js +++ b/src/managers/DateManager.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - import i18n from 'i18n-js'; /** @@ -28,9 +26,9 @@ import i18n from 'i18n-js'; export default class DateManager { static instance: DateManager | null = null; - daysOfWeek = []; + daysOfWeek: Array = []; - monthsOfYear = []; + monthsOfYear: Array = []; constructor() { this.daysOfWeek.push(i18n.t('date.daysOfWeek.sunday')); // 0 represents sunday @@ -60,7 +58,9 @@ export default class DateManager { * @returns {DateManager} */ static getInstance(): DateManager { - if (DateManager.instance == null) DateManager.instance = new DateManager(); + if (DateManager.instance == null) { + DateManager.instance = new DateManager(); + } return DateManager.instance; } diff --git a/src/managers/ServicesManager.js b/src/managers/ServicesManager.ts similarity index 94% rename from src/managers/ServicesManager.js rename to src/managers/ServicesManager.ts index d3ee8f0..b33237d 100644 --- a/src/managers/ServicesManager.js +++ b/src/managers/ServicesManager.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - import i18n from 'i18n-js'; import {StackNavigationProp} from '@react-navigation/stack'; import AvailableWebsites from '../constants/AvailableWebsites'; @@ -93,24 +91,24 @@ export const SERVICES_CATEGORIES_KEY = { }; export type ServiceItemType = { - key: string, - title: string, - subtitle: string, - image: string, - onPress: () => void, - badgeFunction?: (dashboard: FullDashboardType) => number, + key: string; + title: string; + subtitle: string; + image: string; + onPress: () => void; + badgeFunction?: (dashboard: FullDashboardType) => number; }; export type ServiceCategoryType = { - key: string, - title: string, - subtitle: string, - image: string | number, - content: Array, + key: string; + title: string; + subtitle: string; + image: string | number; + content: Array; }; export default class ServicesManager { - navigation: StackNavigationProp; + navigation: StackNavigationProp; amicaleDataset: Array; @@ -122,7 +120,7 @@ export default class ServicesManager { categoriesDataset: Array; - constructor(nav: StackNavigationProp) { + constructor(nav: StackNavigationProp) { this.navigation = nav; this.amicaleDataset = [ { @@ -336,9 +334,11 @@ export default class ServicesManager { * @returns {null} */ onAmicaleServicePress(route: string) { - if (ConnectionManager.getInstance().isLoggedIn()) + if (ConnectionManager.getInstance().isLoggedIn()) { this.navigation.navigate(route); - else this.navigation.navigate('login', {nextScreen: route}); + } else { + this.navigation.navigate('login', {nextScreen: route}); + } } /** @@ -348,8 +348,9 @@ export default class ServicesManager { * @returns {Array} */ getAmicaleServices(excludedItems?: Array): Array { - if (excludedItems != null) + if (excludedItems != null) { return getStrippedServicesList(excludedItems, this.amicaleDataset); + } return this.amicaleDataset; } @@ -360,8 +361,9 @@ export default class ServicesManager { * @returns {Array} */ getStudentServices(excludedItems?: Array): Array { - if (excludedItems != null) + if (excludedItems != null) { return getStrippedServicesList(excludedItems, this.studentsDataset); + } return this.studentsDataset; } @@ -372,8 +374,9 @@ export default class ServicesManager { * @returns {Array} */ getINSAServices(excludedItems?: Array): Array { - if (excludedItems != null) + if (excludedItems != null) { return getStrippedServicesList(excludedItems, this.insaDataset); + } return this.insaDataset; } @@ -384,8 +387,9 @@ export default class ServicesManager { * @returns {Array} */ getSpecialServices(excludedItems?: Array): Array { - if (excludedItems != null) + if (excludedItems != null) { return getStrippedServicesList(excludedItems, this.specialDataset); + } return this.specialDataset; } @@ -396,8 +400,9 @@ export default class ServicesManager { * @returns {Array} */ getCategories(excludedItems?: Array): Array { - if (excludedItems != null) + if (excludedItems != null) { return getStrippedServicesList(excludedItems, this.categoriesDataset); + } return this.categoriesDataset; } } diff --git a/src/utils/AutoHideHandler.js b/src/utils/AutoHideHandler.ts similarity index 89% rename from src/utils/AutoHideHandler.js rename to src/utils/AutoHideHandler.ts index 69d7eb8..c3e7662 100644 --- a/src/utils/AutoHideHandler.js +++ b/src/utils/AutoHideHandler.ts @@ -17,22 +17,12 @@ * along with Campus INSAT. If not, see . */ -// @flow +import {NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; const speedOffset = 5; type ListenerFunctionType = (shouldHide: boolean) => void; -export type OnScrollType = { - nativeEvent: { - contentInset: {bottom: number, left: number, right: number, top: number}, - contentOffset: {x: number, y: number}, - contentSize: {height: number, width: number}, - layoutMeasurement: {height: number, width: number}, - zoomScale: number, - }, -}; - /** * Class used to detect when to show or hide a component based on scrolling */ @@ -46,6 +36,7 @@ export default class AutoHideHandler { constructor(startHidden: boolean) { this.listeners = []; this.isHidden = startHidden; + this.lastOffset = 0; } /** @@ -84,7 +75,7 @@ export default class AutoHideHandler { * * @param event The scroll event generated by the animated component onScroll prop */ - onScroll(event: OnScrollType) { + onScroll(event: NativeSyntheticEvent) { const {nativeEvent} = event; const speed = nativeEvent.contentOffset.y < 0 diff --git a/src/utils/CollapsibleUtils.js b/src/utils/CollapsibleUtils.tsx similarity index 79% rename from src/utils/CollapsibleUtils.js rename to src/utils/CollapsibleUtils.tsx index 0f9f6aa..921cb12 100644 --- a/src/utils/CollapsibleUtils.js +++ b/src/utils/CollapsibleUtils.tsx @@ -17,12 +17,20 @@ * along with Campus INSAT. If not, see . */ -// @flow - import * as React from 'react'; import {useTheme} from 'react-native-paper'; import {createCollapsibleStack} from 'react-navigation-collapsible'; import StackNavigator, {StackNavigationOptions} from '@react-navigation/stack'; +import {StackNavigationState} from '@react-navigation/native'; +import {StackNavigationEventMap} from '@react-navigation/stack/lib/typescript/src/types'; + +type StackNavigatorType = import('@react-navigation/native').TypedNavigator< + Record, + StackNavigationState, + StackNavigationOptions, + StackNavigationEventMap, + typeof StackNavigator +>; /** * Creates a navigation stack with the collapsible library, allowing the header to collapse on scroll. @@ -40,18 +48,16 @@ import StackNavigator, {StackNavigationOptions} from '@react-navigation/stack'; * @param headerColor The color of the header. Uses default color if not specified * @returns {JSX.Element} */ -export function createScreenCollapsibleStack( +export function CreateScreenCollapsibleStack( name: string, - Stack: StackNavigator, - // eslint-disable-next-line flowtype/no-weak-types + Stack: StackNavigatorType, component: React.ComponentType, title: string, - useNativeDriver?: boolean, - options?: StackNavigationOptions, + useNativeDriver: boolean = true, + options: StackNavigationOptions = {}, headerColor?: string, -): React.Node { +) { const {colors} = useTheme(); - const screenOptions = options != null ? options : {}; return createCollapsibleStack( , { collapsedColor: headerColor != null ? headerColor : colors.surface, - useNativeDriver: useNativeDriver != null ? useNativeDriver : true, // native driver does not work with webview + useNativeDriver: useNativeDriver, // native driver does not work with webview }, ); } @@ -85,10 +91,9 @@ export function createScreenCollapsibleStack( */ export function getWebsiteStack( name: string, - Stack: StackNavigator, - // eslint-disable-next-line flowtype/no-weak-types + Stack: StackNavigatorType, component: React.ComponentType, title: string, -): React.Node { - return createScreenCollapsibleStack(name, Stack, component, title, false); +) { + return CreateScreenCollapsibleStack(name, Stack, component, title, false); } diff --git a/src/utils/EquipmentBooking.js b/src/utils/EquipmentBooking.ts similarity index 86% rename from src/utils/EquipmentBooking.js rename to src/utils/EquipmentBooking.ts index 05b96cf..e5e1d0f 100644 --- a/src/utils/EquipmentBooking.js +++ b/src/utils/EquipmentBooking.ts @@ -17,12 +17,9 @@ * along with Campus INSAT. If not, see . */ -// @flow - import i18n from 'i18n-js'; import type {DeviceType} from '../screens/Amicale/Equipment/EquipmentListScreen'; import DateManager from '../managers/DateManager'; -import type {CustomThemeType} from '../managers/ThemeManager'; import type {MarkedDatesObjectType} from '../screens/Amicale/Equipment/EquipmentRentScreen'; /** @@ -56,10 +53,12 @@ export function isEquipmentAvailable(item: DeviceType): boolean { let isAvailable = true; const today = getCurrentDay(); const dates = item.booked_at; - dates.forEach((date: {begin: string, end: string}) => { + dates.forEach((date: {begin: string; end: string}) => { const start = new Date(date.begin); const end = new Date(date.end); - if (!(today < start || today > end)) isAvailable = false; + if (!(today < start || today > end)) { + isAvailable = false; + } }); return isAvailable; } @@ -73,11 +72,13 @@ export function isEquipmentAvailable(item: DeviceType): boolean { export function getFirstEquipmentAvailability(item: DeviceType): Date { let firstAvailability = getCurrentDay(); const dates = item.booked_at; - dates.forEach((date: {begin: string, end: string}) => { + dates.forEach((date: {begin: string; end: string}) => { const start = new Date(date.begin); const end = new Date(date.end); end.setDate(end.getDate() + 1); - if (firstAvailability >= start) firstAvailability = end; + if (firstAvailability >= start) { + firstAvailability = end; + } }); return firstAvailability; } @@ -93,23 +94,24 @@ export function getRelativeDateString(date: Date): string { const monthDelta = date.getUTCMonth() - today.getUTCMonth(); const dayDelta = date.getUTCDate() - today.getUTCDate(); let translatedString = i18n.t('screens.equipment.today'); - if (yearDelta > 0) + if (yearDelta > 0) { translatedString = i18n.t('screens.equipment.otherYear', { date: date.getDate(), month: DateManager.getInstance().getMonthsOfYear()[date.getMonth()], year: date.getFullYear(), }); - else if (monthDelta > 0) + } else if (monthDelta > 0) { translatedString = i18n.t('screens.equipment.otherMonth', { date: date.getDate(), month: DateManager.getInstance().getMonthsOfYear()[date.getMonth()], }); - else if (dayDelta > 1) + } else if (dayDelta > 1) { translatedString = i18n.t('screens.equipment.thisMonth', { date: date.getDate(), }); - else if (dayDelta === 1) + } else if (dayDelta === 1) { translatedString = i18n.t('screens.equipment.tomorrow'); + } return translatedString; } @@ -162,8 +164,11 @@ export function getValidRange( (direction === 1 && date < limit) || (direction === -1 && date > limit) ) { - if (direction === 1) validRange.push(getISODate(date)); - else validRange.unshift(getISODate(date)); + if (direction === 1) { + validRange.push(getISODate(date)); + } else { + validRange.unshift(getISODate(date)); + } date.setDate(date.getDate() + direction); } return validRange; @@ -180,17 +185,27 @@ export function getValidRange( */ export function generateMarkedDates( isSelection: boolean, - theme: CustomThemeType, + theme: ReactNativePaper.Theme, range: Array, ): MarkedDatesObjectType { - const markedDates = {}; + const markedDates: { + [key: string]: { + startingDay: boolean; + endingDay: boolean; + color: string; + }; + } = {}; for (let i = 0; i < range.length; i += 1) { const isStart = i === 0; const isEnd = i === range.length - 1; let color; - if (isSelection && (isStart || isEnd)) color = theme.colors.primary; - else if (isSelection) color = theme.colors.danger; - else color = theme.colors.textDisabled; + if (isSelection && (isStart || isEnd)) { + color = theme.colors.primary; + } else if (isSelection) { + color = theme.colors.danger; + } else { + color = theme.colors.textDisabled; + } markedDates[range[i]] = { startingDay: isStart, endingDay: isEnd, diff --git a/src/utils/Home.js b/src/utils/Home.ts similarity index 81% rename from src/utils/Home.js rename to src/utils/Home.ts index 7b9764d..efebafd 100644 --- a/src/utils/Home.js +++ b/src/utils/Home.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - import {stringToDate} from './Planning'; import type {PlanningEventType} from './Planning'; @@ -29,13 +27,15 @@ import type {PlanningEventType} from './Planning'; */ export function getTodayEventTimeLimit(): Date { const now = new Date(); - if (now.getDay() === 4) + if (now.getDay() === 4) { // Thursday now.setHours(11, 30, 0); - else if (now.getDay() === 6 || now.getDay() === 0) + } else if (now.getDay() === 6 || now.getDay() === 0) { // Weekend now.setHours(0, 0, 0); - else now.setHours(17, 30, 0); + } else { + now.setHours(17, 30, 0); + } return now; } @@ -50,7 +50,7 @@ export function getEventsAfterLimit( events: Array, limit: Date, ): Array { - const validEvents = []; + const validEvents: Array = []; events.forEach((event: PlanningEventType) => { const startDate = stringToDate(event.date_begin); if (startDate != null && startDate >= limit) { @@ -68,11 +68,13 @@ export function getEventsAfterLimit( export function getFutureEvents( events: Array, ): Array { - const validEvents = []; + const validEvents: Array = []; const now = new Date(); events.forEach((event: PlanningEventType) => { const startDate = stringToDate(event.date_begin); - if (startDate != null && startDate > now) validEvents.push(event); + if (startDate != null && startDate > now) { + validEvents.push(event); + } }); return validEvents; } @@ -92,8 +94,13 @@ export function getDisplayEvent( events, getTodayEventTimeLimit(), ); - if (eventsAfterLimit.length > 0) [displayEvent] = eventsAfterLimit; - else [displayEvent] = events; - } else if (events.length === 1) [displayEvent] = events; + if (eventsAfterLimit.length > 0) { + [displayEvent] = eventsAfterLimit; + } else { + [displayEvent] = events; + } + } else if (events.length === 1) { + [displayEvent] = events; + } return displayEvent; } diff --git a/src/utils/Locales.js b/src/utils/Locales.ts similarity index 87% rename from src/utils/Locales.js rename to src/utils/Locales.ts index b44df46..3f6f144 100644 --- a/src/utils/Locales.js +++ b/src/utils/Locales.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - import i18n from 'i18n-js'; import * as RNLocalize from 'react-native-localize'; @@ -28,9 +26,7 @@ import fr from '../../locales/fr.json'; const initLocales = () => { i18n.fallbacks = true; i18n.translations = {fr, en}; - i18n.locale = RNLocalize.findBestAvailableLanguage([ - 'en', - 'fr', - ]).languageTag; -} + const bestLanguage = RNLocalize.findBestAvailableLanguage(['en', 'fr']); + i18n.locale = bestLanguage ? bestLanguage.languageTag : 'en'; +}; export default initLocales; diff --git a/src/utils/Notifications.js b/src/utils/Notifications.ts similarity index 92% rename from src/utils/Notifications.js rename to src/utils/Notifications.ts index 7fc279c..1bb8acd 100644 --- a/src/utils/Notifications.js +++ b/src/utils/Notifications.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - import { checkNotifications, requestNotifications, @@ -41,12 +39,17 @@ const reminderIdFactor = 100; export async function askPermissions(): Promise { return new Promise((resolve: () => void, reject: () => void) => { checkNotifications().then(({status}: {status: string}) => { - if (status === RESULTS.GRANTED) resolve(); - else if (status === RESULTS.BLOCKED) reject(); - else { - requestNotifications().then((result: {status: string}) => { - if (result.status === RESULTS.GRANTED) resolve(); - else reject(); + if (status === RESULTS.GRANTED) { + resolve(); + } else if (status === RESULTS.BLOCKED) { + reject(); + } else { + requestNotifications([]).then((result: {status: string}) => { + if (result.status === RESULTS.GRANTED) { + resolve(); + } else { + reject(); + } }); } }); diff --git a/src/utils/Planning.js b/src/utils/Planning.ts similarity index 87% rename from src/utils/Planning.js rename to src/utils/Planning.ts index aa54be0..81a19f7 100644 --- a/src/utils/Planning.js +++ b/src/utils/Planning.ts @@ -17,18 +17,16 @@ * along with Campus INSAT. If not, see . */ -// @flow - export type PlanningEventType = { - id: number, - title: string, - date_begin: string, - club: string, - category_id: number, - description: string, - place: string, - url: string, - logo: string | null, + id: number; + title: string; + date_begin: string; + club: string; + category_id: number; + description: string; + place: string; + url: string; + logo: string | null; }; // Regex used to check date string validity @@ -41,7 +39,7 @@ const dateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/; * @param dateString The string to check * @return {boolean} */ -export function isEventDateStringFormatValid(dateString: ?string): boolean { +export function isEventDateStringFormatValid(dateString?: string): boolean { return ( dateString !== undefined && dateString !== null && @@ -57,7 +55,7 @@ export function isEventDateStringFormatValid(dateString: ?string): boolean { * @return {Date|null} The date object or null if the given string is invalid */ export function stringToDate(dateString: string): Date | null { - let date = new Date(); + let date: Date | null = new Date(); if (isEventDateStringFormatValid(dateString)) { const stringArray = dateString.split(' '); const dateArray = stringArray[0].split('-'); @@ -68,7 +66,9 @@ export function stringToDate(dateString: string): Date | null { parseInt(dateArray[2], 10), ); date.setHours(parseInt(timeArray[0], 10), parseInt(timeArray[1], 10), 0, 0); - } else date = null; + } else { + date = null; + } return date; } @@ -111,7 +111,9 @@ export function getCurrentDateString(): string { export function isEventBefore(event1Date: string, event2Date: string): boolean { const date1 = stringToDate(event1Date); const date2 = stringToDate(event2Date); - if (date1 !== null && date2 !== null) return date1 < date2; + if (date1 !== null && date2 !== null) { + return date1 < date2; + } return false; } @@ -123,7 +125,9 @@ export function isEventBefore(event1Date: string, event2Date: string): boolean { * @return {string|null} Date in format YYYY:MM:DD or null if given string is invalid */ export function getDateOnlyString(dateString: string): string | null { - if (isEventDateStringFormatValid(dateString)) return dateString.split(' ')[0]; + if (isEventDateStringFormatValid(dateString)) { + return dateString.split(' ')[0]; + } return null; } @@ -135,7 +139,9 @@ export function getDateOnlyString(dateString: string): string | null { * @return {string|null} Time in format HH:MM or null if given string is invalid */ export function getTimeOnlyString(dateString: string): string | null { - if (isEventDateStringFormatValid(dateString)) return dateString.split(' ')[1]; + if (isEventDateStringFormatValid(dateString)) { + return dateString.split(' ')[1]; + } return null; } @@ -148,7 +154,7 @@ export function getTimeOnlyString(dateString: string): string | null { * @param description The text to check * @return {boolean} */ -export function isDescriptionEmpty(description: ?string): boolean { +export function isDescriptionEmpty(description?: string): boolean { if (description !== undefined && description !== null) { return ( description @@ -177,10 +183,12 @@ export function generateEmptyCalendar( ): {[key: string]: Array} { const end = new Date(Date.now()); end.setMonth(end.getMonth() + numberOfMonths); - const daysOfYear = {}; + const daysOfYear: {[key: string]: Array} = {}; for (let d = new Date(Date.now()); d <= end; d.setDate(d.getDate() + 1)) { const dateString = getDateOnlyString(dateToString(new Date(d), false)); - if (dateString !== null) daysOfYear[dateString] = []; + if (dateString !== null) { + daysOfYear[dateString] = []; + } } return daysOfYear; } @@ -197,8 +205,9 @@ export function pushEventInOrder( eventArray: Array, event: PlanningEventType, ) { - if (eventArray.length === 0) eventArray.push(event); - else { + if (eventArray.length === 0) { + eventArray.push(event); + } else { for (let i = 0; i < eventArray.length; i += 1) { if (isEventBefore(event.date_begin, eventArray[i].date_begin)) { eventArray.splice(i, 0, event); @@ -231,7 +240,9 @@ export function generateEventAgenda( const dateString = getDateOnlyString(eventList[i].date_begin); if (dateString != null) { const eventArray = agendaItems[dateString]; - if (eventArray != null) pushEventInOrder(eventArray, eventList[i]); + if (eventArray != null) { + pushEventInOrder(eventArray, eventList[i]); + } } } return agendaItems; diff --git a/src/utils/Proxiwash.js b/src/utils/Proxiwash.ts similarity index 90% rename from src/utils/Proxiwash.js rename to src/utils/Proxiwash.ts index d3d204a..e2e202b 100644 --- a/src/utils/Proxiwash.js +++ b/src/utils/Proxiwash.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - import type {ProxiwashMachineType} from '../screens/Proxiwash/ProxiwashScreen'; /** @@ -32,16 +30,21 @@ import type {ProxiwashMachineType} from '../screens/Proxiwash/ProxiwashScreen'; */ export function getMachineEndDate(machine: ProxiwashMachineType): Date | null { const array = machine.endTime.split(':'); - let endDate = new Date(Date.now()); + let endDate: Date | null = new Date(Date.now()); endDate.setHours(parseInt(array[0], 10), parseInt(array[1], 10)); const limit = new Date(Date.now()); if (endDate < limit) { if (limit.getHours() > 12) { limit.setHours(limit.getHours() - 12); - if (endDate < limit) endDate.setDate(endDate.getDate() + 1); - else endDate = null; - } else endDate = null; + if (endDate < limit) { + endDate.setDate(endDate.getDate() + 1); + } else { + endDate = null; + } + } else { + endDate = null; + } } return endDate; @@ -63,8 +66,9 @@ export function isMachineWatched( if ( watchedMachine.number === machine.number && watchedMachine.endTime === machine.endTime - ) + ) { watched = true; + } }); return watched; } @@ -82,7 +86,9 @@ export function getMachineOfId( ): ProxiwashMachineType | null { let machineFound = null; allMachines.forEach((machine: ProxiwashMachineType) => { - if (machine.number === id) machineFound = machine; + if (machine.number === id) { + machineFound = machine; + } }); return machineFound; } @@ -100,7 +106,7 @@ export function getCleanedMachineWatched( machineWatchedList: Array, allMachines: Array, ): Array { - const newList = []; + const newList: Array = []; machineWatchedList.forEach((watchedMachine: ProxiwashMachineType) => { const machine = getMachineOfId(watchedMachine.number, allMachines); if ( diff --git a/src/utils/Search.js b/src/utils/Search.ts similarity index 95% rename from src/utils/Search.js rename to src/utils/Search.ts index addfa0f..2b2c67f 100644 --- a/src/utils/Search.js +++ b/src/utils/Search.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - /** * Sanitizes the given string to improve search performance. * @@ -60,7 +58,9 @@ export function isItemInCategoryFilter( ): boolean { let itemFound = false; categories.forEach((cat: number | null) => { - if (cat != null && filter.indexOf(cat) !== -1) itemFound = true; + if (cat != null && filter.indexOf(cat) !== -1) { + itemFound = true; + } }); return itemFound; } diff --git a/src/utils/Utils.js b/src/utils/Services.ts similarity index 64% rename from src/utils/Utils.js rename to src/utils/Services.ts index bf35582..9d82611 100644 --- a/src/utils/Utils.js +++ b/src/utils/Services.ts @@ -17,10 +17,25 @@ * along with Campus INSAT. If not, see . */ -// @flow - -import {Platform, StatusBar} from 'react-native'; -import ThemeManager from '../managers/ThemeManager'; +/** + * Gets the given services list without items of the given ids + * + * @param idList The ids of items to remove + * @param sourceList The item list to use as source + * @returns {[]} + */ +export default function getStrippedServicesList( + idList: Array, + sourceList: Array, +) { + const newArray: Array = []; + sourceList.forEach((item: T) => { + if (!idList.includes(item.key)) { + newArray.push(item); + } + }); + return newArray; +} /** * Gets a sublist of the given list with items of the given ids only @@ -31,11 +46,11 @@ import ThemeManager from '../managers/ThemeManager'; * @param originalList The original list * @returns {[]} */ -export function getSublistWithIds( +export function getSublistWithIds( idList: Array, - originalList: Array<{key: string, ...T}>, -): Array<{key: string, ...T} | null> { - const subList = []; + originalList: Array, +) { + const subList: Array = []; for (let i = 0; i < idList.length; i += 1) { subList.push(null); } @@ -45,26 +60,10 @@ export function getSublistWithIds( if (idList.includes(item.key)) { subList[idList.indexOf(item.key)] = item; itemsAdded += 1; - if (itemsAdded === idList.length) break; + if (itemsAdded === idList.length) { + break; + } } } 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, - ); - } -} diff --git a/src/utils/URLHandler.js b/src/utils/URLHandler.ts similarity index 89% rename from src/utils/URLHandler.js rename to src/utils/URLHandler.ts index 8c89dbf..15764e5 100644 --- a/src/utils/URLHandler.js +++ b/src/utils/URLHandler.ts @@ -17,20 +17,18 @@ * along with Campus INSAT. If not, see . */ -// @flow - import {Linking} from 'react-native'; export type ParsedUrlDataType = { - route: string, - data: {[key: string]: string}, + route: string; + data: {[key: string]: string}; }; export type ParsedUrlCallbackType = (parsedData: ParsedUrlDataType) => void; type RawParsedUrlDataType = { - path: string, - queryParams: {[key: string]: string}, + path: string; + queryParams: {[key: string]: string}; }; /** @@ -69,7 +67,7 @@ export default class URLHandler { let parsedData: RawParsedUrlDataType | null = null; const urlNoScheme = url.replace(URLHandler.SCHEME, ''); if (urlNoScheme != null) { - const params = {}; + const params: {[key: string]: string} = {}; const [path, fullParamsString] = urlNoScheme.split('?'); if (fullParamsString != null) { const paramsStringArray = fullParamsString.split('&'); @@ -80,7 +78,9 @@ export default class URLHandler { } }); } - if (path != null) parsedData = {path, queryParams: params}; + if (path != null) { + parsedData = {path, queryParams: params}; + } } return parsedData; } @@ -99,10 +99,11 @@ export default class URLHandler { if (rawParsedUrlData != null) { const {path} = rawParsedUrlData; const {queryParams} = rawParsedUrlData; - if (URLHandler.isClubInformationLink(path)) + if (URLHandler.isClubInformationLink(path)) { parsedData = URLHandler.generateClubInformationData(queryParams); - else if (URLHandler.isPlanningInformationLink(path)) + } else if (URLHandler.isPlanningInformationLink(path)) { parsedData = URLHandler.generatePlanningInformationData(queryParams); + } } return parsedData; @@ -145,15 +146,16 @@ export default class URLHandler { * @returns {null|{route: string, data: {clubId: number}}} */ static generateClubInformationData(params: { - [key: string]: string, + [key: string]: string; }): ParsedUrlDataType | null { if (params.id != null) { const id = parseInt(params.id, 10); - if (!Number.isNaN(id)) + if (!Number.isNaN(id)) { return { route: URLHandler.CLUB_INFO_ROUTE, data: {clubId: id.toString()}, }; + } } return null; } @@ -165,7 +167,7 @@ export default class URLHandler { * @returns {null|{route: string, data: {clubId: number}}} */ static generatePlanningInformationData(params: { - [key: string]: string, + [key: string]: string; }): ParsedUrlDataType | null { if (params.id != null) { const id = parseInt(params.id, 10); @@ -201,7 +203,9 @@ export default class URLHandler { onUrl = ({url}: {url: string}) => { if (url != null) { const data = URLHandler.getUrlData(URLHandler.parseUrl(url)); - if (data != null) this.onDetectURL(data); + if (data != null) { + this.onDetectURL(data); + } } }; @@ -210,10 +214,12 @@ export default class URLHandler { * * @param url The url detected */ - onInitialUrl = (url: ?string) => { + onInitialUrl = (url: string | null) => { if (url != null) { const data = URLHandler.getUrlData(URLHandler.parseUrl(url)); - if (data != null) this.onInitialURLParsed(data); + if (data != null) { + this.onInitialURLParsed(data); + } } }; } diff --git a/src/utils/Services.js b/src/utils/Utils.ts similarity index 58% rename from src/utils/Services.js rename to src/utils/Utils.ts index 0d174a9..b0c7c13 100644 --- a/src/utils/Services.js +++ b/src/utils/Utils.ts @@ -17,22 +17,23 @@ * along with Campus INSAT. If not, see . */ -// @flow +import {Platform, StatusBar} from 'react-native'; +import ThemeManager from '../managers/ThemeManager'; /** - * Gets the given services list without items of the given ids - * - * @param idList The ids of items to remove - * @param sourceList The item list to use as source - * @returns {[]} + * Updates status bar content color if on iOS only, + * as the android status bar is always set to black. */ -export default function getStrippedServicesList( - idList: Array, - sourceList: Array<{key: string, ...T}>, -): Array<{key: string, ...T}> { - const newArray = []; - sourceList.forEach((item: {key: string, ...T}) => { - if (!idList.includes(item.key)) newArray.push(item); - }); - return newArray; +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, + ); + } } diff --git a/src/utils/WebData.js b/src/utils/WebData.ts similarity index 73% rename from src/utils/WebData.js rename to src/utils/WebData.ts index dba16d6..44e68d7 100644 --- a/src/utils/WebData.js +++ b/src/utils/WebData.ts @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - export const ERROR_TYPE = { SUCCESS: 0, BAD_CREDENTIALS: 1, @@ -34,15 +32,14 @@ export const ERROR_TYPE = { }; export type ApiDataLoginType = { - token: string, + token: string; }; -// eslint-disable-next-line flowtype/no-weak-types export type ApiGenericDataType = {[key: string]: any}; -type ApiResponseType = { - error: number, - data: ApiGenericDataType, +type ApiResponseType = { + error: number; + data: T; }; const API_ENDPOINT = 'https://www.amicale-insat.fr/api/'; @@ -55,11 +52,10 @@ const API_ENDPOINT = 'https://www.amicale-insat.fr/api/'; * @param response * @returns {boolean} */ -export function isApiResponseValid(response: ApiResponseType): boolean { +export function isApiResponseValid(response: ApiResponseType): boolean { return ( response != null && response.error != null && - typeof response.error === 'number' && response.data != null && typeof response.data === 'object' ); @@ -76,18 +72,17 @@ export function isApiResponseValid(response: ApiResponseType): boolean { * @param params The params to use for this request * @returns {Promise} */ -export async function apiRequest( +export async function apiRequest( path: string, method: string, - params?: {...}, -): Promise { + params?: object, +): Promise { return new Promise( - ( - resolve: (data: ApiGenericDataType) => void, - reject: (error: number) => void, - ) => { + (resolve: (data: T) => void, reject: (error: number) => void) => { let requestParams = {}; - if (params != null) requestParams = {...params}; + if (params != null) { + requestParams = {...params}; + } fetch(API_ENDPOINT + path, { method, headers: new Headers({ @@ -96,14 +91,20 @@ export async function apiRequest( }), body: JSON.stringify(requestParams), }) - .then(async (response: Response): Promise => - response.json(), + .then( + async (response: Response): Promise> => + response.json(), ) - .then((response: ApiResponseType) => { + .then((response: ApiResponseType) => { if (isApiResponseValid(response)) { - if (response.error === ERROR_TYPE.SUCCESS) resolve(response.data); - else reject(response.error); - } else reject(ERROR_TYPE.SERVER_ERROR); + if (response.error === ERROR_TYPE.SUCCESS) { + resolve(response.data); + } else { + reject(response.error); + } + } else { + reject(ERROR_TYPE.SERVER_ERROR); + } }) .catch((): void => reject(ERROR_TYPE.CONNECTION_ERROR)); }, @@ -121,14 +122,10 @@ export async function apiRequest( * @param url The urls to fetch data from * @return Promise */ -// eslint-disable-next-line flowtype/no-weak-types export async function readData(url: string): Promise { - // eslint-disable-next-line flowtype/no-weak-types return new Promise((resolve: (response: any) => void, reject: () => void) => { fetch(url) - // eslint-disable-next-line flowtype/no-weak-types .then(async (response: Response): Promise => response.json()) - // eslint-disable-next-line flowtype/no-weak-types .then((data: any): void => resolve(data)) .catch((): void => reject()); }); diff --git a/src/utils/withCollapsible.js b/src/utils/withCollapsible.tsx similarity index 76% rename from src/utils/withCollapsible.js rename to src/utils/withCollapsible.tsx index 0c66383..4d537cd 100644 --- a/src/utils/withCollapsible.js +++ b/src/utils/withCollapsible.tsx @@ -17,8 +17,6 @@ * along with Campus INSAT. If not, see . */ -// @flow - import * as React from 'react'; import {useCollapsibleStack} from 'react-navigation-collapsible'; @@ -35,18 +33,12 @@ import {useCollapsibleStack} from 'react-navigation-collapsible'; * @param Component The component to use Collapsible with * @returns {React.ComponentType} */ -export default function withCollapsible( - // eslint-disable-next-line flowtype/no-weak-types - Component: React.ComponentType, - // eslint-disable-next-line flowtype/no-weak-types -): React$AbstractComponent { - // eslint-disable-next-line flowtype/no-weak-types - return React.forwardRef((props: any, ref: any): React.Node => { +export default function withCollapsible(Component: React.ComponentType) { + return React.forwardRef((props: any, ref: any) => { return ( );