Compare commits
No commits in common. "48fdca72c0b4f9bba1e1a815a4df3c6dcfa0c39d" and "ed2bf89d2f570d24172d2e5b4842ad787f871199" have entirely different histories.
48fdca72c0
...
ed2bf89d2f
16 changed files with 404 additions and 676 deletions
|
|
@ -3,7 +3,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {withTheme} from 'react-native-paper';
|
import {withTheme} from 'react-native-paper';
|
||||||
import ConnectionManager, {ERROR_TYPE} from "../../managers/ConnectionManager";
|
import ConnectionManager, {ERROR_TYPE} from "../../managers/ConnectionManager";
|
||||||
import ErrorView from "../Custom/ErrorView";
|
import NetworkErrorComponent from "../Custom/NetworkErrorComponent";
|
||||||
|
import i18n from 'i18n-js';
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -48,7 +49,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
if (this.connectionManager.isLoggedIn()) {
|
if (this.connectionManager.isLoggedIn()) {
|
||||||
for (let i = 0; i < this.props.links.length; i++) {
|
for (let i = 0; i < this.props.links.length; i++) {
|
||||||
this.connectionManager.authenticatedRequest(this.props.links[i].link, null, null)
|
this.connectionManager.authenticatedRequest(this.props.links[i].link)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.onFinishedLoading(data, i, -1);
|
this.onFinishedLoading(data, i, -1);
|
||||||
})
|
})
|
||||||
|
|
@ -97,18 +98,37 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getErrorRender() {
|
getErrorRender() {
|
||||||
|
let message;
|
||||||
|
let icon;
|
||||||
|
switch (this.errorCode) {
|
||||||
|
case ERROR_TYPE.BAD_CREDENTIALS:
|
||||||
|
message = i18n.t("loginScreen.errors.credentials");
|
||||||
|
icon = "account-alert-outline";
|
||||||
|
break;
|
||||||
|
case ERROR_TYPE.BAD_TOKEN:
|
||||||
|
message = "BAD TOKEN"; // TODO translate
|
||||||
|
icon = "access-point-network-off";
|
||||||
|
break;
|
||||||
|
case ERROR_TYPE.CONNECTION_ERROR:
|
||||||
|
message = i18n.t("loginScreen.errors.connection");
|
||||||
|
icon = "access-point-network-off";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message = i18n.t("loginScreen.errors.unknown");
|
||||||
|
icon = "alert-circle-outline";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorView
|
<NetworkErrorComponent
|
||||||
errorCode={this.errorCode}
|
{...this.props}
|
||||||
|
icon={icon}
|
||||||
|
message={message}
|
||||||
onRefresh={this.fetchData}
|
onRefresh={this.fetchData}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
reload() {
|
|
||||||
this.fetchData();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
this.state.loading
|
this.state.loading
|
||||||
|
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import {Avatar, Card, List, ProgressBar, Subheading, withTheme} from "react-native-paper";
|
|
||||||
import {FlatList, StyleSheet} from "react-native";
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
teams: Array<Object>,
|
|
||||||
dateEnd: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
class VoteResults extends React.Component<Props> {
|
|
||||||
|
|
||||||
totalVotes: number;
|
|
||||||
winnerId: number;
|
|
||||||
colors: Object;
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super();
|
|
||||||
this.colors = props.theme.colors;
|
|
||||||
props.teams.sort(this.sortByVotes);
|
|
||||||
this.getTotalVotes(props.teams);
|
|
||||||
this.getWinnerId(props.teams);
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortByVotes = (a: Object, b: Object) => b.votes - a.votes;
|
|
||||||
|
|
||||||
getTotalVotes(teams: Array<Object>) {
|
|
||||||
this.totalVotes = 0;
|
|
||||||
for (let i = 0; i < teams.length; i++) {
|
|
||||||
this.totalVotes += teams[i].votes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getWinnerId(teams: Array<Object>) {
|
|
||||||
this.winnerId = teams[0].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
voteKeyExtractor = (item: Object) => item.id.toString();
|
|
||||||
|
|
||||||
resultRenderItem = ({item}: Object) => {
|
|
||||||
const isWinner = this.winnerId === item.id;
|
|
||||||
return (
|
|
||||||
<Card style={{
|
|
||||||
marginTop: 10,
|
|
||||||
elevation: isWinner ? 5 : 3,
|
|
||||||
}}>
|
|
||||||
<List.Item
|
|
||||||
title={item.name}
|
|
||||||
description={item.votes + ' ' + i18n.t('voteScreen.results.votes')}
|
|
||||||
left={props => isWinner
|
|
||||||
? <List.Icon {...props} icon="trophy" color={this.colors.primary}/>
|
|
||||||
: null}
|
|
||||||
titleStyle={{
|
|
||||||
color: isWinner
|
|
||||||
? this.colors.primary
|
|
||||||
: this.colors.text
|
|
||||||
}}
|
|
||||||
style={{padding: 0}}
|
|
||||||
/>
|
|
||||||
<ProgressBar progress={item.votes / this.totalVotes} color={this.colors.primary}/>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Card style={styles.card}>
|
|
||||||
<Card.Title
|
|
||||||
title={i18n.t('voteScreen.results.title')}
|
|
||||||
subtitle={i18n.t('voteScreen.results.subtitle') + ' ' + this.props.dateEnd}
|
|
||||||
left={(props) => <Avatar.Icon
|
|
||||||
{...props}
|
|
||||||
icon={"podium-gold"}
|
|
||||||
/>}
|
|
||||||
/>
|
|
||||||
<Card.Content>
|
|
||||||
<Subheading>{i18n.t('voteScreen.results.totalVotes') + ' ' +this.totalVotes}</Subheading>
|
|
||||||
{/*$FlowFixMe*/}
|
|
||||||
<FlatList
|
|
||||||
data={this.props.teams}
|
|
||||||
keyExtractor={this.voteKeyExtractor}
|
|
||||||
renderItem={this.resultRenderItem}
|
|
||||||
/>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
card: {
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
backgroundColor: 'transparent'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default withTheme(VoteResults);
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
// @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 "../../Dialog/LoadingConfirmDialog";
|
|
||||||
import ErrorDialog from "../../Dialog/ErrorDialog";
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
teams: Array<Object>,
|
|
||||||
onVoteSuccess: Function,
|
|
||||||
onVoteError: Function,
|
|
||||||
}
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
selectedTeam: string,
|
|
||||||
voteDialogVisible: boolean,
|
|
||||||
errorDialogVisible: boolean,
|
|
||||||
currentError: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default class VoteSelect extends React.PureComponent<Props, State> {
|
|
||||||
|
|
||||||
state = {
|
|
||||||
selectedTeam: "none",
|
|
||||||
voteDialogVisible: false,
|
|
||||||
errorDialogVisible: false,
|
|
||||||
currentError: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
onVoteSelectionChange = (team: string) => this.setState({selectedTeam: team});
|
|
||||||
|
|
||||||
voteKeyExtractor = (item: Object) => item.id.toString();
|
|
||||||
|
|
||||||
voteRenderItem = ({item}: Object) => <RadioButton.Item label={item.name} value={item.id.toString()}/>;
|
|
||||||
|
|
||||||
showVoteDialog = () => this.setState({voteDialogVisible: true});
|
|
||||||
|
|
||||||
onVoteDialogDismiss = () => this.setState({voteDialogVisible: false,});
|
|
||||||
|
|
||||||
onVoteDialogAccept = async () => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
ConnectionManager.getInstance().authenticatedRequest(
|
|
||||||
"elections/vote",
|
|
||||||
["vote"],
|
|
||||||
[parseInt(this.state.selectedTeam)])
|
|
||||||
.then(() => {
|
|
||||||
this.onVoteDialogDismiss();
|
|
||||||
this.props.onVoteSuccess();
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.catch((error: number) => {
|
|
||||||
this.onVoteDialogDismiss();
|
|
||||||
this.showErrorDialog(error);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
showErrorDialog = (error: number) => this.setState({
|
|
||||||
errorDialogVisible: true,
|
|
||||||
currentError: error,
|
|
||||||
});
|
|
||||||
|
|
||||||
onErrorDialogDismiss = () => {
|
|
||||||
this.setState({errorDialogVisible: false});
|
|
||||||
this.props.onVoteError();
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<Card style={styles.card}>
|
|
||||||
<Card.Title
|
|
||||||
title={i18n.t('voteScreen.select.title')}
|
|
||||||
subtitle={i18n.t('voteScreen.select.subtitle')}
|
|
||||||
left={(props) => <Avatar.Icon
|
|
||||||
{...props}
|
|
||||||
icon={"alert-decagram"}
|
|
||||||
/>}
|
|
||||||
/>
|
|
||||||
<Card.Content>
|
|
||||||
<RadioButton.Group
|
|
||||||
onValueChange={this.onVoteSelectionChange}
|
|
||||||
value={this.state.selectedTeam}
|
|
||||||
>
|
|
||||||
{/*$FlowFixMe*/}
|
|
||||||
<FlatList
|
|
||||||
data={this.props.teams}
|
|
||||||
keyExtractor={this.voteKeyExtractor}
|
|
||||||
extraData={this.state.selectedTeam}
|
|
||||||
renderItem={this.voteRenderItem}
|
|
||||||
/>
|
|
||||||
</RadioButton.Group>
|
|
||||||
</Card.Content>
|
|
||||||
<Card.Actions>
|
|
||||||
<Button
|
|
||||||
icon="send"
|
|
||||||
mode="contained"
|
|
||||||
onPress={this.showVoteDialog}
|
|
||||||
style={{marginLeft: 'auto'}}
|
|
||||||
disabled={this.state.selectedTeam === "none"}
|
|
||||||
>
|
|
||||||
{i18n.t('voteScreen.select.sendButton')}
|
|
||||||
</Button>
|
|
||||||
</Card.Actions>
|
|
||||||
</Card>
|
|
||||||
<LoadingConfirmDialog
|
|
||||||
visible={this.state.voteDialogVisible}
|
|
||||||
onDismiss={this.onVoteDialogDismiss}
|
|
||||||
onAccept={this.onVoteDialogAccept}
|
|
||||||
title={i18n.t('voteScreen.select.dialogTitle')}
|
|
||||||
titleLoading={i18n.t('voteScreen.select.dialogTitleLoading')}
|
|
||||||
message={i18n.t('voteScreen.select.dialogMessage')}
|
|
||||||
/>
|
|
||||||
<ErrorDialog
|
|
||||||
visible={this.state.errorDialogVisible}
|
|
||||||
onDismiss={this.onErrorDialogDismiss}
|
|
||||||
errorCode={this.state.currentError}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
card: {
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
backgroundColor: 'transparent'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import {Avatar, Card, Paragraph} from "react-native-paper";
|
|
||||||
import {StyleSheet} from "react-native";
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
startDate: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class VoteTease extends React.Component<Props> {
|
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Card style={styles.card}>
|
|
||||||
<Card.Title
|
|
||||||
title={i18n.t('voteScreen.tease.title')}
|
|
||||||
subtitle={i18n.t('voteScreen.tease.subtitle')}
|
|
||||||
left={props => <Avatar.Icon
|
|
||||||
{...props}
|
|
||||||
icon="vote"/>}
|
|
||||||
/>
|
|
||||||
<Card.Content>
|
|
||||||
<Paragraph>
|
|
||||||
{i18n.t('voteScreen.tease.message') + ' ' + this.props.startDate}
|
|
||||||
</Paragraph>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
card: {
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
backgroundColor: 'transparent'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import {Avatar, Card, Paragraph} from "react-native-paper";
|
|
||||||
import {StyleSheet} from "react-native";
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
|
|
||||||
const ICON_AMICALE = require('../../../../assets/amicale.png');
|
|
||||||
|
|
||||||
type Props = {}
|
|
||||||
|
|
||||||
export default class VoteTitle extends React.Component<Props> {
|
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Card style={styles.card}>
|
|
||||||
<Card.Title
|
|
||||||
title={i18n.t('voteScreen.title.title')}
|
|
||||||
subtitle={i18n.t('voteScreen.title.subtitle')}
|
|
||||||
left={(props) => <Avatar.Image
|
|
||||||
{...props}
|
|
||||||
source={ICON_AMICALE}
|
|
||||||
style={styles.icon}
|
|
||||||
/>}
|
|
||||||
/>
|
|
||||||
<Card.Content>
|
|
||||||
<Paragraph>
|
|
||||||
{i18n.t('voteScreen.title.paragraph1')}
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
{i18n.t('voteScreen.title.paragraph2')}
|
|
||||||
</Paragraph>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
card: {
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
backgroundColor: 'transparent'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import {ActivityIndicator, Card, Paragraph, withTheme} from "react-native-paper";
|
|
||||||
import {StyleSheet} from "react-native";
|
|
||||||
import i18n from 'i18n-js';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
startDate: string | null,
|
|
||||||
justVoted: boolean,
|
|
||||||
hasVoted: boolean,
|
|
||||||
isVoteRunning: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
class VoteWait extends React.Component<Props> {
|
|
||||||
|
|
||||||
colors: Object;
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.colors = props.theme.colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Card style={styles.card}>
|
|
||||||
<Card.Title
|
|
||||||
title={this.props.isVoteRunning
|
|
||||||
? i18n.t('voteScreen.wait.titleSubmitted')
|
|
||||||
: i18n.t('voteScreen.wait.titleEnded')}
|
|
||||||
subtitle={i18n.t('voteScreen.wait.subtitle')}
|
|
||||||
left={(props) => <ActivityIndicator {...props}/>}
|
|
||||||
/>
|
|
||||||
<Card.Content>
|
|
||||||
{
|
|
||||||
this.props.justVoted
|
|
||||||
? <Paragraph style={{color: this.colors.success}}>
|
|
||||||
{i18n.t('voteScreen.wait.messageSubmitted')}
|
|
||||||
</Paragraph>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
{
|
|
||||||
this.props.hasVoted
|
|
||||||
? <Paragraph style={{color: this.colors.success}}>
|
|
||||||
{i18n.t('voteScreen.wait.messageVoted')}
|
|
||||||
</Paragraph>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
{
|
|
||||||
this.props.startDate !== null
|
|
||||||
? <Paragraph>
|
|
||||||
{i18n.t('voteScreen.wait.messageDate') + ' ' + this.props.startDate}
|
|
||||||
</Paragraph>
|
|
||||||
: <Paragraph>{i18n.t('voteScreen.wait.messageDateUndefined')}</Paragraph>
|
|
||||||
}
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
card: {
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
backgroundColor: 'transparent'
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default withTheme(VoteWait);
|
|
||||||
|
|
@ -5,10 +5,10 @@ import {Button, Subheading, withTheme} from 'react-native-paper';
|
||||||
import {StyleSheet, View} from "react-native";
|
import {StyleSheet, View} from "react-native";
|
||||||
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
import {MaterialCommunityIcons} from "@expo/vector-icons";
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {ERROR_TYPE} from "../../managers/ConnectionManager";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
errorCode: number,
|
message: string,
|
||||||
|
icon: string,
|
||||||
onRefresh: Function,
|
onRefresh: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,13 +16,10 @@ type State = {
|
||||||
refreshing: boolean,
|
refreshing: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
class ErrorView extends React.PureComponent<Props, State> {
|
class NetworkErrorComponent extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
colors: Object;
|
colors: Object;
|
||||||
|
|
||||||
message: string;
|
|
||||||
icon: string;
|
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
};
|
};
|
||||||
|
|
@ -32,45 +29,7 @@ class ErrorView extends React.PureComponent<Props, State> {
|
||||||
this.colors = props.theme.colors;
|
this.colors = props.theme.colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateMessage() {
|
|
||||||
switch (this.props.errorCode) {
|
|
||||||
case ERROR_TYPE.BAD_CREDENTIALS:
|
|
||||||
this.message = i18n.t("errors.badCredentials");
|
|
||||||
this.icon = "account-alert-outline";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.BAD_TOKEN:
|
|
||||||
this.message = i18n.t("errors.badToken");
|
|
||||||
this.icon = "account-alert-outline";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.NO_CONSENT:
|
|
||||||
this.message = i18n.t("errors.noConsent");
|
|
||||||
this.icon = "account-remove-outline";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.BAD_INPUT:
|
|
||||||
this.message = i18n.t("errors.badInput");
|
|
||||||
this.icon = "alert-circle-outline";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.FORBIDDEN:
|
|
||||||
this.message = i18n.t("errors.forbidden");
|
|
||||||
this.icon = "lock";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.CONNECTION_ERROR:
|
|
||||||
this.message = i18n.t("errors.connectionError");
|
|
||||||
this.icon = "access-point-network-off";
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.SERVER_ERROR:
|
|
||||||
this.message = i18n.t("errors.serverError");
|
|
||||||
this.icon = "server-network-off";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.message = i18n.t("errors.unknown");
|
|
||||||
this.icon = "alert-circle-outline";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.generateMessage();
|
|
||||||
return (
|
return (
|
||||||
<View style={{
|
<View style={{
|
||||||
...styles.outer,
|
...styles.outer,
|
||||||
|
|
@ -79,7 +38,7 @@ class ErrorView extends React.PureComponent<Props, State> {
|
||||||
<View style={styles.inner}>
|
<View style={styles.inner}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name={this.icon}
|
name={this.props.icon}
|
||||||
size={150}
|
size={150}
|
||||||
color={this.colors.textDisabled}/>
|
color={this.colors.textDisabled}/>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -87,7 +46,7 @@ class ErrorView extends React.PureComponent<Props, State> {
|
||||||
...styles.subheading,
|
...styles.subheading,
|
||||||
color: this.colors.textDisabled
|
color: this.colors.textDisabled
|
||||||
}}>
|
}}>
|
||||||
{this.message}
|
{this.props.message}
|
||||||
</Subheading>
|
</Subheading>
|
||||||
<Button
|
<Button
|
||||||
mode={'contained'}
|
mode={'contained'}
|
||||||
|
|
@ -127,4 +86,4 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default withTheme(ErrorView);
|
export default withTheme(NetworkErrorComponent);
|
||||||
|
|
@ -15,31 +15,22 @@ class ErrorDialog extends React.PureComponent<Props> {
|
||||||
message: string;
|
message: string;
|
||||||
|
|
||||||
generateMessage() {
|
generateMessage() {
|
||||||
this.title = i18n.t("errors.title");
|
this.title = i18n.t("loginScreen.errors.title");
|
||||||
switch (this.props.errorCode) {
|
switch (this.props.errorCode) {
|
||||||
case ERROR_TYPE.BAD_CREDENTIALS:
|
case ERROR_TYPE.BAD_CREDENTIALS:
|
||||||
this.message = i18n.t("errors.badCredentials");
|
this.message = i18n.t("loginScreen.errors.credentials");
|
||||||
break;
|
|
||||||
case ERROR_TYPE.BAD_TOKEN:
|
|
||||||
this.message = i18n.t("errors.badToken");
|
|
||||||
break;
|
break;
|
||||||
case ERROR_TYPE.NO_CONSENT:
|
case ERROR_TYPE.NO_CONSENT:
|
||||||
this.message = i18n.t("errors.noConsent");
|
this.message = i18n.t("loginScreen.errors.consent");
|
||||||
break;
|
|
||||||
case ERROR_TYPE.BAD_INPUT:
|
|
||||||
this.message = i18n.t("errors.badInput");
|
|
||||||
break;
|
|
||||||
case ERROR_TYPE.FORBIDDEN:
|
|
||||||
this.message = i18n.t("errors.forbidden");
|
|
||||||
break;
|
break;
|
||||||
case ERROR_TYPE.CONNECTION_ERROR:
|
case ERROR_TYPE.CONNECTION_ERROR:
|
||||||
this.message = i18n.t("errors.connectionError");
|
this.message = i18n.t("loginScreen.errors.connection");
|
||||||
break;
|
break;
|
||||||
case ERROR_TYPE.SERVER_ERROR:
|
case ERROR_TYPE.SERVER_ERROR:
|
||||||
this.message = i18n.t("errors.serverError");
|
this.message = "SERVER ERROR"; // TODO translate
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.message = i18n.t("errors.unknown");
|
this.message = i18n.t("loginScreen.errors.unknown");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,8 @@ import {readData} from "../../utils/WebData";
|
||||||
import i18n from "i18n-js";
|
import i18n from "i18n-js";
|
||||||
import {Snackbar} from 'react-native-paper';
|
import {Snackbar} from 'react-native-paper';
|
||||||
import {RefreshControl, SectionList, View} from "react-native";
|
import {RefreshControl, SectionList, View} from "react-native";
|
||||||
import ErrorView from "../Custom/ErrorView";
|
import NetworkErrorComponent from "../Custom/NetworkErrorComponent";
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
import {ERROR_TYPE} from "../../managers/ConnectionManager";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
@ -193,8 +192,9 @@ export default class WebSectionList extends React.PureComponent<Props, State> {
|
||||||
style={{minHeight: '100%'}}
|
style={{minHeight: '100%'}}
|
||||||
ListEmptyComponent={this.state.refreshing
|
ListEmptyComponent={this.state.refreshing
|
||||||
? <BasicLoadingScreen/>
|
? <BasicLoadingScreen/>
|
||||||
: <ErrorView
|
: <NetworkErrorComponent
|
||||||
errorCode={ERROR_TYPE.CONNECTION_ERROR}
|
message={i18n.t('general.networkError')}
|
||||||
|
icon={"access-point-network-off"}
|
||||||
onRefresh={this.onRefresh}/>
|
onRefresh={this.onRefresh}/>
|
||||||
}
|
}
|
||||||
getItemLayout={this.props.itemHeight !== undefined ? this.itemLayout : undefined}
|
getItemLayout={this.props.itemHeight !== undefined ? this.itemLayout : undefined}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import WebView from "react-native-webview";
|
import WebView from "react-native-webview";
|
||||||
|
import {withTheme} from 'react-native-paper';
|
||||||
import HeaderButton from "../Custom/HeaderButton";
|
import HeaderButton from "../Custom/HeaderButton";
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
import ErrorView from "../Custom/ErrorView";
|
import NetworkErrorComponent from "../Custom/NetworkErrorComponent";
|
||||||
import {ERROR_TYPE} from "../../managers/ConnectionManager";
|
import i18n from "i18n-js";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
@ -31,12 +32,20 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
hasSideMenu: true,
|
hasSideMenu: true,
|
||||||
hasFooter: true,
|
hasFooter: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
webviewRef: Object;
|
webviewRef: Object;
|
||||||
|
|
||||||
constructor() {
|
onRefreshClicked: Function;
|
||||||
super();
|
onWebviewRef: Function;
|
||||||
this.webviewRef = React.createRef();
|
getRenderLoading: Function;
|
||||||
|
|
||||||
|
colors: Object;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onRefreshClicked = this.onRefreshClicked.bind(this);
|
||||||
|
this.onWebviewRef = this.onWebviewRef.bind(this);
|
||||||
|
this.getRenderLoading = this.getRenderLoading.bind(this);
|
||||||
|
this.colors = props.theme.colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -61,19 +70,33 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
/**
|
/**
|
||||||
* Callback to use when refresh button is clicked. Reloads the webview.
|
* Callback to use when refresh button is clicked. Reloads the webview.
|
||||||
*/
|
*/
|
||||||
onRefreshClicked = () => this.webviewRef.current.reload();
|
onRefreshClicked() {
|
||||||
|
if (this.webviewRef !== null)
|
||||||
|
this.webviewRef.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used when receiving the webview ref. Stores the ref for later use
|
||||||
|
*
|
||||||
|
* @param ref
|
||||||
|
*/
|
||||||
|
onWebviewRef(ref: Object) {
|
||||||
|
this.webviewRef = ref
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the loading indicator
|
* Gets the loading indicator
|
||||||
*
|
*
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getRenderLoading = () => <BasicLoadingScreen/>;
|
getRenderLoading() {
|
||||||
|
return <BasicLoadingScreen/>;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<WebView
|
<WebView
|
||||||
ref={this.webviewRef}
|
ref={this.onWebviewRef}
|
||||||
source={{uri: this.props.data[0]['url']}}
|
source={{uri: this.props.data[0]['url']}}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|
@ -83,13 +106,15 @@ class WebViewScreen extends React.PureComponent<Props> {
|
||||||
injectedJavaScript={this.props.data[0]['customJS']}
|
injectedJavaScript={this.props.data[0]['customJS']}
|
||||||
javaScriptEnabled={true}
|
javaScriptEnabled={true}
|
||||||
renderLoading={this.getRenderLoading}
|
renderLoading={this.getRenderLoading}
|
||||||
renderError={() => <ErrorView
|
renderError={() => <NetworkErrorComponent
|
||||||
errorCode={ERROR_TYPE.CONNECTION_ERROR}
|
{...this.props}
|
||||||
onRefresh={this.onRefreshClicked}
|
onRefresh={this.onRefreshClicked}
|
||||||
|
message={i18n.t("loginScreen.errors.connection")}
|
||||||
|
icon={'access-point-network-off'}
|
||||||
/>}
|
/>}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WebViewScreen;
|
export default withTheme(WebViewScreen);
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ class SideBar extends React.Component<Props, State> {
|
||||||
onlyWhenLoggedIn: true,
|
onlyWhenLoggedIn: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n.t('screens.vote'),
|
name: "VOTE",
|
||||||
route: "VoteScreen",
|
route: "VoteScreen",
|
||||||
icon: "vote",
|
icon: "vote",
|
||||||
onlyWhenLoggedIn: true,
|
onlyWhenLoggedIn: true,
|
||||||
|
|
|
||||||
|
|
@ -190,11 +190,11 @@ export default class ConnectionManager {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticatedRequest(path: string, keys: Array<string>|null, values: Array<any>|null) {
|
async authenticatedRequest(path: string, keys: Array<string>, values: Array<any>) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (this.getToken() !== null) {
|
if (this.getToken() !== null) {
|
||||||
let data = {};
|
let data = {};
|
||||||
if (keys !== null && values !== null && keys.length === values.length)
|
if (keys !== undefined && values !== undefined && keys.length === values.length)
|
||||||
data = this.generatePostArguments(keys, values);
|
data = this.generatePostArguments(keys, values);
|
||||||
console.log(data);
|
console.log(data);
|
||||||
fetch(API_ENDPOINT + path, {
|
fetch(API_ENDPOINT + path, {
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ function VoteStackComponent() {
|
||||||
options={({navigation}) => {
|
options={({navigation}) => {
|
||||||
const openDrawer = getDrawerButton.bind(this, navigation);
|
const openDrawer = getDrawerButton.bind(this, navigation);
|
||||||
return {
|
return {
|
||||||
title: i18n.t('screens.vote'),
|
title: "VoteScreen",
|
||||||
headerLeft: openDrawer
|
headerLeft: openDrawer
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,31 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {FlatList, View} from "react-native";
|
import {FlatList, StyleSheet, View} from "react-native";
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
List,
|
||||||
|
Paragraph,
|
||||||
|
ProgressBar,
|
||||||
|
RadioButton,
|
||||||
|
Subheading,
|
||||||
|
withTheme
|
||||||
|
} from 'react-native-paper';
|
||||||
import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
|
import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
|
||||||
import {getTimeOnlyString, stringToDate} from "../../utils/Planning";
|
import {getTimeOnlyString, stringToDate} from "../../utils/Planning";
|
||||||
import VoteTitle from "../../components/Amicale/Vote/VoteTitle";
|
import LoadingConfirmDialog from "../../components/Dialog/LoadingConfirmDialog";
|
||||||
import VoteTease from "../../components/Amicale/Vote/VoteTease";
|
import ConnectionManager from "../../managers/ConnectionManager";
|
||||||
import VoteSelect from "../../components/Amicale/Vote/VoteSelect";
|
|
||||||
import VoteResults from "../../components/Amicale/Vote/VoteResults";
|
const ICON_AMICALE = require('../../../assets/amicale.png');
|
||||||
import VoteWait from "../../components/Amicale/Vote/VoteWait";
|
|
||||||
|
|
||||||
const FAKE_DATE = {
|
const FAKE_DATE = {
|
||||||
"date_begin": "2020-04-07 21:50",
|
"date_begin": "2020-04-06 21:50",
|
||||||
"date_end": "2020-04-06 23:50",
|
"date_end": "2020-04-07 23:50",
|
||||||
"date_result_begin": "2020-04-06 21:50",
|
"date_result_begin": "2020-04-07 21:50",
|
||||||
"date_result_end": "2020-04-07 22:50",
|
"date_result_end": "2020-04-07 21:50",
|
||||||
};
|
};
|
||||||
|
|
||||||
const FAKE_DATE2 = {
|
const FAKE_DATE2 = {
|
||||||
|
|
@ -53,18 +64,25 @@ const FAKE_TEAMS2 = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {}
|
type Props = {
|
||||||
|
navigation: Object,
|
||||||
type State = {
|
theme: Object,
|
||||||
hasVoted: boolean,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VoteScreen extends React.Component<Props, State> {
|
type State = {
|
||||||
|
selectedTeam: string,
|
||||||
|
voteDialogVisible: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
class VoteScreen extends React.Component<Props, State> {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
hasVoted: false,
|
selectedTeam: "none",
|
||||||
|
voteDialogVisible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
colors: Object;
|
||||||
|
|
||||||
teams: Array<Object>;
|
teams: Array<Object>;
|
||||||
hasVoted: boolean;
|
hasVoted: boolean;
|
||||||
datesString: Object;
|
datesString: Object;
|
||||||
|
|
@ -73,21 +91,80 @@ export default class VoteScreen extends React.Component<Props, State> {
|
||||||
today: Date;
|
today: Date;
|
||||||
|
|
||||||
mainFlatListData: Array<Object>;
|
mainFlatListData: Array<Object>;
|
||||||
|
totalVotes: number;
|
||||||
|
|
||||||
authRef: Object;
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
constructor() {
|
this.colors = props.theme.colors;
|
||||||
super();
|
|
||||||
this.hasVoted = false;
|
this.hasVoted = false;
|
||||||
this.today = new Date();
|
this.today = new Date();
|
||||||
this.authRef = React.createRef();
|
|
||||||
this.mainFlatListData = [
|
this.mainFlatListData = [
|
||||||
{key: 'main'},
|
{key: 'main'},
|
||||||
{key: 'info'},
|
{key: 'info'},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadData = () => this.authRef.current.reload();
|
mainRenderItem = ({item}: Object) => {
|
||||||
|
if (item.key === 'info')
|
||||||
|
return this.getTitleCard();
|
||||||
|
else if (item.key === 'main' && this.isVoteAvailable())
|
||||||
|
return this.getContent();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
getScreen = (data: Array<Object>) => {
|
||||||
|
data[0] = FAKE_TEAMS2;
|
||||||
|
data[1] = FAKE_DATE;
|
||||||
|
|
||||||
|
if (data[0] !== null) {
|
||||||
|
this.teams = data[0].teams;
|
||||||
|
this.hasVoted = data[0].has_voted;
|
||||||
|
}
|
||||||
|
this.datesString = data[1];
|
||||||
|
this.generateDateObject();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{/*$FlowFixMe*/}
|
||||||
|
<FlatList
|
||||||
|
data={this.mainFlatListData}
|
||||||
|
extraData={this.state.selectedTeam}
|
||||||
|
renderItem={this.mainRenderItem}
|
||||||
|
/>
|
||||||
|
<LoadingConfirmDialog
|
||||||
|
{...this.props}
|
||||||
|
visible={this.state.voteDialogVisible}
|
||||||
|
onDismiss={this.onVoteDialogDismiss}
|
||||||
|
onAccept={this.onVoteDialogAccept}
|
||||||
|
title={"VOTE?"}
|
||||||
|
titleLoading={"SENDING VOTE..."}
|
||||||
|
message={"SURE?"}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
onVoteDialogDismiss = () => this.setState({voteDialogVisible: false});
|
||||||
|
|
||||||
|
showVoteDialog = () => this.setState({voteDialogVisible: true});
|
||||||
|
|
||||||
|
onVoteDialogAccept = async () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
ConnectionManager.getInstance().authenticatedRequest(
|
||||||
|
"elections/vote",
|
||||||
|
["vote"],
|
||||||
|
[parseInt(this.state.selectedTeam)])
|
||||||
|
.then(() => {
|
||||||
|
this.onVoteDialogDismiss();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.onVoteDialogDismiss();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
generateDateObject() {
|
generateDateObject() {
|
||||||
this.dates = {
|
this.dates = {
|
||||||
|
|
@ -98,14 +175,6 @@ export default class VoteScreen extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getDateString(date: Date, dateString: string): string {
|
|
||||||
if (this.today.getDate() === date.getDate()) {
|
|
||||||
const str = getTimeOnlyString(dateString);
|
|
||||||
return str !== null ? str : "";
|
|
||||||
} else
|
|
||||||
return dateString;
|
|
||||||
}
|
|
||||||
|
|
||||||
isVoteAvailable() {
|
isVoteAvailable() {
|
||||||
return this.dates.date_begin !== null;
|
return this.dates.date_begin !== null;
|
||||||
}
|
}
|
||||||
|
|
@ -126,41 +195,10 @@ export default class VoteScreen extends React.Component<Props, State> {
|
||||||
return this.today > this.dates.date_result_begin;
|
return this.today > this.dates.date_result_begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
mainRenderItem = ({item}: Object) => {
|
|
||||||
if (item.key === 'info')
|
|
||||||
return <VoteTitle/>;
|
|
||||||
else if (item.key === 'main' && this.isVoteAvailable())
|
|
||||||
return this.getContent();
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
getScreen = (data: Array<Object>) => {
|
|
||||||
data[0] = FAKE_TEAMS2;
|
|
||||||
data[1] = FAKE_DATE;
|
|
||||||
|
|
||||||
if (data[0] !== null) {
|
|
||||||
this.teams = data[0].teams;
|
|
||||||
this.hasVoted = data[0].has_voted;
|
|
||||||
}
|
|
||||||
this.datesString = data[1];
|
|
||||||
this.generateDateObject();
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
{/*$FlowFixMe*/}
|
|
||||||
<FlatList
|
|
||||||
data={this.mainFlatListData}
|
|
||||||
extraData={this.state.hasVoted.toString()}
|
|
||||||
renderItem={this.mainRenderItem}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
getContent() {
|
getContent() {
|
||||||
if (!this.isVoteStarted())
|
if (!this.isVoteStarted())
|
||||||
return this.getTeaseVoteCard();
|
return this.getTeaseVoteCard();
|
||||||
else if (this.isVoteRunning() && (!this.hasVoted && !this.state.hasVoted))
|
else if (this.isVoteRunning() && !this.hasVoted)
|
||||||
return this.getVoteCard();
|
return this.getVoteCard();
|
||||||
else if (!this.isResultStarted())
|
else if (!this.isResultStarted())
|
||||||
return this.getWaitVoteCard();
|
return this.getWaitVoteCard();
|
||||||
|
|
@ -170,47 +208,222 @@ export default class VoteScreen extends React.Component<Props, State> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onVoteSuccess = () => this.setState({hasVoted: true});
|
getTitleCard() {
|
||||||
|
return (
|
||||||
|
<Card style={styles.card}>
|
||||||
|
<Card.Title
|
||||||
|
title={"VOTE"}
|
||||||
|
subtitle={"WHY"}
|
||||||
|
left={(props) => <Avatar.Image
|
||||||
|
{...props}
|
||||||
|
source={ICON_AMICALE}
|
||||||
|
style={styles.icon}
|
||||||
|
/>}
|
||||||
|
/>
|
||||||
|
<Card.Content>
|
||||||
|
<Paragraph>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rhoncus porttitor
|
||||||
|
suscipit. Quisque hendrerit, quam id vestibulum vestibulum, lorem nisi hendrerit nisi, a
|
||||||
|
eleifend sapien diam ut elit. Curabitur sit amet vulputate lectus. Donec semper cursus sapien
|
||||||
|
vel finibus.
|
||||||
|
</Paragraph>
|
||||||
|
<Paragraph>
|
||||||
|
Sed et venenatis turpis. Fusce malesuada magna urna, sed vehicula sem luctus in. Vivamus
|
||||||
|
faucibus vel eros a ultricies. In sed laoreet ante, luctus mattis tellus. Etiam vitae ipsum
|
||||||
|
sagittis, consequat purus sed, blandit risus.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onVoteSelectionChange = (team: string) => {
|
||||||
|
this.setState({selectedTeam: team})
|
||||||
|
};
|
||||||
|
|
||||||
|
onVotePress = () => {
|
||||||
|
this.showVoteDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
voteKeyExtractor = (item: Object) => item.id.toString();
|
||||||
|
|
||||||
|
voteRenderItem = ({item}: Object) => <RadioButton.Item label={item.name} value={item.id.toString()}/>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user has not voted yet, and the votes are open
|
* The user has not voted yet, and the votes are open
|
||||||
*/
|
*/
|
||||||
getVoteCard() {
|
getVoteCard() {
|
||||||
return <VoteSelect teams={this.teams} onVoteSuccess={this.onVoteSuccess} onVoteError={this.reloadData}/>;
|
return (
|
||||||
|
<Card style={styles.card}>
|
||||||
|
<Card.Title
|
||||||
|
title={"VOTE OPEN"}
|
||||||
|
subtitle={"VOTE NOW"}
|
||||||
|
left={(props) => <Avatar.Icon
|
||||||
|
{...props}
|
||||||
|
icon={"alert-decagram"}
|
||||||
|
/>}
|
||||||
|
/>
|
||||||
|
<Card.Content>
|
||||||
|
<RadioButton.Group
|
||||||
|
onValueChange={this.onVoteSelectionChange}
|
||||||
|
value={this.state.selectedTeam}
|
||||||
|
>
|
||||||
|
{/*$FlowFixMe*/}
|
||||||
|
<FlatList
|
||||||
|
data={this.teams}
|
||||||
|
keyExtractor={this.voteKeyExtractor}
|
||||||
|
extraData={this.state.selectedTeam}
|
||||||
|
renderItem={this.voteRenderItem}
|
||||||
|
/>
|
||||||
|
</RadioButton.Group>
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Actions>
|
||||||
|
<Button
|
||||||
|
icon="send"
|
||||||
|
mode="contained"
|
||||||
|
onPress={this.onVotePress}
|
||||||
|
style={{marginLeft: 'auto'}}
|
||||||
|
disabled={this.state.selectedTeam === "none"}
|
||||||
|
>
|
||||||
|
SEND VOTE
|
||||||
|
</Button>
|
||||||
|
</Card.Actions>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sortByVotes = (a: Object, b: Object) => b.votes - a.votes;
|
||||||
|
|
||||||
|
getTotalVotes() {
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i < this.teams.length; i++) {
|
||||||
|
count += this.teams[i].votes;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWinnerId() {
|
||||||
|
return this.teams[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Votes have ended, results can be displayed
|
* Votes have ended, results can be displayed
|
||||||
*/
|
*/
|
||||||
getVoteResultCard() {
|
getVoteResultCard() {
|
||||||
return <VoteResults teams={this.teams}
|
this.totalVotes = this.getTotalVotes();
|
||||||
dateEnd={this.getDateString(this.dates.date_result_end, this.datesString.date_result_end)}/>;
|
this.teams.sort(this.sortByVotes);
|
||||||
|
return (
|
||||||
|
<Card style={styles.card}>
|
||||||
|
<Card.Title
|
||||||
|
title={"RESULTS"}
|
||||||
|
subtitle={"AVAILABLE UNTIL " + this.getDateString(this.dates.date_result_end, this.datesString.date_result_end)}
|
||||||
|
left={(props) => <Avatar.Icon
|
||||||
|
{...props}
|
||||||
|
icon={"podium-gold"}
|
||||||
|
/>}
|
||||||
|
/>
|
||||||
|
<Card.Content>
|
||||||
|
<Subheading>TOTAL VOTES : {this.totalVotes}</Subheading>
|
||||||
|
{/*$FlowFixMe*/}
|
||||||
|
<FlatList
|
||||||
|
data={this.teams}
|
||||||
|
keyExtractor={this.voteKeyExtractor}
|
||||||
|
renderItem={this.resultRenderItem}
|
||||||
|
/>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resultRenderItem = ({item}: Object) => {
|
||||||
|
const isWinner = this.getWinnerId() === item.id;
|
||||||
|
return (
|
||||||
|
<Card style={{
|
||||||
|
marginTop: 10,
|
||||||
|
elevation: isWinner ? 5 : 3,
|
||||||
|
}}>
|
||||||
|
<List.Item
|
||||||
|
title={item.name}
|
||||||
|
description={item.votes + " VOTES"}
|
||||||
|
left={props => isWinner
|
||||||
|
? <List.Icon {...props} icon="trophy" color={this.colors.primary}/>
|
||||||
|
: null}
|
||||||
|
titleStyle={{
|
||||||
|
color: isWinner
|
||||||
|
? this.colors.primary
|
||||||
|
: this.colors.text
|
||||||
|
}}
|
||||||
|
style={{padding: 0}}
|
||||||
|
/>
|
||||||
|
<ProgressBar progress={item.votes / this.totalVotes} color={this.colors.primary}/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vote will open shortly
|
* Vote will open shortly
|
||||||
*/
|
*/
|
||||||
getTeaseVoteCard() {
|
getTeaseVoteCard() {
|
||||||
return <VoteTease startDate={this.getDateString(this.dates.date_begin, this.datesString.date_begin)}/>;
|
return (
|
||||||
|
<Card style={styles.card}>
|
||||||
|
<Card.Title
|
||||||
|
title={"VOTE INCOMING"}
|
||||||
|
subtitle={"GET READY"}
|
||||||
|
left={props => <Avatar.Icon
|
||||||
|
{...props}
|
||||||
|
icon="vote"/>}
|
||||||
|
/>
|
||||||
|
<Card.Content>
|
||||||
|
<Paragraph>
|
||||||
|
VOTE STARTS
|
||||||
|
AT {this.getDateString(this.dates.date_begin, this.datesString.date_begin)}
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Votes have ended, or user has voted waiting for results
|
* Votes have ended waiting for results
|
||||||
*/
|
*/
|
||||||
getWaitVoteCard() {
|
getWaitVoteCard() {
|
||||||
let startDate = null;
|
return (
|
||||||
if (this.dates.date_result_begin !== null)
|
<Card style={styles.card}>
|
||||||
startDate = this.getDateString(this.dates.date_result_begin, this.datesString.date_result_begin);
|
<Card.Title
|
||||||
return <VoteWait startDate={startDate} hasVoted={this.hasVoted || this.state.hasVoted}
|
title={"VOTES HAVE ENDED"}
|
||||||
justVoted={this.state.hasVoted}
|
subtitle={"WAITING FOR RESULTS"}
|
||||||
isVoteRunning={this.isVoteRunning()}/>;
|
left={(props) => <ActivityIndicator {...props}/>}
|
||||||
|
/>
|
||||||
|
<Card.Content>
|
||||||
|
{
|
||||||
|
this.hasVoted
|
||||||
|
? <Paragraph>THX FOR THE VOTE</Paragraph>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this.dates.date_result_begin !== null
|
||||||
|
? <Paragraph>
|
||||||
|
RESULTS AVAILABLE
|
||||||
|
AT {this.getDateString(this.dates.date_result_begin, this.datesString.date_result_begin)}
|
||||||
|
</Paragraph>
|
||||||
|
: <Paragraph>RESULTS AVAILABLE SHORTLY</Paragraph>
|
||||||
|
}
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDateString(date: Date, dateString: string) {
|
||||||
|
if (this.today.getDate() === date.getDate())
|
||||||
|
return getTimeOnlyString(dateString);
|
||||||
|
else
|
||||||
|
return dateString;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
{...this.props}
|
{...this.props}
|
||||||
ref={this.authRef}
|
|
||||||
links={[
|
links={[
|
||||||
{
|
{
|
||||||
link: 'elections/teams',
|
link: 'elections/teams',
|
||||||
|
|
@ -226,3 +439,14 @@ export default class VoteScreen extends React.Component<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
margin: 10,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withTheme(VoteScreen);
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@
|
||||||
"debug": "Debug",
|
"debug": "Debug",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"profile": "Profile",
|
"profile": "Profile"
|
||||||
"vote": "Elections"
|
|
||||||
},
|
},
|
||||||
"sidenav": {
|
"sidenav": {
|
||||||
"divider1": "Student websites",
|
"divider1": "Student websites",
|
||||||
|
|
@ -237,18 +236,15 @@
|
||||||
"whyAccountSub": "What can you do wth an account",
|
"whyAccountSub": "What can you do wth an account",
|
||||||
"whyAccountParagraph": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!",
|
"whyAccountParagraph": "An Amicale account allows you to take part in several activities around campus. You can join a club, or even create your own!",
|
||||||
"whyAccountParagraph2": "Logging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!",
|
"whyAccountParagraph2": "Logging into your Amicale account on the app will allow you to see all available clubs on the campus, vote for the upcoming elections, and more to come!",
|
||||||
"noAccount": "No Account? Go to the Amicale's building during open hours to create one."
|
"noAccount": "No Account? Go to the Amicale's building during open hours to create one.",
|
||||||
},
|
"errors": {
|
||||||
"errors": {
|
"title": "Error!",
|
||||||
"title": "Error!",
|
"connection": "Network error. Please check your internet connection.",
|
||||||
"badCredentials": "Email or password invalid.",
|
"credentials": "Email or password invalid.",
|
||||||
"badToken": "Session expired, please login again.",
|
"saveToken": "Failed to save connection information, please contact support.",
|
||||||
"noConsent": "You did not give your consent for data processing to the Amicale.",
|
"consent": "You did not give your consent for data processing to the Amicale.",
|
||||||
"badInput": "Invalid input. Please try again.",
|
"unknown": "Unknown error, please contact support."
|
||||||
"forbidden": "You do not have access to this data.",
|
}
|
||||||
"connectionError": "Network error. Please check your internet connection.",
|
|
||||||
"serverError": "Server error. Please contact support.",
|
|
||||||
"unknown": "Unknown error. Please contact support."
|
|
||||||
},
|
},
|
||||||
"clubs": {
|
"clubs": {
|
||||||
"clubList": "Club list",
|
"clubList": "Club list",
|
||||||
|
|
@ -258,42 +254,6 @@
|
||||||
"categories": "Categories",
|
"categories": "Categories",
|
||||||
"categoriesFilterMessage": "Click on a category to filter the list"
|
"categoriesFilterMessage": "Click on a category to filter the list"
|
||||||
},
|
},
|
||||||
"voteScreen": {
|
|
||||||
"select": {
|
|
||||||
"title": "Elections open",
|
|
||||||
"subtitle": "Vote now!",
|
|
||||||
"sendButton": "Send Vote",
|
|
||||||
"dialogTitle": "Send Vote?",
|
|
||||||
"dialogTitleLoading": "Sending vote...",
|
|
||||||
"dialogMessage": "Are you sure you want to send your vote? You will not be able to change it."
|
|
||||||
},
|
|
||||||
"tease": {
|
|
||||||
"title": "Elections incoming",
|
|
||||||
"subtitle": "Be ready to vote!",
|
|
||||||
"message" : "Vote start:"
|
|
||||||
},
|
|
||||||
"wait": {
|
|
||||||
"titleSubmitted": "Vote submitted!",
|
|
||||||
"titleEnded": "Votes closed",
|
|
||||||
"subtitle" : "Waiting for results...",
|
|
||||||
"messageSubmitted" : "Vote submitted successfully.",
|
|
||||||
"messageVoted" : "Thank you for your participation.",
|
|
||||||
"messageDate" : "Results available:",
|
|
||||||
"messageDateUndefined" : "Results will be available shortly"
|
|
||||||
},
|
|
||||||
"results": {
|
|
||||||
"title": "Results",
|
|
||||||
"subtitle": "Available until:",
|
|
||||||
"totalVotes" : "Total votes:",
|
|
||||||
"votes" : "votes"
|
|
||||||
},
|
|
||||||
"title": {
|
|
||||||
"title": "The Elections",
|
|
||||||
"subtitle": "Why your vote is important",
|
|
||||||
"paragraph1" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rhoncus porttitor suscipit. Quisque hendrerit, quam id vestibulum vestibulum, lorem nisi hendrerit nisi, a eleifend sapien diam ut elit. Curabitur sit amet vulputate lectus. Donec semper cursus sapienvel finibus.",
|
|
||||||
"paragraph2" : "Sed et venenatis turpis. Fusce malesuada magna urna, sed vehicula sem luctus in. Vivamus faucibus vel eros a ultricies. In sed laoreet ante, luctus mattis tellus. Etiam vitae ipsum sagittis, consequat purus sed, blandit risus."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dialog": {
|
"dialog": {
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@
|
||||||
"debug": "Debug",
|
"debug": "Debug",
|
||||||
"login": "Se Connecter",
|
"login": "Se Connecter",
|
||||||
"logout": "Se Déconnecter",
|
"logout": "Se Déconnecter",
|
||||||
"profile": "Profil",
|
"profile": "Profil"
|
||||||
"vote": "Élections"
|
|
||||||
},
|
},
|
||||||
"sidenav": {
|
"sidenav": {
|
||||||
"divider1": "Sites étudiants",
|
"divider1": "Sites étudiants",
|
||||||
|
|
@ -237,18 +236,15 @@
|
||||||
"whyAccountSub": "Ce que vous pouvez faire avec un compte",
|
"whyAccountSub": "Ce que vous pouvez faire avec un compte",
|
||||||
"whyAccountParagraph": "Un compte Amicale vous donne la possibilité de participer à diverses activités sur le campus. Vous pouvez rejoindre des clubs ou même créer le votre !",
|
"whyAccountParagraph": "Un compte Amicale vous donne la possibilité de participer à diverses activités sur le campus. Vous pouvez rejoindre des clubs ou même créer le votre !",
|
||||||
"whyAccountParagraph2": "Vous connecter à votre compte Amicale sur l'appli vous permettra de voir tous les clubs en activité, de voter pour les prochaines élections, et plus à venir !",
|
"whyAccountParagraph2": "Vous connecter à votre compte Amicale sur l'appli vous permettra de voir tous les clubs en activité, de voter pour les prochaines élections, et plus à venir !",
|
||||||
"noAccount": "Pas de compte ? Passez à l'Amicale pendant une perm pour en créer un."
|
"noAccount": "Pas de compte ? Passez à l'Amicale pendant une perm pour en créer un.",
|
||||||
},
|
"errors": {
|
||||||
"errors": {
|
"title": "Erreur !",
|
||||||
"title": "Erreur !",
|
"connection": "Erreur de réseau. Merci de vérifier votre connexion Internet.",
|
||||||
"badCredentials": "Email ou mot de passe invalide.",
|
"credentials": "Email ou mot de passe invalide.",
|
||||||
"badToken": "Session expirée, merci de vous reconnecter.",
|
"saveToken": "Erreur de sauvegarde des informations de connexion, merci de contacter le support.",
|
||||||
"noConsent": "Vous n'avez pas donné votre consentement pour l'utilisation de vos données personnelles.",
|
"consent": "Vous n'avez pas donné votre consentement pour l'utilisation de vos données personnelles.",
|
||||||
"badInput": "Entrée invalide. Merci de réessayer.",
|
"unknown": "Erreur inconnue, merci de contacter le support."
|
||||||
"forbidden": "Vous n'avez pas accès à cette information.",
|
}
|
||||||
"connectionError": "Erreur de réseau. Merci de vérifier votre connexion Internet.",
|
|
||||||
"serverError": "Erreur de serveur. Merci de contacter le support.",
|
|
||||||
"unknown": "Erreur inconnue. Merci de contacter le support."
|
|
||||||
},
|
},
|
||||||
"clubs": {
|
"clubs": {
|
||||||
"clubList": "Liste des clubs",
|
"clubList": "Liste des clubs",
|
||||||
|
|
@ -258,42 +254,6 @@
|
||||||
"categories": "Catégories",
|
"categories": "Catégories",
|
||||||
"categoriesFilterMessage": "Cliquez sur une catégorie pour filtrer la liste"
|
"categoriesFilterMessage": "Cliquez sur une catégorie pour filtrer la liste"
|
||||||
},
|
},
|
||||||
"voteScreen": {
|
|
||||||
"select": {
|
|
||||||
"title": "Élections ouvertes",
|
|
||||||
"subtitle": "Votez maintenant !",
|
|
||||||
"sendButton": "Envoyer votre vote",
|
|
||||||
"dialogTitle": "Envoyer votre vote ?",
|
|
||||||
"dialogTitleLoading": "Envoi du vote...",
|
|
||||||
"dialogMessage": "Êtes vous sûr de vouloir envoyer votre vote ? Vous ne pourrez plus le changer."
|
|
||||||
},
|
|
||||||
"tease": {
|
|
||||||
"title": "Les élections arrivent",
|
|
||||||
"subtitle": "Préparez vous à voter !",
|
|
||||||
"message" : "Début des votes :"
|
|
||||||
},
|
|
||||||
"wait": {
|
|
||||||
"titleSubmitted": "Vote envoyé !",
|
|
||||||
"titleEnded": "Votes fermés",
|
|
||||||
"subtitle" : "Attente des résultats...",
|
|
||||||
"messageSubmitted" : "Votre vote a bien été envoyé.",
|
|
||||||
"messageVoted" : "Merci pour votre participation.",
|
|
||||||
"messageDate" : "Disponibilité des résultats :",
|
|
||||||
"messageDateUndefined" : "les résultats seront disponibles sous peu."
|
|
||||||
},
|
|
||||||
"results": {
|
|
||||||
"title": "Résultats",
|
|
||||||
"subtitle": "Disponibles jusqu'à :",
|
|
||||||
"totalVotes" : "Nombre total de votes :",
|
|
||||||
"votes" : "votes"
|
|
||||||
},
|
|
||||||
"title": {
|
|
||||||
"title": "Les Élections",
|
|
||||||
"subtitle": "Pourquoi votre vote est important",
|
|
||||||
"paragraph1" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus rhoncus porttitor suscipit. Quisque hendrerit, quam id vestibulum vestibulum, lorem nisi hendrerit nisi, a eleifend sapien diam ut elit. Curabitur sit amet vulputate lectus. Donec semper cursus sapienvel finibus.",
|
|
||||||
"paragraph2" : "Sed et venenatis turpis. Fusce malesuada magna urna, sed vehicula sem luctus in. Vivamus faucibus vel eros a ultricies. In sed laoreet ante, luctus mattis tellus. Etiam vitae ipsum sagittis, consequat purus sed, blandit risus."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dialog": {
|
"dialog": {
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"yes": "Oui",
|
"yes": "Oui",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue