From 4db4516296f6c217f7c821c5a1608846c0bd812e Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Tue, 4 Aug 2020 21:24:43 +0200 Subject: [PATCH] Improve override components to match linter --- src/components/Overrides/CustomAgenda.js | 105 +-- src/components/Overrides/CustomHTML.js | 79 +- .../Overrides/CustomHeaderButton.js | 46 +- src/components/Overrides/CustomIntroSlider.js | 752 +++++++++--------- src/components/Overrides/CustomModal.js | 49 +- src/components/Overrides/CustomSlider.js | 85 +- 6 files changed, 573 insertions(+), 543 deletions(-) diff --git a/src/components/Overrides/CustomAgenda.js b/src/components/Overrides/CustomAgenda.js index b490fc7..eaf4f20 100644 --- a/src/components/Overrides/CustomAgenda.js +++ b/src/components/Overrides/CustomAgenda.js @@ -1,60 +1,63 @@ -import * as React from 'react'; -import {View} from "react-native"; -import {withTheme} from 'react-native-paper'; -import {Agenda} from "react-native-calendars"; +// @flow -type Props = { - theme: Object, -} +import * as React from 'react'; +import {View} from 'react-native'; +import {withTheme} from 'react-native-paper'; +import {Agenda} from 'react-native-calendars'; +import type {CustomTheme} from '../../managers/ThemeManager'; + +type PropsType = { + theme: CustomTheme, + onRef: (ref: Agenda) => void, +}; /** * Abstraction layer for Agenda component, using custom configuration */ -class CustomAgenda extends React.Component { +class CustomAgenda extends React.Component { + getAgenda(): React.Node { + const {props} = this; + return ( + + ); + } - getAgenda() { - return ; - } - - render() { - // Completely recreate the component on theme change to force theme reload - if (this.props.theme.dark) - return ( - - {this.getAgenda()} - - ); - else - return this.getAgenda(); - } + render(): React.Node { + const {props} = this; + // Completely recreate the component on theme change to force theme reload + if (props.theme.dark) + return {this.getAgenda()}; + return this.getAgenda(); + } } export default withTheme(CustomAgenda); diff --git a/src/components/Overrides/CustomHTML.js b/src/components/Overrides/CustomHTML.js index 4e28948..d3f2d93 100644 --- a/src/components/Overrides/CustomHTML.js +++ b/src/components/Overrides/CustomHTML.js @@ -1,47 +1,58 @@ +/* eslint-disable flowtype/require-parameter-type */ +// @flow + import * as React from 'react'; import {Text, withTheme} from 'react-native-paper'; -import HTML from "react-native-render-html"; -import {Linking} from "react-native"; +import HTML from 'react-native-render-html'; +import {Linking} from 'react-native'; +import type {CustomTheme} from '../../managers/ThemeManager'; -type Props = { - theme: Object, - html: string, -} +type PropsType = { + theme: CustomTheme, + html: string, +}; /** * Abstraction layer for Agenda component, using custom configuration */ -class CustomHTML extends React.Component { +class CustomHTML extends React.Component { + openWebLink = (event: {...}, link: string) => { + Linking.openURL(link); + }; - openWebLink = (event, link) => { - Linking.openURL(link).catch((err) => console.error('Error opening link', err)); - }; + getBasicText = ( + htmlAttribs, + children, + convertedCSSStyles, + passProps, + ): React.Node => { + // eslint-disable-next-line react/jsx-props-no-spreading + return {children}; + }; - getBasicText = (htmlAttribs, children, convertedCSSStyles, passProps) => { - return {children}; - }; + getListBullet = (): React.Node => { + return - ; + }; - getListBullet = (htmlAttribs, children, convertedCSSStyles, passProps) => { - return ( - - - ); - }; - - render() { - // Surround description with p to allow text styling if the description is not html - return " + this.props.html + "

"} - renderers={{ - p: this.getBasicText, - li: this.getBasicText, - }} - listsPrefixesRenderers={{ - ul: this.getListBullet - }} - ignoredTags={['img']} - ignoredStyles={['color', 'background-color']} - onLinkPress={this.openWebLink}/>; - } + render(): React.Node { + const {props} = this; + // Surround description with p to allow text styling if the description is not html + return ( + ${props.html}

`} + renderers={{ + p: this.getBasicText, + li: this.getBasicText, + }} + listsPrefixesRenderers={{ + ul: this.getListBullet, + }} + ignoredTags={['img']} + ignoredStyles={['color', 'background-color']} + onLinkPress={this.openWebLink} + /> + ); + } } export default withTheme(CustomHTML); diff --git a/src/components/Overrides/CustomHeaderButton.js b/src/components/Overrides/CustomHeaderButton.js index add0138..47c0c1f 100644 --- a/src/components/Overrides/CustomHeaderButton.js +++ b/src/components/Overrides/CustomHeaderButton.js @@ -1,27 +1,39 @@ // @flow import * as React from 'react'; -import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons"; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import {HeaderButton, HeaderButtons} from 'react-navigation-header-buttons'; -import {withTheme} from "react-native-paper"; +import {withTheme} from 'react-native-paper'; +import type {CustomTheme} from '../../managers/ThemeManager'; -const MaterialHeaderButton = (props: Object) => +const MaterialHeaderButton = (props: { + theme: CustomTheme, + color: string, +}): React.Node => { + const {color, theme} = props; + return ( + // $FlowFixMe ; - -const MaterialHeaderButtons = (props: Object) => { - return ( - - ); + // eslint-disable-next-line react/jsx-props-no-spreading + {...props} + IconComponent={MaterialCommunityIcons} + iconSize={26} + color={color != null ? color : theme.colors.text} + /> + ); }; -export default withTheme(MaterialHeaderButtons); +const MaterialHeaderButtons = (props: {...}): React.Node => { + return ( + // $FlowFixMe + + ); +}; + +export default MaterialHeaderButtons; export {Item} from 'react-navigation-header-buttons'; diff --git a/src/components/Overrides/CustomIntroSlider.js b/src/components/Overrides/CustomIntroSlider.js index 371fe12..131700b 100644 --- a/src/components/Overrides/CustomIntroSlider.js +++ b/src/components/Overrides/CustomIntroSlider.js @@ -1,411 +1,403 @@ // @flow import * as React from 'react'; -import {Platform, StatusBar, StyleSheet, View} from "react-native"; -import type {MaterialCommunityIconsGlyphs} from "react-native-vector-icons/MaterialCommunityIcons"; -import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons"; +import {Platform, StatusBar, StyleSheet, View} from 'react-native'; +import type {MaterialCommunityIconsGlyphs} from 'react-native-vector-icons/MaterialCommunityIcons'; +import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import i18n from 'i18n-js'; -import AppIntroSlider from "react-native-app-intro-slider"; -import Update from "../../constants/Update"; -import ThemeManager from "../../managers/ThemeManager"; +import AppIntroSlider from 'react-native-app-intro-slider'; import LinearGradient from 'react-native-linear-gradient'; -import Mascot, {MASCOT_STYLE} from "../Mascot/Mascot"; -import * as Animatable from "react-native-animatable"; -import {Card} from "react-native-paper"; +import * as Animatable from 'react-native-animatable'; +import {Card} from 'react-native-paper'; +import Update from '../../constants/Update'; +import ThemeManager from '../../managers/ThemeManager'; +import Mascot, {MASCOT_STYLE} from '../Mascot/Mascot'; -type Props = { - onDone: Function, - isUpdate: boolean, - isAprilFools: boolean, +type PropsType = { + onDone: () => void, + isUpdate: boolean, + isAprilFools: boolean, }; -type State = { - currentSlide: number, -} - -type Slide = { - key: string, - title: string, - text: string, - view: () => React.Node, - mascotStyle: number, - colors: [string, string] +type StateType = { + currentSlide: number, }; +type IntroSlideType = { + key: string, + title: string, + text: string, + view: () => React.Node, + mascotStyle: number, + colors: [string, string], +}; + +const styles = StyleSheet.create({ + mainContent: { + paddingBottom: 100, + }, + text: { + color: 'rgba(255, 255, 255, 0.8)', + backgroundColor: 'transparent', + textAlign: 'center', + paddingHorizontal: 16, + }, + title: { + fontSize: 22, + color: 'white', + backgroundColor: 'transparent', + textAlign: 'center', + marginBottom: 16, + }, + center: { + marginTop: 'auto', + marginBottom: 'auto', + marginRight: 'auto', + marginLeft: 'auto', + }, +}); + /** * Class used to create intro slides */ -export default class CustomIntroSlider extends React.Component { +export default class CustomIntroSlider extends React.Component< + PropsType, + StateType, +> { + sliderRef: {current: null | AppIntroSlider}; - state = { - currentSlide: 0, - } + introSlides: Array; - sliderRef: { current: null | AppIntroSlider }; + updateSlides: Array; - introSlides: Array; - updateSlides: Array; - aprilFoolsSlides: Array; - currentSlides: Array; + aprilFoolsSlides: Array; - /** - * Generates intro slides - */ - constructor() { - super(); - this.sliderRef = React.createRef(); - this.introSlides = [ - { - key: '0', // Mascot - title: i18n.t('intro.slideMain.title'), - text: i18n.t('intro.slideMain.text'), - view: this.getWelcomeView, - mascotStyle: MASCOT_STYLE.NORMAL, - colors: ['#be1522', '#57080e'], - }, - { - key: '1', - title: i18n.t('intro.slidePlanex.title'), - text: i18n.t('intro.slidePlanex.text'), - view: () => this.getIconView("calendar-clock"), - mascotStyle: MASCOT_STYLE.INTELLO, - colors: ['#be1522', '#57080e'], - }, - { - key: '2', - title: i18n.t('intro.slideEvents.title'), - text: i18n.t('intro.slideEvents.text'), - view: () => this.getIconView("calendar-star",), - mascotStyle: MASCOT_STYLE.HAPPY, - colors: ['#be1522', '#57080e'], - }, - { - key: '3', - title: i18n.t('intro.slideServices.title'), - text: i18n.t('intro.slideServices.text'), - view: () => this.getIconView("view-dashboard-variant",), - mascotStyle: MASCOT_STYLE.CUTE, - colors: ['#be1522', '#57080e'], - }, - { - key: '4', - title: i18n.t('intro.slideDone.title'), - text: i18n.t('intro.slideDone.text'), - view: () => this.getEndView(), - mascotStyle: MASCOT_STYLE.COOL, - colors: ['#9c165b', '#3e042b'], - }, - ]; - this.updateSlides = []; - for (let i = 0; i < Update.slidesNumber; i++) { - this.updateSlides.push( - { - key: i.toString(), - title: Update.getInstance().titleList[i], - text: Update.getInstance().descriptionList[i], - icon: Update.iconList[i], - colors: Update.colorsList[i], - }, - ); - } + currentSlides: Array; - this.aprilFoolsSlides = [ - { - key: '1', - title: i18n.t('intro.aprilFoolsSlide.title'), - text: i18n.t('intro.aprilFoolsSlide.text'), - view: () => , - mascotStyle: MASCOT_STYLE.NORMAL, - colors: ['#e01928', '#be1522'], - }, - ]; - } - - /** - * Render item to be used for the intro introSlides - * - * @param item The item to be displayed - * @param dimensions Dimensions of the item - */ - getIntroRenderItem = ({item, dimensions}: { item: Slide, dimensions: { width: number, height: number } }) => { - const index = parseInt(item.key); - return ( - - {this.state.currentSlide === index - ? - - {item.view()} - - - {index !== 0 && index !== this.introSlides.length - 1 - ? - : null} - - - - - {item.title} - - - {item.text} - - - - - : null} - - ); - } - - getEndView = () => { - return ( - - - - ); - } - - getWelcomeView = () => { - return ( - - - - PABLO - - - - - - ) - } - - getIconView(icon: MaterialCommunityIconsGlyphs) { - return ( - - - - - - ) - } - - setStatusBarColor(color: string) { - if (Platform.OS === 'android') - StatusBar.setBackgroundColor(color, true); - } - - onSlideChange = (index: number, lastIndex: number) => { - this.setStatusBarColor(this.currentSlides[index].colors[0]); - this.setState({currentSlide: index}); + /** + * Generates intro slides + */ + constructor() { + super(); + this.state = { + currentSlide: 0, }; - - onSkip = () => { - this.setStatusBarColor(this.currentSlides[this.currentSlides.length - 1].colors[0]); - if (this.sliderRef.current != null) - this.sliderRef.current.goToSlide(this.currentSlides.length - 1); + this.sliderRef = React.createRef(); + this.introSlides = [ + { + key: '0', // Mascot + title: i18n.t('intro.slideMain.title'), + text: i18n.t('intro.slideMain.text'), + view: this.getWelcomeView, + mascotStyle: MASCOT_STYLE.NORMAL, + colors: ['#be1522', '#57080e'], + }, + { + key: '1', + title: i18n.t('intro.slidePlanex.title'), + text: i18n.t('intro.slidePlanex.text'), + view: (): React.Node => CustomIntroSlider.getIconView('calendar-clock'), + mascotStyle: MASCOT_STYLE.INTELLO, + colors: ['#be1522', '#57080e'], + }, + { + key: '2', + title: i18n.t('intro.slideEvents.title'), + text: i18n.t('intro.slideEvents.text'), + view: (): React.Node => CustomIntroSlider.getIconView('calendar-star'), + mascotStyle: MASCOT_STYLE.HAPPY, + colors: ['#be1522', '#57080e'], + }, + { + key: '3', + title: i18n.t('intro.slideServices.title'), + text: i18n.t('intro.slideServices.text'), + view: (): React.Node => + CustomIntroSlider.getIconView('view-dashboard-variant'), + mascotStyle: MASCOT_STYLE.CUTE, + colors: ['#be1522', '#57080e'], + }, + { + key: '4', + title: i18n.t('intro.slideDone.title'), + text: i18n.t('intro.slideDone.text'), + view: (): React.Node => this.getEndView(), + mascotStyle: MASCOT_STYLE.COOL, + colors: ['#9c165b', '#3e042b'], + }, + ]; + // $FlowFixMe + this.updateSlides = []; + for (let i = 0; i < Update.slidesNumber; i += 1) { + this.updateSlides.push({ + key: i.toString(), + title: Update.getInstance().titleList[i], + text: Update.getInstance().descriptionList[i], + icon: Update.iconList[i], + colors: Update.colorsList[i], + }); } - onDone = () => { - this.setStatusBarColor(ThemeManager.getCurrentTheme().colors.surface); - this.props.onDone(); - } - - renderNextButton = () => { - return ( - , + mascotStyle: MASCOT_STYLE.NORMAL, + colors: ['#e01928', '#be1522'], + }, + ]; + } + /** + * Render item to be used for the intro introSlides + * + * @param item The item to be displayed + * @param dimensions Dimensions of the item + */ + getIntroRenderItem = ({ + item, + dimensions, + }: { + item: IntroSlideType, + dimensions: {width: number, height: number}, + }): React.Node => { + const {state} = this; + const index = parseInt(item.key, 10); + return ( + + {state.currentSlide === index ? ( + + {item.view()} + + {index !== 0 && index !== this.introSlides.length - 1 ? ( + + ) : null} + - - - ) - } - - renderDoneButton = () => { - return ( - + - + + + {item.title} + + + {item.text} + + + - ) - } + + ) : null} + + ); + }; - render() { - this.currentSlides = this.introSlides; - if (this.props.isUpdate) - this.currentSlides = this.updateSlides; - else if (this.props.isAprilFools) - this.currentSlides = this.aprilFoolsSlides; - this.setStatusBarColor(this.currentSlides[0].colors[0]); - return ( - { + return ( + + + + ); + }; - renderItem={this.getIntroRenderItem} - renderNextButton={this.renderNextButton} - renderDoneButton={this.renderDoneButton} + getWelcomeView = (): React.Node => { + return ( + + + + PABLO + + + + + + ); + }; - onDone={this.onDone} - onSlideChange={this.onSlideChange} - onSkip={this.onSkip} - /> - ); - } + static getIconView(icon: MaterialCommunityIconsGlyphs): React.Node { + return ( + + + + + + ); + } + static setStatusBarColor(color: string) { + if (Platform.OS === 'android') StatusBar.setBackgroundColor(color, true); + } + + onSlideChange = (index: number) => { + CustomIntroSlider.setStatusBarColor(this.currentSlides[index].colors[0]); + this.setState({currentSlide: index}); + }; + + onSkip = () => { + CustomIntroSlider.setStatusBarColor( + this.currentSlides[this.currentSlides.length - 1].colors[0], + ); + if (this.sliderRef.current != null) + this.sliderRef.current.goToSlide(this.currentSlides.length - 1); + }; + + onDone = () => { + const {props} = this; + CustomIntroSlider.setStatusBarColor( + ThemeManager.getCurrentTheme().colors.surface, + ); + props.onDone(); + }; + + getRenderNextButton = (): React.Node => { + return ( + + + + ); + }; + + getRenderDoneButton = (): React.Node => { + return ( + + + + ); + }; + + render(): React.Node { + const {props, state} = this; + this.currentSlides = this.introSlides; + if (props.isUpdate) this.currentSlides = this.updateSlides; + else if (props.isAprilFools) this.currentSlides = this.aprilFoolsSlides; + CustomIntroSlider.setStatusBarColor(this.currentSlides[0].colors[0]); + return ( + + ); + } } - - -const styles = StyleSheet.create({ - mainContent: { - paddingBottom: 100, - }, - text: { - color: 'rgba(255, 255, 255, 0.8)', - backgroundColor: 'transparent', - textAlign: 'center', - paddingHorizontal: 16, - }, - title: { - fontSize: 22, - color: 'white', - backgroundColor: 'transparent', - textAlign: 'center', - marginBottom: 16, - }, - center: { - marginTop: 'auto', - marginBottom: 'auto', - marginRight: 'auto', - marginLeft: 'auto', - }, -}); diff --git a/src/components/Overrides/CustomModal.js b/src/components/Overrides/CustomModal.js index 598acc4..b11868a 100644 --- a/src/components/Overrides/CustomModal.js +++ b/src/components/Overrides/CustomModal.js @@ -2,9 +2,10 @@ import * as React from 'react'; import {withTheme} from 'react-native-paper'; -import {Modalize} from "react-native-modalize"; -import {View} from "react-native-animatable"; -import CustomTabBar from "../Tabbar/CustomTabBar"; +import {Modalize} from 'react-native-modalize'; +import {View} from 'react-native-animatable'; +import CustomTabBar from '../Tabbar/CustomTabBar'; +import type {CustomTheme} from '../../managers/ThemeManager'; /** * Abstraction layer for Modalize component, using custom configuration @@ -12,25 +13,29 @@ import CustomTabBar from "../Tabbar/CustomTabBar"; * @param props Props to pass to the element. Must specify an onRef prop to get an Modalize ref. * @return {*} */ -function CustomModal(props) { - const {colors} = props.theme; - return ( - - - {props.children} - - - - ); +function CustomModal(props: { + theme: CustomTheme, + onRef: (re: Modalize) => void, + children?: React.Node, +}): React.Node { + const {theme, onRef, children} = props; + return ( + + + {children} + + + ); } -export default withTheme(CustomModal); +CustomModal.defaultProps = {children: null}; +export default withTheme(CustomModal); diff --git a/src/components/Overrides/CustomSlider.js b/src/components/Overrides/CustomSlider.js index 122ea4a..419ca02 100644 --- a/src/components/Overrides/CustomSlider.js +++ b/src/components/Overrides/CustomSlider.js @@ -2,19 +2,19 @@ import * as React from 'react'; import {Text, withTheme} from 'react-native-paper'; -import {View} from "react-native-animatable"; -import type {CustomTheme} from "../../managers/ThemeManager"; -import Slider, {SliderProps} from "@react-native-community/slider"; +import {View} from 'react-native-animatable'; +import Slider, {SliderProps} from '@react-native-community/slider'; +import type {CustomTheme} from '../../managers/ThemeManager'; -type Props = { - theme: CustomTheme, - valueSuffix: string, - ...SliderProps -} +type PropsType = { + theme: CustomTheme, + valueSuffix?: string, + ...SliderProps, +}; -type State = { - currentValue: number, -} +type StateType = { + currentValue: number, +}; /** * Abstraction layer for Modalize component, using custom configuration @@ -22,37 +22,44 @@ type State = { * @param props Props to pass to the element. Must specify an onRef prop to get an Modalize ref. * @return {*} */ -class CustomSlider extends React.Component { +class CustomSlider extends React.Component { + static defaultProps = { + valueSuffix: '', + }; - static defaultProps = { - valueSuffix: "", - } + constructor(props: PropsType) { + super(props); + this.state = { + currentValue: props.value, + }; + } - state = { - currentValue: this.props.value, - } - - onValueChange = (value: number) => { - this.setState({currentValue: value}); - if (this.props.onValueChange != null) - this.props.onValueChange(value); - } - - render() { - return ( - - - {this.state.currentValue}min - - - - ); - } + onValueChange = (value: number) => { + const {props} = this; + this.setState({currentValue: value}); + if (props.onValueChange != null) props.onValueChange(value); + }; + render(): React.Node { + const {props, state} = this; + return ( + + + {state.currentValue}min + + + + ); + } } export default withTheme(CustomSlider); -