Compare commits

...

5 commits

10 changed files with 523 additions and 201 deletions

View file

@ -65,84 +65,64 @@ test('recoverLogin success saved', () => {
test('isRequestResponseValid', () => {
let json = {
state: true,
error: 0,
data: {}
};
expect(c.isRequestResponseValid(json)).toBeTrue();
expect(c.isResponseValid(json)).toBeTrue();
json = {
state: false,
error: 1,
data: {}
};
expect(c.isRequestResponseValid(json)).toBeTrue();
expect(c.isResponseValid(json)).toBeTrue();
json = {
state: false,
message: 'coucou',
error: 50,
data: {}
};
expect(c.isResponseValid(json)).toBeTrue();
json = {
error: 50,
data: {truc: 'machin'}
};
expect(c.isRequestResponseValid(json)).toBeTrue();
expect(c.isResponseValid(json)).toBeTrue();
json = {
message: 'coucou'
};
expect(c.isRequestResponseValid(json)).toBeFalse();
expect(c.isResponseValid(json)).toBeFalse();
json = {
state: 'coucou'
error: 'coucou',
data: {truc: 'machin'}
};
expect(c.isRequestResponseValid(json)).toBeFalse();
expect(c.isResponseValid(json)).toBeFalse();
json = {
state: true,
error: 0,
data: 'coucou'
};
expect(c.isRequestResponseValid(json)).toBeFalse();
expect(c.isResponseValid(json)).toBeFalse();
json = {
error: 0,
};
expect(c.isResponseValid(json)).toBeFalse();
});
test("isConnectionResponseValid", () => {
let json = {
state: true,
message: 'Connexion confirmée',
token: 'token'
error: 0,
data: {token: 'token'}
};
expect(c.isConnectionResponseValid(json)).toBeTrue();
json = {
state: true,
token: 'token'
error: 2,
data: {}
};
expect(c.isConnectionResponseValid(json)).toBeTrue();
json = {
state: false,
};
expect(c.isConnectionResponseValid(json)).toBeTrue();
json = {
state: false,
message: 'Adresse mail ou mot de passe incorrect',
token: ''
};
expect(c.isConnectionResponseValid(json)).toBeTrue();
json = {
state: true,
message: 'Connexion confirmée',
token: ''
error: 0,
data: {token: ''}
};
expect(c.isConnectionResponseValid(json)).toBeFalse();
json = {
state: true,
message: 'Connexion confirmée',
};
expect(c.isConnectionResponseValid(json)).toBeFalse();
json = {
state: 'coucou',
message: 'Connexion confirmée',
token: 'token'
};
expect(c.isConnectionResponseValid(json)).toBeFalse();
json = {
state: true,
message: 'Connexion confirmée',
token: 2
};
expect(c.isConnectionResponseValid(json)).toBeFalse();
json = {
coucou: 'coucou',
message: 'Connexion confirmée',
token: 'token'
error: 'prout',
data: {token: ''}
};
expect(c.isConnectionResponseValid(json)).toBeFalse();
});
@ -152,10 +132,9 @@ test("connect bad credentials", () => {
return Promise.resolve({
json: () => {
return {
state: false,
message: 'Adresse mail ou mot de passe incorrect',
token: ''
}
error: ERROR_TYPE.BAD_CREDENTIALS,
data: {}
};
},
})
});
@ -168,10 +147,9 @@ test("connect good credentials", () => {
return Promise.resolve({
json: () => {
return {
state: true,
message: 'Connexion confirmée',
token: 'token'
}
error: ERROR_TYPE.SUCCESS,
data: {token: 'token'}
};
},
})
});
@ -186,13 +164,9 @@ test("connect good credentials no consent", () => {
return Promise.resolve({
json: () => {
return {
state: false,
message: 'pas de consent',
token: '',
data: {
consent: false,
}
}
error: ERROR_TYPE.NO_CONSENT,
data: {}
};
},
})
});
@ -205,17 +179,16 @@ test("connect good credentials, fail save token", () => {
return Promise.resolve({
json: () => {
return {
state: true,
message: 'Connexion confirmée',
token: 'token'
}
error: ERROR_TYPE.SUCCESS,
data: {token: 'token'}
};
},
})
});
jest.spyOn(ConnectionManager.prototype, 'saveLogin').mockImplementationOnce(() => {
return Promise.reject(false);
});
return expect(c.connect('email', 'password')).rejects.toBe(ERROR_TYPE.SAVE_TOKEN);
return expect(c.connect('email', 'password')).rejects.toBe(ERROR_TYPE.UNKNOWN);
});
test("connect connection error", () => {
@ -249,7 +222,10 @@ test("authenticatedRequest success", () => {
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
json: () => {
return {state: true, message: 'Connexion vérifiée', data: {coucou: 'toi'}}
return {
error: ERROR_TYPE.SUCCESS,
data: {coucou: 'toi'}
};
},
})
});
@ -264,12 +240,15 @@ test("authenticatedRequest error wrong token", () => {
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
json: () => {
return {state: false, message: 'Le champ token sélectionné est invalide.'}
return {
error: ERROR_TYPE.BAD_TOKEN,
data: {}
};
},
})
});
return expect(c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check'))
.rejects.toBe(ERROR_TYPE.BAD_CREDENTIALS);
.rejects.toBe(ERROR_TYPE.BAD_TOKEN);
});
test("authenticatedRequest error bogus response", () => {
@ -279,7 +258,9 @@ test("authenticatedRequest error bogus response", () => {
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
json: () => {
return {state: true, message: 'Connexion vérifiée'}
return {
error: ERROR_TYPE.SUCCESS,
};
},
})
});
@ -302,13 +283,6 @@ test("authenticatedRequest error no token", () => {
jest.spyOn(ConnectionManager.prototype, 'getToken').mockImplementationOnce(() => {
return null;
});
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
json: () => {
return {state: false, message: 'Le champ token sélectionné est invalide.'}
},
})
});
return expect(c.authenticatedRequest('https://www.amicale-insat.fr/api/token/check'))
.rejects.toBe(ERROR_TYPE.NO_TOKEN);
.rejects.toBe(ERROR_TYPE.UNKNOWN);
});

View file

@ -1 +1,20 @@
rm -rf node_modules/ && rm -f package-lock.json && rm -f yarn.lock && npm cache verify && npm install && expo r -c
#!/bin/bash
echo "Removing node_modules..."
rm -rf node_modules/
echo -e "Done\n"
echo "Removing locks..."
rm -f package-lock.json && rm -f yarn.lock
echo -e "Done\n"
#echo "Verifying npm cache..."
#npm cache verify
#echo -e "Done\n"
echo "Installing dependencies..."
npm install
echo -e "Done\n"
echo "Starting expo with clear cache..."
expo r -c

View file

@ -10,7 +10,7 @@ import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
type Props = {
navigation: Object,
theme: Object,
link: string,
links: Array<{link: string, mandatory: boolean}>,
renderFunction: Function,
}
@ -27,7 +27,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
currentUserToken: string | null;
connectionManager: ConnectionManager;
errorCode: number;
data: Object;
data: Array<Object>;
colors: Object;
constructor(props) {
@ -35,6 +35,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
this.colors = props.theme.colors;
this.connectionManager = ConnectionManager.getInstance();
this.props.navigation.addListener('focus', this.onScreenFocus.bind(this));
this.data = new Array(this.props.links.length);
}
onScreenFocus() {
@ -46,25 +47,53 @@ class AuthenticatedScreen extends React.Component<Props, State> {
if (!this.state.loading)
this.setState({loading: true});
if (this.connectionManager.isLoggedIn()) {
this.connectionManager.authenticatedRequest(this.props.link)
.then((data) => {
this.onFinishedLoading(data, -1);
})
.catch((error) => {
this.onFinishedLoading(undefined, error);
});
for (let i = 0; i < this.props.links.length; i++) {
this.connectionManager.authenticatedRequest(this.props.links[i].link)
.then((data) => {
this.onFinishedLoading(data, i, -1);
})
.catch((error) => {
this.onFinishedLoading(null, i, error);
});
}
} else {
this.onFinishedLoading(undefined, ERROR_TYPE.BAD_CREDENTIALS);
this.onFinishedLoading(null, -1, ERROR_TYPE.BAD_CREDENTIALS);
}
};
onFinishedLoading(data: Object, error: number) {
this.data = data;
onFinishedLoading(data: Object, index: number, error: number) {
if (index >= 0 && index < this.props.links.length)
this.data[index] = data;
this.currentUserToken = data !== undefined
? this.connectionManager.getToken()
: null;
this.errorCode = error;
this.setState({loading: false});
if (this.allRequestsFinished())
this.setState({loading: false});
}
allRequestsFinished() {
let finished = true;
for (let i = 0; i < this.data.length; i++) {
if (this.data[i] === undefined) {
finished = false;
break;
}
}
return finished;
}
allRequestsValid() {
let valid = true;
for (let i = 0; i < this.data.length; i++) {
if (this.data[i] === null && this.props.links[i].mandatory) {
valid = false;
break;
}
}
return valid;
}
getErrorRender() {
@ -75,6 +104,10 @@ class AuthenticatedScreen extends React.Component<Props, State> {
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";
@ -99,7 +132,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
return (
this.state.loading
? <BasicLoadingScreen/>
: (this.data !== undefined
: (this.allRequestsValid()
? this.props.renderFunction(this.data)
: this.getErrorRender())
);

View file

@ -69,6 +69,12 @@ class SideBar extends React.Component<Props, State> {
icon: "account-group",
onlyWhenLoggedIn: true,
},
{
name: "VOTE",
route: "VoteScreen",
icon: "vote",
onlyWhenLoggedIn: true,
},
{
name: i18n.t('screens.logout'),
route: 'disconnect',
@ -161,18 +167,17 @@ class SideBar extends React.Component<Props, State> {
}
onRouteChange = (event) => {
if (event.data.state.routes !== undefined) {
const route = event.data.state.routes[0]; // get the current route (ROOT)
if (route.state !== undefined) {
const state = route.state; // Get the Drawer's state if it exists
// Get the current route name. This will only show Drawer routes.
// Tab routes will be shown as 'Main'
const routeName = state.routeNames[state.index];
if (this.state.activeRoute !== routeName)
this.setState({activeRoute: routeName});
}
try {
const state = event.data.state.routes[0].state; // Get the Drawer's state if it exists
// Get the current route name. This will only show Drawer routes.
// Tab routes will be shown as 'Main'
const routeName = state.routeNames[state.index];
if (this.state.activeRoute !== routeName)
this.setState({activeRoute: routeName});
} catch(e) {
this.setState({activeRoute: 'Main'});
}
};
showDisconnectDialog = () => this.setState({dialogVisible: true});

View file

@ -3,14 +3,35 @@
import * as SecureStore from 'expo-secure-store';
export const ERROR_TYPE = {
BAD_CREDENTIALS: 0,
CONNECTION_ERROR: 1,
SAVE_TOKEN: 2,
NO_TOKEN: 3,
NO_CONSENT: 4,
SUCCESS: 0,
BAD_CREDENTIALS: 1,
BAD_TOKEN: 2,
NO_CONSENT: 3,
BAD_INPUT: 400,
FORBIDDEN: 403,
CONNECTION_ERROR: 404,
SERVER_ERROR: 500,
UNKNOWN: 999,
};
const AUTH_URL = "https://www.amicale-insat.fr/api/password";
type response_format = {
error: number,
data: Object,
}
/**
* champ: error
*
* 0 : SUCCESS -> pas d'erreurs
* 1 : BAD_CREDENTIALS -> email ou mdp invalide
* 2 : BAD_TOKEN -> session expirée
* 3 : NO_CONSENT
* 403 : FORBIDDEN -> accès a la ressource interdit
* 500 : SERVER_ERROR -> pb coté serveur
*/
const API_ENDPOINT = "https://www.amicale-insat.fr/api/";
const AUTH_PATH = "password";
export default class ConnectionManager {
static instance: ConnectionManager | null = null;
@ -110,7 +131,7 @@ export default class ConnectionManager {
password: password,
};
return new Promise((resolve, reject) => {
fetch(AUTH_URL, {
fetch(API_ENDPOINT + AUTH_PATH, {
method: 'POST',
headers: new Headers({
'Accept': 'application/json',
@ -118,22 +139,18 @@ export default class ConnectionManager {
}),
body: JSON.stringify(data)
}).then(async (response) => response.json())
.then((data) => {
if (this.isConnectionResponseValid(data)) {
if (data.state) {
this.saveLogin(email, data.token)
.then((response: response_format) => {
if (this.isConnectionResponseValid(response)) {
if (response.error === ERROR_TYPE.SUCCESS) {
this.saveLogin(email, response.data.token)
.then(() => {
resolve(true);
})
.catch(() => {
reject(ERROR_TYPE.SAVE_TOKEN);
reject(ERROR_TYPE.UNKNOWN);
});
} else if (data.data !== undefined
&& data.data.consent !== undefined
&& !data.data.consent)
reject(ERROR_TYPE.NO_CONSENT);
else
reject(ERROR_TYPE.BAD_CREDENTIALS);
} else
reject(response.error);
} else
reject(ERROR_TYPE.CONNECTION_ERROR);
})
@ -143,35 +160,32 @@ export default class ConnectionManager {
});
}
isRequestResponseValid(response: Object) {
isResponseValid(response: response_format) {
let valid = response !== undefined
&& response.state !== undefined
&& typeof response.state === "boolean";
&& response.error !== undefined
&& typeof response.error === "number";
if (valid && response.state)
valid = valid
&& response.data !== undefined
&& typeof response.data === "object";
valid = valid
&& response.data !== undefined
&& typeof response.data === "object";
return valid;
}
isConnectionResponseValid(response: Object) {
let valid = response !== undefined
&& response.state !== undefined
&& typeof response.state === "boolean";
isConnectionResponseValid(response: response_format) {
let valid = this.isResponseValid(response);
if (valid && response.state)
if (valid && response.error === ERROR_TYPE.SUCCESS)
valid = valid
&& response.token !== undefined
&& response.token !== ''
&& typeof response.token === "string";
&& response.data.token !== undefined
&& response.data.token !== ''
&& typeof response.data.token === "string";
return valid;
}
async authenticatedRequest(url: string) {
async authenticatedRequest(path: string) {
return new Promise((resolve, reject) => {
if (this.getToken() !== null) {
fetch(url, {
fetch(API_ENDPOINT + path, {
method: 'POST',
headers: new Headers({
'Accept': 'application/json',
@ -179,12 +193,13 @@ export default class ConnectionManager {
}),
body: JSON.stringify({token: this.getToken()})
}).then(async (response) => response.json())
.then((data) => {
if (this.isRequestResponseValid(data)) {
if (data.state)
resolve(data.data);
.then((response: response_format) => {
console.log(response);
if (this.isResponseValid(response)) {
if (response.error === ERROR_TYPE.SUCCESS)
resolve(response.data);
else
reject(ERROR_TYPE.BAD_CREDENTIALS);
reject(response.error);
} else
reject(ERROR_TYPE.CONNECTION_ERROR);
})
@ -192,7 +207,7 @@ export default class ConnectionManager {
reject(ERROR_TYPE.CONNECTION_ERROR);
});
} else
reject(ERROR_TYPE.NO_TOKEN);
reject(ERROR_TYPE.UNKNOWN);
});
}
}

View file

@ -20,6 +20,7 @@ import ProfileScreen from "../screens/Amicale/ProfileScreen";
import ClubListScreen from "../screens/Amicale/Clubs/ClubListScreen";
import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
import ClubAboutScreen from "../screens/Amicale/Clubs/ClubAboutScreen";
import VoteScreen from "../screens/Amicale/VoteScreen";
const defaultScreenOptions = {
gestureEnabled: true,
@ -239,6 +240,31 @@ function ProfileStackComponent() {
);
}
const VoteStack = createStackNavigator();
function VoteStackComponent() {
return (
<VoteStack.Navigator
initialRouteName="VoteScreen"
headerMode="float"
screenOptions={defaultScreenOptions}
>
<VoteStack.Screen
name="VoteScreen"
component={VoteScreen}
options={({navigation}) => {
const openDrawer = getDrawerButton.bind(this, navigation);
return {
title: "VoteScreen",
headerLeft: openDrawer
};
}}
/>
</VoteStack.Navigator>
);
}
const ClubStack = createStackNavigator();
function ClubStackComponent() {
@ -341,6 +367,10 @@ export default function DrawerNavigator() {
name="ClubListScreen"
component={ClubStackComponent}
/>
<Drawer.Screen
name="VoteScreen"
component={VoteStackComponent}
/>
</Drawer.Navigator>
);
}

View file

@ -71,7 +71,7 @@ class ClubListScreen extends React.Component<Props, State> {
* @return {*}
*/
getHeaderButtons = () => {
const onPress = () => this.props.navigation.navigate( "ClubAboutScreen");
const onPress = () => this.props.navigation.navigate("ClubAboutScreen");
return <HeaderButton icon={'information'} onPress={onPress}/>;
};
@ -91,11 +91,11 @@ class ClubListScreen extends React.Component<Props, State> {
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
getScreen = (data: Object) => {
this.categories = data.categories;
this.categories = data[0].categories;
return (
//$FlowFixMe
<FlatList
data={data.clubs}
data={data[0].clubs}
keyExtractor={this.keyExtractor}
renderItem={this.getRenderItem}
ListHeaderComponent={this.getListHeader()}
@ -193,7 +193,12 @@ class ClubListScreen extends React.Component<Props, State> {
return (
<AuthenticatedScreen
{...this.props}
link={'https://www.amicale-insat.fr/api/clubs/list'}
links={[
{
link: 'clubs/list',
mandatory: true,
}
]}
renderFunction={this.getScreen}
/>
);

View file

@ -149,18 +149,18 @@ class LoginScreen extends React.Component<Props, State> {
const title = i18n.t("loginScreen.errors.title");
let message;
switch (error) {
case ERROR_TYPE.CONNECTION_ERROR:
message = i18n.t("loginScreen.errors.connection");
break;
case ERROR_TYPE.BAD_CREDENTIALS:
message = i18n.t("loginScreen.errors.credentials");
break;
case ERROR_TYPE.SAVE_TOKEN:
message = i18n.t("loginScreen.errors.saveToken");
break;
case ERROR_TYPE.NO_CONSENT:
message = i18n.t("loginScreen.errors.consent");
break;
case ERROR_TYPE.CONNECTION_ERROR:
message = i18n.t("loginScreen.errors.connection");
break;
case ERROR_TYPE.SERVER_ERROR:
message = "SERVER ERROR"; // TODO translate
break;
default:
message = i18n.t("loginScreen.errors.unknown");
break;
@ -236,13 +236,6 @@ class LoginScreen extends React.Component<Props, State> {
<Card.Content>
{this.getFormInput()}
<Card.Actions>
<Button
icon="help-circle"
mode="contained"
onPress={this.onResetPasswordClick}
style={{marginLeft: 'auto'}}>
{i18n.t("loginScreen.resetPassword")}
</Button>
<Button
icon="send"
mode="contained"
@ -253,6 +246,15 @@ class LoginScreen extends React.Component<Props, State> {
{i18n.t("loginScreen.login")}
</Button>
</Card.Actions>
<Card.Actions>
<Button
icon="help-circle"
mode="contained"
onPress={this.onResetPasswordClick}
style={{marginLeft: 'auto'}}>
{i18n.t("loginScreen.resetPassword")}
</Button>
</Card.Actions>
</Card.Content>
</Card>
);

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import {FlatList, StyleSheet, View} from "react-native";
import {FlatList, ScrollView, StyleSheet} from "react-native";
import {Avatar, Button, Card, Divider, List, withTheme} from 'react-native-paper';
import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
import {openBrowser} from "../../utils/WebBrowser";
@ -28,16 +28,9 @@ class ProfileScreen extends React.Component<Props, State> {
data: Object;
flatListData: Array<Object>;
constructor(props) {
super(props);
this.colors = props.theme.colors;
this.flatListData = [
{id: '0'},
{id: '1'},
{id: '2'},
]
}
componentDidMount() {
@ -47,44 +40,29 @@ class ProfileScreen extends React.Component<Props, State> {
});
}
showDisconnectDialog = () => this.setState({ dialogVisible: true });
showDisconnectDialog = () => this.setState({dialogVisible: true});
hideDisconnectDialog = () => this.setState({ dialogVisible: false });
hideDisconnectDialog = () => this.setState({dialogVisible: false});
getHeaderButtons() {
return <HeaderButton icon={'logout'} onPress={this.showDisconnectDialog}/>;
}
getScreen(data: Object) {
this.data = data;
getScreen = (data: Object) => {
this.data = data[0];
return (
<View>
<FlatList
renderItem={item => this.getRenderItem(item)}
keyExtractor={item => item.id}
data={this.flatListData}
/>
<ScrollView>
{this.getPersonalCard()}
{this.getClubCard()}
{this.getMembershipCar()}
<LogoutDialog
{...this.props}
visible={this.state.dialogVisible}
onDismiss={this.hideDisconnectDialog}
/>
</View>
</ScrollView>
)
}
getRenderItem({item}: Object): any {
switch (item.id) {
case '0':
return this.getPersonalCard();
case '1':
return this.getClubCard();
case '2':
return this.getMembershipCar();
}
}
};
getPersonalCard() {
return (
@ -232,8 +210,13 @@ class ProfileScreen extends React.Component<Props, State> {
return (
<AuthenticatedScreen
{...this.props}
link={'https://www.amicale-insat.fr/api/user/profile'}
renderFunction={(data) => this.getScreen(data)}
links={[
{
link: 'user/profile',
mandatory: true,
}
]}
renderFunction={this.getScreen}
/>
);
}

View file

@ -0,0 +1,256 @@
// @flow
import * as React from 'react';
import {ScrollView, StyleSheet} from "react-native";
import {Avatar, Card, Paragraph, withTheme} from 'react-native-paper';
import AuthenticatedScreen from "../../components/Amicale/AuthenticatedScreen";
import {stringToDate} from "../../utils/Planning";
const ICON_AMICALE = require('../../../assets/amicale.png');
type Props = {
navigation: Object,
theme: Object,
}
const FAKE_DATE = {
"date_begin": "2020-04-06 13:00",
"date_end": "2020-04-06 20:00",
"date_result_begin": "2020-04-06 20:15",
"date_result_end": "2020-04-07 12:00",
};
const FAKE_DATE2 = {
"date_begin": null,
"date_end": null,
"date_result_begin": null,
"date_result_end": null,
};
const FAKE_TEAMS = {
has_voted: false,
teams: [
{
id: 1,
name: "TEST TEAM",
},
],
};
type State = {}
class VoteScreen extends React.Component<Props, State> {
state = {};
colors: Object;
teams: Array<Object> | null;
hasVoted: boolean;
datesString: Object;
dates: Object;
today: Date;
constructor(props) {
super(props);
this.colors = props.theme.colors;
this.hasVoted = false;
this.teams = null;
this.today = new Date();
}
getScreen = (data: Array<Object>) => {
data[0] = FAKE_TEAMS;
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();
console.log(this.teams);
console.log(this.datesString);
console.log(this.dates);
return (
<ScrollView>
{
this.isVoteAvailable()
? this.getContent()
: null
}
{this.getTitleCard()}
</ScrollView>
);
};
generateDateObject() {
this.dates = {
date_begin: stringToDate(this.datesString.date_begin),
date_end: stringToDate(this.datesString.date_end),
date_result_begin: stringToDate(this.datesString.date_result_begin),
date_result_end: stringToDate(this.datesString.date_result_end),
};
}
isVoteAvailable() {
return this.dates.date_begin !== null;
}
isVoteRunning() {
return this.today > this.dates.date_begin && this.today < this.dates.date_end;
}
isVoteStarted() {
return this.today > this.dates.date_begin;
}
isResultRunning() {
return this.today > this.dates.date_result_begin && this.today < this.dates.date_result_end;
}
isResultStarted() {
return this.today > this.dates.date_result_begin;
}
getContent() {
if (!this.isVoteStarted())
return this.getTeaseVoteCard();
else if (this.isVoteRunning() && !this.hasVoted)
return this.getVoteCard();
else if (!this.isResultStarted())
return this.getWaitVoteCard();
else if (this.isResultRunning())
return this.getVoteResultCard();
else
return null;
}
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>
);
}
/**
* The user has not voted yet, and the votes are open
*/
getVoteCard() {
return (
<Card style={styles.card}>
<Card.Title
title={"getVoteCard"}
subtitle={"getVoteCard"}
/>
<Card.Content>
<Paragraph>TEAM1</Paragraph>
<Paragraph>TEAM2</Paragraph>
</Card.Content>
</Card>
);
}
/**
* Votes have ended, results can be displayed
*/
getVoteResultCard() {
return (
<Card style={styles.card}>
<Card.Title
title={"getVoteResultCard"}
subtitle={"getVoteResultCard"}
/>
<Card.Content>
<Paragraph>TEAM1</Paragraph>
<Paragraph>TEAM2</Paragraph>
</Card.Content>
</Card>
);
}
/**
* Vote will open shortly
*/
getTeaseVoteCard() {
return (
<Card style={styles.card}>
<Card.Title
title={"getTeaseVoteCard"}
subtitle={"getTeaseVoteCard"}
/>
<Card.Content>
</Card.Content>
</Card>
);
}
/**
* User has voted, waiting for results
*/
getWaitVoteCard() {
return (
<Card style={styles.card}>
<Card.Title
title={"getWaitVoteCard"}
subtitle={"getWaitVoteCard"}
/>
<Card.Content>
</Card.Content>
</Card>
);
}
render() {
return (
<AuthenticatedScreen
{...this.props}
links={[
{
link: 'elections/teams',
mandatory: false,
},
{
link: 'elections/datesString',
mandatory: false,
},
]}
renderFunction={this.getScreen}
/>
);
}
}
const styles = StyleSheet.create({
card: {
margin: 10,
},
icon: {
backgroundColor: 'transparent'
},
});
export default withTheme(VoteScreen);