From 6dbce2cc3e4da46804c52fc371b71893e8876e59 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Sun, 19 Apr 2020 18:49:24 +0200 Subject: [PATCH] Improved flow typing for the vote screen --- src/components/Amicale/AuthenticatedScreen.js | 9 +- src/components/Amicale/LogoutDialog.js | 7 +- src/components/Amicale/Vote/VoteResults.js | 28 +-- src/components/Amicale/Vote/VoteSelect.js | 22 +- src/components/Amicale/Vote/VoteTitle.js | 4 +- src/components/Amicale/Vote/VoteWait.js | 20 +- src/screens/Amicale/VoteScreen.js | 202 +++++++++++------- 7 files changed, 170 insertions(+), 122 deletions(-) diff --git a/src/components/Amicale/AuthenticatedScreen.js b/src/components/Amicale/AuthenticatedScreen.js index 7aff22b..3217421 100644 --- a/src/components/Amicale/AuthenticatedScreen.js +++ b/src/components/Amicale/AuthenticatedScreen.js @@ -5,15 +5,16 @@ import ConnectionManager from "../../managers/ConnectionManager"; import {ERROR_TYPE} from "../../utils/WebData"; import ErrorView from "../Screens/ErrorView"; import BasicLoadingScreen from "../Screens/BasicLoadingScreen"; +import {StackNavigationProp} from "@react-navigation/stack"; type Props = { - navigation: Object, + navigation: StackNavigationProp, requests: Array<{ link: string, params: Object, mandatory: boolean }>, - renderFunction: Function, + renderFunction: (Array<{ [key: string]: any } | null>) => React.Node, } type State = { @@ -29,7 +30,7 @@ class AuthenticatedScreen extends React.Component { currentUserToken: string | null; connectionManager: ConnectionManager; errors: Array; - fetchedData: Array; + fetchedData: Array<{ [key: string]: any } | null>; constructor(props: Object) { super(props); @@ -88,7 +89,7 @@ class AuthenticatedScreen extends React.Component { * @param index The index for the data * @param error The error code received */ - onRequestFinished(data: Object | null, index: number, error: number) { + onRequestFinished(data: { [key: string]: any } | null, index: number, error: number) { if (index >= 0 && index < this.props.requests.length) { this.fetchedData[index] = data; this.errors[index] = error; diff --git a/src/components/Amicale/LogoutDialog.js b/src/components/Amicale/LogoutDialog.js index 5c0f63f..71e208b 100644 --- a/src/components/Amicale/LogoutDialog.js +++ b/src/components/Amicale/LogoutDialog.js @@ -4,17 +4,18 @@ import * as React from 'react'; import i18n from 'i18n-js'; import LoadingConfirmDialog from "../Dialogs/LoadingConfirmDialog"; import ConnectionManager from "../../managers/ConnectionManager"; +import {StackNavigationProp} from "@react-navigation/stack"; type Props = { - navigation: Object, + navigation: StackNavigationProp, visible: boolean, - onDismiss: Function, + onDismiss: () => void, } class LogoutDialog extends React.PureComponent { onClickAccept = async () => { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { ConnectionManager.getInstance().disconnect() .then(() => { this.props.navigation.reset({ diff --git a/src/components/Amicale/Vote/VoteResults.js b/src/components/Amicale/Vote/VoteResults.js index ff5fd6a..90fa68c 100644 --- a/src/components/Amicale/Vote/VoteResults.js +++ b/src/components/Amicale/Vote/VoteResults.js @@ -1,24 +1,25 @@ // @flow import * as React from 'react'; -import {Avatar, Card, List, ProgressBar, Subheading, withTheme} from "react-native-paper"; +import {Avatar, Card, List, ProgressBar, Subheading, Theme, withTheme} from "react-native-paper"; import {FlatList, StyleSheet} from "react-native"; import i18n from 'i18n-js'; +import type {team} from "../../../screens/Amicale/VoteScreen"; + type Props = { - teams: Array, + teams: Array, dateEnd: string, + theme: Theme, } class VoteResults extends React.Component { totalVotes: number; winnerIds: Array; - colors: Object; constructor(props) { super(); - this.colors = props.theme.colors; props.teams.sort(this.sortByVotes); this.getTotalVotes(props.teams); this.getWinnerIds(props.teams); @@ -28,16 +29,16 @@ class VoteResults extends React.Component { return false; } - sortByVotes = (a: Object, b: Object) => b.votes - a.votes; + sortByVotes = (a: team, b: team) => b.votes - a.votes; - getTotalVotes(teams: Array) { + getTotalVotes(teams: Array) { this.totalVotes = 0; for (let i = 0; i < teams.length; i++) { this.totalVotes += teams[i].votes; } } - getWinnerIds(teams: Array){ + getWinnerIds(teams: Array){ let max = teams[0].votes; this.winnerIds= []; for (let i = 0; i < teams.length; i++) { @@ -48,11 +49,12 @@ class VoteResults extends React.Component { } } - voteKeyExtractor = (item: Object) => item.id.toString(); + voteKeyExtractor = (item: team) => item.id.toString(); - resultRenderItem = ({item}: Object) => { + resultRenderItem = ({item}: {item: team}) => { const isWinner = this.winnerIds.indexOf(item.id) !== -1; const isDraw = this.winnerIds.length > 1; + const colors = this.props.theme.colors; return ( { title={item.name} description={item.votes + ' ' + i18n.t('voteScreen.results.votes')} left={props => isWinner - ? + ? : null} titleStyle={{ color: isWinner - ? this.colors.primary - : this.colors.text + ? colors.primary + : colors.text }} style={{padding: 0}} /> - + ); }; diff --git a/src/components/Amicale/Vote/VoteSelect.js b/src/components/Amicale/Vote/VoteSelect.js index 48fb02f..a3f249e 100644 --- a/src/components/Amicale/Vote/VoteSelect.js +++ b/src/components/Amicale/Vote/VoteSelect.js @@ -7,11 +7,12 @@ import ConnectionManager from "../../../managers/ConnectionManager"; import LoadingConfirmDialog from "../../Dialogs/LoadingConfirmDialog"; import ErrorDialog from "../../Dialogs/ErrorDialog"; import i18n from 'i18n-js'; +import type {team} from "../../../screens/Amicale/VoteScreen"; type Props = { - teams: Array, - onVoteSuccess: Function, - onVoteError: Function, + teams: Array, + onVoteSuccess: () => void, + onVoteError: () => void, } type State = { @@ -33,16 +34,16 @@ export default class VoteSelect extends React.PureComponent { onVoteSelectionChange = (team: string) => this.setState({selectedTeam: team}); - voteKeyExtractor = (item: Object) => item.id.toString(); + voteKeyExtractor = (item: team) => item.id.toString(); - voteRenderItem = ({item}: Object) => ; + voteRenderItem = ({item}: { item: team }) => ; showVoteDialog = () => this.setState({voteDialogVisible: true}); onVoteDialogDismiss = () => this.setState({voteDialogVisible: false,}); onVoteDialogAccept = async () => { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { ConnectionManager.getInstance().authenticatedRequest( "elections/vote", {"team": parseInt(this.state.selectedTeam)}) @@ -76,10 +77,11 @@ export default class VoteSelect extends React.PureComponent { } + left={(props) => + } /> { +export default class VoteTitle extends React.Component<{}> { shouldComponentUpdate() { return false; diff --git a/src/components/Amicale/Vote/VoteWait.js b/src/components/Amicale/Vote/VoteWait.js index a2129dd..da44ba8 100644 --- a/src/components/Amicale/Vote/VoteWait.js +++ b/src/components/Amicale/Vote/VoteWait.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react'; -import {ActivityIndicator, Card, Paragraph, withTheme} from "react-native-paper"; +import {ActivityIndicator, Card, Paragraph, Theme, withTheme} from "react-native-paper"; import {StyleSheet} from "react-native"; import i18n from 'i18n-js'; @@ -10,22 +10,18 @@ type Props = { justVoted: boolean, hasVoted: boolean, isVoteRunning: boolean, + theme: Theme, } class VoteWait extends React.Component { - colors: Object; - - constructor(props) { - super(props); - this.colors = props.theme.colors; - } - shouldComponentUpdate() { return false; } render() { + const colors = this.props.theme.colors; + const startDate = this.props.startDate; return ( { { this.props.justVoted - ? + ? {i18n.t('voteScreen.wait.messageSubmitted')} : null } { this.props.hasVoted - ? + ? {i18n.t('voteScreen.wait.messageVoted')} : null } { - this.props.startDate !== null + startDate != null ? - {i18n.t('voteScreen.wait.messageDate') + ' ' + this.props.startDate} + {i18n.t('voteScreen.wait.messageDate') + ' ' + startDate} : {i18n.t('voteScreen.wait.messageDateUndefined')} } diff --git a/src/screens/Amicale/VoteScreen.js b/src/screens/Amicale/VoteScreen.js index d38e9a5..39defc8 100644 --- a/src/screens/Amicale/VoteScreen.js +++ b/src/screens/Amicale/VoteScreen.js @@ -10,53 +10,78 @@ import VoteSelect from "../../components/Amicale/Vote/VoteSelect"; import VoteResults from "../../components/Amicale/Vote/VoteResults"; import VoteWait from "../../components/Amicale/Vote/VoteWait"; -const FAKE_DATE = { - "date_begin": "2020-04-09 15:50", - "date_end": "2020-04-09 15:50", - "date_result_begin": "2020-04-09 15:50", - "date_result_end": "2020-04-09 22:50", +export type team = { + id: number, + name: string, + votes: number, +} + +type teamResponse = { + has_voted: boolean, + teams: Array, }; -const FAKE_DATE2 = { - "date_begin": null, - "date_end": null, - "date_result_begin": null, - "date_result_end": null, -}; +type stringVoteDates = { + date_begin: string, + date_end: string, + date_result_begin: string, + date_result_end: string, +} -const FAKE_TEAMS = { - has_voted: false, - teams: [ - { - id: 1, - name: "TEST TEAM1", - }, - { - id: 2, - name: "TEST TEAM2", - }, - ], -}; -const FAKE_TEAMS2 = { - has_voted: false, - teams: [ - { - id: 1, - name: "TEST TEAM1", - votes: 9, - }, - { - id: 2, - name: "TEST TEAM2", - votes: 9, - }, - { - id: 3, - name: "TEST TEAM3", - votes: 5, - }, - ], -}; +type objectVoteDates = { + date_begin: Date, + date_end: Date, + date_result_begin: Date, + date_result_end: Date, +} + +// const FAKE_DATE = { +// "date_begin": "2020-04-19 15:50", +// "date_end": "2020-04-19 15:50", +// "date_result_begin": "2020-04-19 19:50", +// "date_result_end": "2020-04-19 22:50", +// }; +// +// const FAKE_DATE2 = { +// "date_begin": null, +// "date_end": null, +// "date_result_begin": null, +// "date_result_end": null, +// }; +// +// const FAKE_TEAMS = { +// has_voted: false, +// teams: [ +// { +// id: 1, +// name: "TEST TEAM1", +// }, +// { +// id: 2, +// name: "TEST TEAM2", +// }, +// ], +// }; +// const FAKE_TEAMS2 = { +// has_voted: false, +// teams: [ +// { +// id: 1, +// name: "TEST TEAM1", +// votes: 9, +// }, +// { +// id: 2, +// name: "TEST TEAM2", +// votes: 9, +// }, +// { +// id: 3, +// name: "TEST TEAM3", +// votes: 5, +// }, +// ], +// }; const MIN_REFRESH_TIME = 5 * 1000; @@ -74,17 +99,17 @@ export default class VoteScreen extends React.Component { hasVoted: false, }; - teams: Array; + teams: Array; hasVoted: boolean; - datesString: Object; - dates: Object; + datesString: null | stringVoteDates; + dates: null | objectVoteDates; today: Date; - mainFlatListData: Array; + mainFlatListData: Array<{ key: string }>; lastRefresh: Date; - authRef: Object; + authRef: { current: null | AuthenticatedScreen }; constructor() { super(); @@ -103,69 +128,81 @@ export default class VoteScreen extends React.Component { canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) > MIN_REFRESH_TIME; else canRefresh = true; - if (canRefresh) + if (canRefresh && this.authRef.current != null) this.authRef.current.reload() }; generateDateObject() { - this.dates = { - date_begin: stringToDate(this.datesString.date_begin), - date_end: stringToDate(this.datesString.date_end), - date_result_begin: stringToDate(this.datesString.date_result_begin), - date_result_end: stringToDate(this.datesString.date_result_end), - }; + const strings = this.datesString; + if (strings != null) { + const dateBegin = stringToDate(strings.date_begin); + const dateEnd = stringToDate(strings.date_end); + const dateResultBegin = stringToDate(strings.date_result_begin); + const dateResultEnd = stringToDate(strings.date_result_end); + if (dateBegin != null && dateEnd != null && dateResultBegin != null && dateResultEnd != null) { + this.dates = { + date_begin: dateBegin, + date_end: dateEnd, + date_result_begin: dateResultBegin, + date_result_end: dateResultEnd, + }; + } else + this.dates = null; + } else + this.dates = null; } getDateString(date: Date, dateString: string): string { if (this.today.getDate() === date.getDate()) { const str = getTimeOnlyString(dateString); - return str !== null ? str : ""; + return str != null ? str : ""; } else return dateString; } - isVoteAvailable() { - return this.dates.date_begin !== null; - } - isVoteRunning() { - return this.today > this.dates.date_begin && this.today < this.dates.date_end; + return this.dates != null && this.today > this.dates.date_begin && this.today < this.dates.date_end; } isVoteStarted() { - return this.today > this.dates.date_begin; + return this.dates != null && this.today > this.dates.date_begin; } isResultRunning() { - return this.today > this.dates.date_result_begin && this.today < this.dates.date_result_end; + return this.dates != null && this.today > this.dates.date_result_begin && this.today < this.dates.date_result_end; } isResultStarted() { - return this.today > this.dates.date_result_begin; + return this.dates != null && this.today > this.dates.date_result_begin; } mainRenderItem = ({item}: Object) => { if (item.key === 'info') return ; - else if (item.key === 'main' && this.isVoteAvailable()) + else if (item.key === 'main' && this.dates != null) return this.getContent(); else return null; }; - getScreen = (data: Array) => { + getScreen = (data: Array<{ [key: string]: any } | null>) => { // data[0] = FAKE_TEAMS2; // data[1] = FAKE_DATE; this.lastRefresh = new Date(); - if (data[1] === null) - data[1] = {date_begin: null}; + const teams : teamResponse | null = data[0]; + const dateStrings : stringVoteDates | null = data[1]; - if (data[0] !== null) { - this.teams = data[0].teams; - this.hasVoted = data[0].has_voted; + if (dateStrings != null && dateStrings.date_begin == null) + this.datesString = null; + else + this.datesString = dateStrings; + + if (teams != null) { + this.teams = teams.teams; + this.hasVoted = teams.has_voted; } - this.datesString = data[1]; + this.generateDateObject(); return ( @@ -211,15 +248,26 @@ export default class VoteScreen extends React.Component { * Votes have ended, results can be displayed */ getVoteResultCard() { - return ; + if (this.dates != null && this.datesString != null) + return ; + else + return null; } /** * Vote will open shortly */ getTeaseVoteCard() { - return ; + if (this.dates != null && this.datesString != null) + return ; + else + return null; } /** @@ -227,7 +275,7 @@ export default class VoteScreen extends React.Component { */ getWaitVoteCard() { let startDate = null; - if (this.dates.date_result_begin !== null) + if (this.dates != null && this.datesString != null && this.dates.date_result_begin != null) startDate = this.getDateString(this.dates.date_result_begin, this.datesString.date_result_begin); return