Improve vote screens to match linter

This commit is contained in:
Arnaud Vergnet 2020-08-02 19:51:19 +02:00
parent 0a9e0eb0ca
commit 142b861ccb
5 changed files with 394 additions and 359 deletions

View file

@ -2,33 +2,35 @@
import * as React from 'react'; import * as React from 'react';
import {View} from 'react-native'; import {View} from 'react-native';
import {Headline, withTheme} from "react-native-paper"; import {Headline, withTheme} from 'react-native-paper';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from '../../../managers/ThemeManager';
type Props = { type PropsType = {
theme: CustomTheme theme: CustomTheme,
} };
class VoteNotAvailable extends React.Component<Props> { class VoteNotAvailable extends React.Component<PropsType> {
shouldComponentUpdate(): boolean {
shouldComponentUpdate() {
return false; return false;
} }
render() { render(): React.Node {
const {props} = this;
return ( return (
<View style={{ <View
width: "100%", style={{
width: '100%',
marginTop: 10, marginTop: 10,
marginBottom: 10, marginBottom: 10,
}}> }}>
<Headline <Headline
style={{ style={{
color: this.props.theme.colors.textDisabled, color: props.theme.colors.textDisabled,
textAlign: "center", textAlign: 'center',
}} }}>
>{i18n.t("screens.vote.noVote")}</Headline> {i18n.t('screens.vote.noVote')}
</Headline>
</View> </View>
); );
} }

View file

@ -1,100 +1,127 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Avatar, Card, List, ProgressBar, Subheading, withTheme} from "react-native-paper"; import {
import {FlatList, StyleSheet} from "react-native"; Avatar,
Card,
List,
ProgressBar,
Subheading,
withTheme,
} from 'react-native-paper';
import {FlatList, StyleSheet} from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import type {team} from "../../../screens/Amicale/VoteScreen"; import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from '../../../managers/ThemeManager';
type PropsType = {
type Props = { teams: Array<VoteTeamType>,
teams: Array<team>,
dateEnd: string, dateEnd: string,
theme: CustomTheme, theme: CustomTheme,
} };
class VoteResults extends React.Component<Props> { const styles = StyleSheet.create({
card: {
margin: 10,
},
icon: {
backgroundColor: 'transparent',
},
});
class VoteResults extends React.Component<PropsType> {
totalVotes: number; totalVotes: number;
winnerIds: Array<number>; winnerIds: Array<number>;
constructor(props) { constructor(props: PropsType) {
super(); super();
props.teams.sort(this.sortByVotes); props.teams.sort(this.sortByVotes);
this.getTotalVotes(props.teams); this.getTotalVotes(props.teams);
this.getWinnerIds(props.teams); this.getWinnerIds(props.teams);
} }
shouldComponentUpdate() { shouldComponentUpdate(): boolean {
return false; return false;
} }
sortByVotes = (a: team, b: team) => b.votes - a.votes; getTotalVotes(teams: Array<VoteTeamType>) {
getTotalVotes(teams: Array<team>) {
this.totalVotes = 0; this.totalVotes = 0;
for (let i = 0; i < teams.length; i++) { for (let i = 0; i < teams.length; i += 1) {
this.totalVotes += teams[i].votes; this.totalVotes += teams[i].votes;
} }
} }
getWinnerIds(teams: Array<team>) { getWinnerIds(teams: Array<VoteTeamType>) {
let max = teams[0].votes; const max = teams[0].votes;
this.winnerIds = []; this.winnerIds = [];
for (let i = 0; i < teams.length; i++) { for (let i = 0; i < teams.length; i += 1) {
if (teams[i].votes === max) if (teams[i].votes === max) this.winnerIds.push(teams[i].id);
this.winnerIds.push(teams[i].id); else break;
else
break;
} }
} }
voteKeyExtractor = (item: team) => item.id.toString(); sortByVotes = (a: VoteTeamType, b: VoteTeamType): number => b.votes - a.votes;
resultRenderItem = ({item}: { item: team }) => { voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
resultRenderItem = ({item}: {item: VoteTeamType}): React.Node => {
const isWinner = this.winnerIds.indexOf(item.id) !== -1; const isWinner = this.winnerIds.indexOf(item.id) !== -1;
const isDraw = this.winnerIds.length > 1; const isDraw = this.winnerIds.length > 1;
const colors = this.props.theme.colors; const {props} = this;
return ( return (
<Card style={{ <Card
style={{
marginTop: 10, marginTop: 10,
elevation: isWinner ? 5 : 3, elevation: isWinner ? 5 : 3,
}}> }}>
<List.Item <List.Item
title={item.name} title={item.name}
description={item.votes + ' ' + i18n.t('screens.vote.results.votes')} description={`${item.votes} ${i18n.t('screens.vote.results.votes')}`}
left={props => isWinner left={({size}: {size: number}): React.Node =>
? <List.Icon {...props} icon={isDraw ? "trophy-outline" : "trophy"} color={colors.primary}/> isWinner ? (
: null} <List.Icon
size={size}
icon={isDraw ? 'trophy-outline' : 'trophy'}
color={props.theme.colors.primary}
/>
) : null
}
titleStyle={{ titleStyle={{
color: isWinner color: isWinner
? colors.primary ? props.theme.colors.primary
: colors.text : props.theme.colors.text,
}} }}
style={{padding: 0}} style={{padding: 0}}
/> />
<ProgressBar progress={item.votes / this.totalVotes} color={colors.primary}/> <ProgressBar
progress={item.votes / this.totalVotes}
color={props.theme.colors.primary}
/>
</Card> </Card>
); );
}; };
render() { render(): React.Node {
const {props} = this;
return ( return (
<Card style={styles.card}> <Card style={styles.card}>
<Card.Title <Card.Title
title={i18n.t('screens.vote.results.title')} title={i18n.t('screens.vote.results.title')}
subtitle={i18n.t('screens.vote.results.subtitle') + ' ' + this.props.dateEnd} subtitle={`${i18n.t('screens.vote.results.subtitle')} ${
left={(props) => <Avatar.Icon props.dateEnd
{...props} }`}
icon={"podium-gold"} left={({size}: {size: number}): React.Node => (
/>} <Avatar.Icon size={size} icon="podium-gold" />
)}
/> />
<Card.Content> <Card.Content>
<Subheading>{i18n.t('screens.vote.results.totalVotes') + ' ' + this.totalVotes}</Subheading> <Subheading>{`${i18n.t('screens.vote.results.totalVotes')} ${
{/*$FlowFixMe*/} this.totalVotes
}`}</Subheading>
{/* $FlowFixMe */}
<FlatList <FlatList
data={this.props.teams} data={props.teams}
keyExtractor={this.voteKeyExtractor} keyExtractor={this.voteKeyExtractor}
renderItem={this.resultRenderItem} renderItem={this.resultRenderItem}
/> />
@ -104,13 +131,4 @@ class VoteResults extends React.Component<Props> {
} }
} }
const styles = StyleSheet.create({
card: {
margin: 10,
},
icon: {
backgroundColor: 'transparent'
},
});
export default withTheme(VoteResults); export default withTheme(VoteResults);

View file

@ -1,55 +1,74 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Avatar, Button, Card, RadioButton} from "react-native-paper"; import {Avatar, Button, Card, RadioButton} from 'react-native-paper';
import {FlatList, StyleSheet, View} from "react-native"; import {FlatList, StyleSheet, View} from 'react-native';
import ConnectionManager from "../../../managers/ConnectionManager";
import LoadingConfirmDialog from "../../Dialogs/LoadingConfirmDialog";
import ErrorDialog from "../../Dialogs/ErrorDialog";
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import type {team} from "../../../screens/Amicale/VoteScreen"; import ConnectionManager from '../../../managers/ConnectionManager';
import LoadingConfirmDialog from '../../Dialogs/LoadingConfirmDialog';
import ErrorDialog from '../../Dialogs/ErrorDialog';
import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
type Props = { type PropsType = {
teams: Array<team>, teams: Array<VoteTeamType>,
onVoteSuccess: () => void, onVoteSuccess: () => void,
onVoteError: () => void, onVoteError: () => void,
} };
type State = { type StateType = {
selectedTeam: string, selectedTeam: string,
voteDialogVisible: boolean, voteDialogVisible: boolean,
errorDialogVisible: boolean, errorDialogVisible: boolean,
currentError: number, currentError: number,
} };
const styles = StyleSheet.create({
card: {
margin: 10,
},
icon: {
backgroundColor: 'transparent',
},
});
export default class VoteSelect extends React.PureComponent<Props, State> { export default class VoteSelect extends React.PureComponent<
PropsType,
state = { StateType,
selectedTeam: "none", > {
constructor() {
super();
this.state = {
selectedTeam: 'none',
voteDialogVisible: false, voteDialogVisible: false,
errorDialogVisible: false, errorDialogVisible: false,
currentError: 0, currentError: 0,
}; };
}
onVoteSelectionChange = (team: string) => this.setState({selectedTeam: team}); onVoteSelectionChange = (teamName: string): void =>
this.setState({selectedTeam: teamName});
voteKeyExtractor = (item: team) => item.id.toString(); voteKeyExtractor = (item: VoteTeamType): string => item.id.toString();
voteRenderItem = ({item}: { item: team }) => <RadioButton.Item label={item.name} value={item.id.toString()}/>; voteRenderItem = ({item}: {item: VoteTeamType}): React.Node => (
<RadioButton.Item label={item.name} value={item.id.toString()} />
);
showVoteDialog = () => this.setState({voteDialogVisible: true}); showVoteDialog = (): void => this.setState({voteDialogVisible: true});
onVoteDialogDismiss = () => this.setState({voteDialogVisible: false,}); onVoteDialogDismiss = (): void => this.setState({voteDialogVisible: false});
onVoteDialogAccept = async () => { onVoteDialogAccept = async (): Promise<void> => {
return new Promise((resolve) => { return new Promise((resolve: () => void) => {
ConnectionManager.getInstance().authenticatedRequest( const {state} = this;
"elections/vote", ConnectionManager.getInstance()
{"team": parseInt(this.state.selectedTeam)}) .authenticatedRequest('elections/vote', {
team: parseInt(state.selectedTeam, 10),
})
.then(() => { .then(() => {
this.onVoteDialogDismiss(); this.onVoteDialogDismiss();
this.props.onVoteSuccess(); const {props} = this;
props.onVoteSuccess();
resolve(); resolve();
}) })
.catch((error: number) => { .catch((error: number) => {
@ -60,39 +79,39 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
}); });
}; };
showErrorDialog = (error: number) => this.setState({ showErrorDialog = (error: number): void =>
this.setState({
errorDialogVisible: true, errorDialogVisible: true,
currentError: error, currentError: error,
}); });
onErrorDialogDismiss = () => { onErrorDialogDismiss = () => {
this.setState({errorDialogVisible: false}); this.setState({errorDialogVisible: false});
this.props.onVoteError(); const {props} = this;
props.onVoteError();
}; };
render() { render(): React.Node {
const {state, props} = this;
return ( return (
<View> <View>
<Card style={styles.card}> <Card style={styles.card}>
<Card.Title <Card.Title
title={i18n.t('screens.vote.select.title')} title={i18n.t('screens.vote.select.title')}
subtitle={i18n.t('screens.vote.select.subtitle')} subtitle={i18n.t('screens.vote.select.subtitle')}
left={(props) => left={({size}: {size: number}): React.Node => (
<Avatar.Icon <Avatar.Icon size={size} icon="alert-decagram" />
{...props} )}
icon={"alert-decagram"}
/>}
/> />
<Card.Content> <Card.Content>
<RadioButton.Group <RadioButton.Group
onValueChange={this.onVoteSelectionChange} onValueChange={this.onVoteSelectionChange}
value={this.state.selectedTeam} value={state.selectedTeam}>
> {/* $FlowFixMe */}
{/*$FlowFixMe*/}
<FlatList <FlatList
data={this.props.teams} data={props.teams}
keyExtractor={this.voteKeyExtractor} keyExtractor={this.voteKeyExtractor}
extraData={this.state.selectedTeam} extraData={state.selectedTeam}
renderItem={this.voteRenderItem} renderItem={this.voteRenderItem}
/> />
</RadioButton.Group> </RadioButton.Group>
@ -103,14 +122,13 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
mode="contained" mode="contained"
onPress={this.showVoteDialog} onPress={this.showVoteDialog}
style={{marginLeft: 'auto'}} style={{marginLeft: 'auto'}}
disabled={this.state.selectedTeam === "none"} disabled={state.selectedTeam === 'none'}>
>
{i18n.t('screens.vote.select.sendButton')} {i18n.t('screens.vote.select.sendButton')}
</Button> </Button>
</Card.Actions> </Card.Actions>
</Card> </Card>
<LoadingConfirmDialog <LoadingConfirmDialog
visible={this.state.voteDialogVisible} visible={state.voteDialogVisible}
onDismiss={this.onVoteDialogDismiss} onDismiss={this.onVoteDialogDismiss}
onAccept={this.onVoteDialogAccept} onAccept={this.onVoteDialogAccept}
title={i18n.t('screens.vote.select.dialogTitle')} title={i18n.t('screens.vote.select.dialogTitle')}
@ -118,20 +136,11 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
message={i18n.t('screens.vote.select.dialogMessage')} message={i18n.t('screens.vote.select.dialogMessage')}
/> />
<ErrorDialog <ErrorDialog
visible={this.state.errorDialogVisible} visible={state.errorDialogVisible}
onDismiss={this.onErrorDialogDismiss} onDismiss={this.onErrorDialogDismiss}
errorCode={this.state.currentError} errorCode={state.currentError}
/> />
</View> </View>
); );
} }
} }
const styles = StyleSheet.create({
card: {
margin: 10,
},
icon: {
backgroundColor: 'transparent'
},
});

View file

@ -1,45 +1,45 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {Avatar, Card, Paragraph} from "react-native-paper"; import {Avatar, Card, Paragraph} from 'react-native-paper';
import {StyleSheet} from "react-native"; import {StyleSheet} from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
type Props = { type PropsType = {
startDate: string, startDate: string,
} };
export default class VoteTease extends React.Component<Props> {
shouldComponentUpdate() {
return false;
}
render() {
return (
<Card style={styles.card}>
<Card.Title
title={i18n.t('screens.vote.tease.title')}
subtitle={i18n.t('screens.vote.tease.subtitle')}
left={props => <Avatar.Icon
{...props}
icon="vote"/>}
/>
<Card.Content>
<Paragraph>
{i18n.t('screens.vote.tease.message') + ' ' + this.props.startDate}
</Paragraph>
</Card.Content>
</Card>
);
}
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
card: { card: {
margin: 10, margin: 10,
}, },
icon: { icon: {
backgroundColor: 'transparent' backgroundColor: 'transparent',
}, },
}); });
export default class VoteTease extends React.Component<PropsType> {
shouldComponentUpdate(): boolean {
return false;
}
render(): React.Node {
const {props} = this;
return (
<Card style={styles.card}>
<Card.Title
title={i18n.t('screens.vote.tease.title')}
subtitle={i18n.t('screens.vote.tease.subtitle')}
left={({size}: {size: number}): React.Node => (
<Avatar.Icon size={size} icon="vote" />
)}
/>
<Card.Content>
<Paragraph>
{`${i18n.t('screens.vote.tease.message')} ${props.startDate}`}
</Paragraph>
</Card.Content>
</Card>
);
}
}

View file

@ -1,72 +1,78 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {ActivityIndicator, Card, Paragraph, withTheme} from "react-native-paper"; import {
import {StyleSheet} from "react-native"; ActivityIndicator,
Card,
Paragraph,
withTheme,
} from 'react-native-paper';
import {StyleSheet} from 'react-native';
import i18n from 'i18n-js'; import i18n from 'i18n-js';
import type {CustomTheme} from "../../../managers/ThemeManager"; import type {CustomTheme} from '../../../managers/ThemeManager';
type Props = { type PropsType = {
startDate: string | null, startDate: string | null,
justVoted: boolean, justVoted: boolean,
hasVoted: boolean, hasVoted: boolean,
isVoteRunning: boolean, isVoteRunning: boolean,
theme: CustomTheme, theme: CustomTheme,
} };
class VoteWait extends React.Component<Props> {
shouldComponentUpdate() {
return false;
}
render() {
const colors = this.props.theme.colors;
const startDate = this.props.startDate;
return (
<Card style={styles.card}>
<Card.Title
title={this.props.isVoteRunning
? i18n.t('screens.vote.wait.titleSubmitted')
: i18n.t('screens.vote.wait.titleEnded')}
subtitle={i18n.t('screens.vote.wait.subtitle')}
left={(props) => <ActivityIndicator {...props}/>}
/>
<Card.Content>
{
this.props.justVoted
? <Paragraph style={{color: colors.success}}>
{i18n.t('screens.vote.wait.messageSubmitted')}
</Paragraph>
: null
}
{
this.props.hasVoted
? <Paragraph style={{color: colors.success}}>
{i18n.t('screens.vote.wait.messageVoted')}
</Paragraph>
: null
}
{
startDate != null
? <Paragraph>
{i18n.t('screens.vote.wait.messageDate') + ' ' + startDate}
</Paragraph>
: <Paragraph>{i18n.t('screens.vote.wait.messageDateUndefined')}</Paragraph>
}
</Card.Content>
</Card>
);
}
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
card: { card: {
margin: 10, margin: 10,
}, },
icon: { icon: {
backgroundColor: 'transparent' backgroundColor: 'transparent',
}, },
}); });
class VoteWait extends React.Component<PropsType> {
shouldComponentUpdate(): boolean {
return false;
}
render(): React.Node {
const {props} = this;
const {startDate} = props;
return (
<Card style={styles.card}>
<Card.Title
title={
props.isVoteRunning
? i18n.t('screens.vote.wait.titleSubmitted')
: i18n.t('screens.vote.wait.titleEnded')
}
subtitle={i18n.t('screens.vote.wait.subtitle')}
left={({size}: {size: number}): React.Node => (
<ActivityIndicator size={size} />
)}
/>
<Card.Content>
{props.justVoted ? (
<Paragraph style={{color: props.theme.colors.success}}>
{i18n.t('screens.vote.wait.messageSubmitted')}
</Paragraph>
) : null}
{props.hasVoted ? (
<Paragraph style={{color: props.theme.colors.success}}>
{i18n.t('screens.vote.wait.messageVoted')}
</Paragraph>
) : null}
{startDate != null ? (
<Paragraph>
{`${i18n.t('screens.vote.wait.messageDate')} ${startDate}`}
</Paragraph>
) : (
<Paragraph>
{i18n.t('screens.vote.wait.messageDateUndefined')}
</Paragraph>
)}
</Card.Content>
</Card>
);
}
}
export default withTheme(VoteWait); export default withTheme(VoteWait);