forked from vergnet/application-amicale
Improve vote screens to match linter
This commit is contained in:
parent
0a9e0eb0ca
commit
142b861ccb
5 changed files with 394 additions and 359 deletions
|
@ -2,33 +2,35 @@
|
|||
|
||||
import * as React from 'react';
|
||||
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 type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
import type {CustomTheme} from '../../../managers/ThemeManager';
|
||||
|
||||
type Props = {
|
||||
theme: CustomTheme
|
||||
}
|
||||
type PropsType = {
|
||||
theme: CustomTheme,
|
||||
};
|
||||
|
||||
class VoteNotAvailable extends React.Component<Props> {
|
||||
|
||||
shouldComponentUpdate() {
|
||||
class VoteNotAvailable extends React.Component<PropsType> {
|
||||
shouldComponentUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
return (
|
||||
<View style={{
|
||||
width: "100%",
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<Headline
|
||||
style={{
|
||||
color: this.props.theme.colors.textDisabled,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>{i18n.t("screens.vote.noVote")}</Headline>
|
||||
color: props.theme.colors.textDisabled,
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
{i18n.t('screens.vote.noVote')}
|
||||
</Headline>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,100 +1,127 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, Card, List, ProgressBar, Subheading, withTheme} from "react-native-paper";
|
||||
import {FlatList, StyleSheet} from "react-native";
|
||||
import {
|
||||
Avatar,
|
||||
Card,
|
||||
List,
|
||||
ProgressBar,
|
||||
Subheading,
|
||||
withTheme,
|
||||
} from 'react-native-paper';
|
||||
import {FlatList, StyleSheet} from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import type {team} from "../../../screens/Amicale/VoteScreen";
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
import type {VoteTeamType} from '../../../screens/Amicale/VoteScreen';
|
||||
import type {CustomTheme} from '../../../managers/ThemeManager';
|
||||
|
||||
|
||||
type Props = {
|
||||
teams: Array<team>,
|
||||
type PropsType = {
|
||||
teams: Array<VoteTeamType>,
|
||||
dateEnd: string,
|
||||
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;
|
||||
|
||||
winnerIds: Array<number>;
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: PropsType) {
|
||||
super();
|
||||
props.teams.sort(this.sortByVotes);
|
||||
this.getTotalVotes(props.teams);
|
||||
this.getWinnerIds(props.teams);
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
shouldComponentUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
sortByVotes = (a: team, b: team) => b.votes - a.votes;
|
||||
|
||||
getTotalVotes(teams: Array<team>) {
|
||||
getTotalVotes(teams: Array<VoteTeamType>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
getWinnerIds(teams: Array<team>) {
|
||||
let max = teams[0].votes;
|
||||
getWinnerIds(teams: Array<VoteTeamType>) {
|
||||
const max = teams[0].votes;
|
||||
this.winnerIds = [];
|
||||
for (let i = 0; i < teams.length; i++) {
|
||||
if (teams[i].votes === max)
|
||||
this.winnerIds.push(teams[i].id);
|
||||
else
|
||||
break;
|
||||
for (let i = 0; i < teams.length; i += 1) {
|
||||
if (teams[i].votes === max) this.winnerIds.push(teams[i].id);
|
||||
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 isDraw = this.winnerIds.length > 1;
|
||||
const colors = this.props.theme.colors;
|
||||
const {props} = this;
|
||||
return (
|
||||
<Card style={{
|
||||
<Card
|
||||
style={{
|
||||
marginTop: 10,
|
||||
elevation: isWinner ? 5 : 3,
|
||||
}}>
|
||||
<List.Item
|
||||
title={item.name}
|
||||
description={item.votes + ' ' + i18n.t('screens.vote.results.votes')}
|
||||
left={props => isWinner
|
||||
? <List.Icon {...props} icon={isDraw ? "trophy-outline" : "trophy"} color={colors.primary}/>
|
||||
: null}
|
||||
description={`${item.votes} ${i18n.t('screens.vote.results.votes')}`}
|
||||
left={({size}: {size: number}): React.Node =>
|
||||
isWinner ? (
|
||||
<List.Icon
|
||||
size={size}
|
||||
icon={isDraw ? 'trophy-outline' : 'trophy'}
|
||||
color={props.theme.colors.primary}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
titleStyle={{
|
||||
color: isWinner
|
||||
? colors.primary
|
||||
: colors.text
|
||||
? props.theme.colors.primary
|
||||
: props.theme.colors.text,
|
||||
}}
|
||||
style={{padding: 0}}
|
||||
/>
|
||||
<ProgressBar progress={item.votes / this.totalVotes} color={colors.primary}/>
|
||||
<ProgressBar
|
||||
progress={item.votes / this.totalVotes}
|
||||
color={props.theme.colors.primary}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {props} = this;
|
||||
return (
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.vote.results.title')}
|
||||
subtitle={i18n.t('screens.vote.results.subtitle') + ' ' + this.props.dateEnd}
|
||||
left={(props) => <Avatar.Icon
|
||||
{...props}
|
||||
icon={"podium-gold"}
|
||||
/>}
|
||||
subtitle={`${i18n.t('screens.vote.results.subtitle')} ${
|
||||
props.dateEnd
|
||||
}`}
|
||||
left={({size}: {size: number}): React.Node => (
|
||||
<Avatar.Icon size={size} icon="podium-gold" />
|
||||
)}
|
||||
/>
|
||||
<Card.Content>
|
||||
<Subheading>{i18n.t('screens.vote.results.totalVotes') + ' ' + this.totalVotes}</Subheading>
|
||||
{/*$FlowFixMe*/}
|
||||
<Subheading>{`${i18n.t('screens.vote.results.totalVotes')} ${
|
||||
this.totalVotes
|
||||
}`}</Subheading>
|
||||
{/* $FlowFixMe */}
|
||||
<FlatList
|
||||
data={this.props.teams}
|
||||
data={props.teams}
|
||||
keyExtractor={this.voteKeyExtractor}
|
||||
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);
|
||||
|
|
|
@ -1,55 +1,74 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, Button, Card, RadioButton} from "react-native-paper";
|
||||
import {FlatList, StyleSheet, View} from "react-native";
|
||||
import ConnectionManager from "../../../managers/ConnectionManager";
|
||||
import LoadingConfirmDialog from "../../Dialogs/LoadingConfirmDialog";
|
||||
import ErrorDialog from "../../Dialogs/ErrorDialog";
|
||||
import {Avatar, Button, Card, RadioButton} from 'react-native-paper';
|
||||
import {FlatList, StyleSheet, View} from 'react-native';
|
||||
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 = {
|
||||
teams: Array<team>,
|
||||
type PropsType = {
|
||||
teams: Array<VoteTeamType>,
|
||||
onVoteSuccess: () => void,
|
||||
onVoteError: () => void,
|
||||
}
|
||||
};
|
||||
|
||||
type State = {
|
||||
type StateType = {
|
||||
selectedTeam: string,
|
||||
voteDialogVisible: boolean,
|
||||
errorDialogVisible: boolean,
|
||||
currentError: number,
|
||||
}
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
|
||||
export default class VoteSelect extends React.PureComponent<Props, State> {
|
||||
|
||||
state = {
|
||||
selectedTeam: "none",
|
||||
export default class VoteSelect extends React.PureComponent<
|
||||
PropsType,
|
||||
StateType,
|
||||
> {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
selectedTeam: 'none',
|
||||
voteDialogVisible: false,
|
||||
errorDialogVisible: false,
|
||||
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 () => {
|
||||
return new Promise((resolve) => {
|
||||
ConnectionManager.getInstance().authenticatedRequest(
|
||||
"elections/vote",
|
||||
{"team": parseInt(this.state.selectedTeam)})
|
||||
onVoteDialogAccept = async (): Promise<void> => {
|
||||
return new Promise((resolve: () => void) => {
|
||||
const {state} = this;
|
||||
ConnectionManager.getInstance()
|
||||
.authenticatedRequest('elections/vote', {
|
||||
team: parseInt(state.selectedTeam, 10),
|
||||
})
|
||||
.then(() => {
|
||||
this.onVoteDialogDismiss();
|
||||
this.props.onVoteSuccess();
|
||||
const {props} = this;
|
||||
props.onVoteSuccess();
|
||||
resolve();
|
||||
})
|
||||
.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,
|
||||
currentError: error,
|
||||
});
|
||||
|
||||
onErrorDialogDismiss = () => {
|
||||
this.setState({errorDialogVisible: false});
|
||||
this.props.onVoteError();
|
||||
const {props} = this;
|
||||
props.onVoteError();
|
||||
};
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {state, props} = this;
|
||||
return (
|
||||
<View>
|
||||
<Card style={styles.card}>
|
||||
<Card.Title
|
||||
title={i18n.t('screens.vote.select.title')}
|
||||
subtitle={i18n.t('screens.vote.select.subtitle')}
|
||||
left={(props) =>
|
||||
<Avatar.Icon
|
||||
{...props}
|
||||
icon={"alert-decagram"}
|
||||
/>}
|
||||
left={({size}: {size: number}): React.Node => (
|
||||
<Avatar.Icon size={size} icon="alert-decagram" />
|
||||
)}
|
||||
/>
|
||||
<Card.Content>
|
||||
<RadioButton.Group
|
||||
onValueChange={this.onVoteSelectionChange}
|
||||
value={this.state.selectedTeam}
|
||||
>
|
||||
{/*$FlowFixMe*/}
|
||||
value={state.selectedTeam}>
|
||||
{/* $FlowFixMe */}
|
||||
<FlatList
|
||||
data={this.props.teams}
|
||||
data={props.teams}
|
||||
keyExtractor={this.voteKeyExtractor}
|
||||
extraData={this.state.selectedTeam}
|
||||
extraData={state.selectedTeam}
|
||||
renderItem={this.voteRenderItem}
|
||||
/>
|
||||
</RadioButton.Group>
|
||||
|
@ -103,14 +122,13 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
|
|||
mode="contained"
|
||||
onPress={this.showVoteDialog}
|
||||
style={{marginLeft: 'auto'}}
|
||||
disabled={this.state.selectedTeam === "none"}
|
||||
>
|
||||
disabled={state.selectedTeam === 'none'}>
|
||||
{i18n.t('screens.vote.select.sendButton')}
|
||||
</Button>
|
||||
</Card.Actions>
|
||||
</Card>
|
||||
<LoadingConfirmDialog
|
||||
visible={this.state.voteDialogVisible}
|
||||
visible={state.voteDialogVisible}
|
||||
onDismiss={this.onVoteDialogDismiss}
|
||||
onAccept={this.onVoteDialogAccept}
|
||||
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')}
|
||||
/>
|
||||
<ErrorDialog
|
||||
visible={this.state.errorDialogVisible}
|
||||
visible={state.errorDialogVisible}
|
||||
onDismiss={this.onErrorDialogDismiss}
|
||||
errorCode={this.state.currentError}
|
||||
errorCode={state.currentError}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
icon: {
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {Avatar, Card, Paragraph} from "react-native-paper";
|
||||
import {StyleSheet} from "react-native";
|
||||
import {Avatar, Card, Paragraph} from 'react-native-paper';
|
||||
import {StyleSheet} from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
|
||||
type Props = {
|
||||
type PropsType = {
|
||||
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({
|
||||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +1,78 @@
|
|||
// @flow
|
||||
|
||||
import * as React from 'react';
|
||||
import {ActivityIndicator, Card, Paragraph, withTheme} from "react-native-paper";
|
||||
import {StyleSheet} from "react-native";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Card,
|
||||
Paragraph,
|
||||
withTheme,
|
||||
} from 'react-native-paper';
|
||||
import {StyleSheet} from 'react-native';
|
||||
import i18n from 'i18n-js';
|
||||
import type {CustomTheme} from "../../../managers/ThemeManager";
|
||||
import type {CustomTheme} from '../../../managers/ThemeManager';
|
||||
|
||||
type Props = {
|
||||
type PropsType = {
|
||||
startDate: string | null,
|
||||
justVoted: boolean,
|
||||
hasVoted: boolean,
|
||||
isVoteRunning: boolean,
|
||||
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({
|
||||
card: {
|
||||
margin: 10,
|
||||
},
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue