Compare commits

..

No commits in common. "db967006933c60346261b49e669c89608453b8ab" and "dc3a49ee6cd85efb10393f04ea700db2d34a101e" have entirely different histories.

10 changed files with 207 additions and 529 deletions

View file

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

View file

@ -1,20 +1 @@
#!/bin/bash rm -rf node_modules/ && rm -f package-lock.json && rm -f yarn.lock && npm cache verify && npm install && expo r -c
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 = { type Props = {
navigation: Object, navigation: Object,
theme: Object, theme: Object,
links: Array<{link: string, mandatory: boolean}>, link: string,
renderFunction: Function, renderFunction: Function,
} }
@ -27,7 +27,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
currentUserToken: string | null; currentUserToken: string | null;
connectionManager: ConnectionManager; connectionManager: ConnectionManager;
errorCode: number; errorCode: number;
data: Array<Object>; data: Object;
colors: Object; colors: Object;
constructor(props) { constructor(props) {
@ -35,7 +35,6 @@ class AuthenticatedScreen extends React.Component<Props, State> {
this.colors = props.theme.colors; this.colors = props.theme.colors;
this.connectionManager = ConnectionManager.getInstance(); this.connectionManager = ConnectionManager.getInstance();
this.props.navigation.addListener('focus', this.onScreenFocus.bind(this)); this.props.navigation.addListener('focus', this.onScreenFocus.bind(this));
this.data = new Array(this.props.links.length);
} }
onScreenFocus() { onScreenFocus() {
@ -47,55 +46,27 @@ class AuthenticatedScreen extends React.Component<Props, State> {
if (!this.state.loading) if (!this.state.loading)
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++) { this.connectionManager.authenticatedRequest(this.props.link)
this.connectionManager.authenticatedRequest(this.props.links[i].link)
.then((data) => { .then((data) => {
this.onFinishedLoading(data, i, -1); this.onFinishedLoading(data, -1);
}) })
.catch((error) => { .catch((error) => {
this.onFinishedLoading(null, i, error); this.onFinishedLoading(undefined, error);
}); });
}
} else { } else {
this.onFinishedLoading(null, -1, ERROR_TYPE.BAD_CREDENTIALS); this.onFinishedLoading(undefined, ERROR_TYPE.BAD_CREDENTIALS);
} }
}; };
onFinishedLoading(data: Object, index: number, error: number) { onFinishedLoading(data: Object, error: number) {
if (index >= 0 && index < this.props.links.length) this.data = data;
this.data[index] = data;
this.currentUserToken = data !== undefined this.currentUserToken = data !== undefined
? this.connectionManager.getToken() ? this.connectionManager.getToken()
: null; : null;
this.errorCode = error; this.errorCode = error;
if (this.allRequestsFinished())
this.setState({loading: false}); 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() { getErrorRender() {
let message; let message;
let icon; let icon;
@ -104,10 +75,6 @@ class AuthenticatedScreen extends React.Component<Props, State> {
message = i18n.t("loginScreen.errors.credentials"); message = i18n.t("loginScreen.errors.credentials");
icon = "account-alert-outline"; icon = "account-alert-outline";
break; break;
case ERROR_TYPE.BAD_TOKEN:
message = "BAD TOKEN"; // TODO translate
icon = "access-point-network-off";
break;
case ERROR_TYPE.CONNECTION_ERROR: case ERROR_TYPE.CONNECTION_ERROR:
message = i18n.t("loginScreen.errors.connection"); message = i18n.t("loginScreen.errors.connection");
icon = "access-point-network-off"; icon = "access-point-network-off";
@ -132,7 +99,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
return ( return (
this.state.loading this.state.loading
? <BasicLoadingScreen/> ? <BasicLoadingScreen/>
: (this.allRequestsValid() : (this.data !== undefined
? this.props.renderFunction(this.data) ? this.props.renderFunction(this.data)
: this.getErrorRender()) : this.getErrorRender())
); );

View file

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

View file

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

View file

@ -20,7 +20,6 @@ import ProfileScreen from "../screens/Amicale/ProfileScreen";
import ClubListScreen from "../screens/Amicale/Clubs/ClubListScreen"; import ClubListScreen from "../screens/Amicale/Clubs/ClubListScreen";
import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen"; import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
import ClubAboutScreen from "../screens/Amicale/Clubs/ClubAboutScreen"; import ClubAboutScreen from "../screens/Amicale/Clubs/ClubAboutScreen";
import VoteScreen from "../screens/Amicale/VoteScreen";
const defaultScreenOptions = { const defaultScreenOptions = {
gestureEnabled: true, gestureEnabled: true,
@ -240,31 +239,6 @@ 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(); const ClubStack = createStackNavigator();
function ClubStackComponent() { function ClubStackComponent() {
@ -367,10 +341,6 @@ export default function DrawerNavigator() {
name="ClubListScreen" name="ClubListScreen"
component={ClubStackComponent} component={ClubStackComponent}
/> />
<Drawer.Screen
name="VoteScreen"
component={VoteStackComponent}
/>
</Drawer.Navigator> </Drawer.Navigator>
); );
} }

View file

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

View file

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

View file

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

View file

@ -1,256 +0,0 @@
// @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);