forked from vergnet/application-amicale
Improved error handling and added new api endpoints
This commit is contained in:
parent
3fe6be4238
commit
0ba48adb6e
13 changed files with 219 additions and 211 deletions
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ConnectionManager, {ERROR_TYPE} from "../../src/managers/ConnectionManager";
|
import ConnectionManager from "../../src/managers/ConnectionManager";
|
||||||
|
import {ERROR_TYPE} from "../../src/utils/WebData";
|
||||||
import * as SecureStore from 'expo-secure-store';
|
import * as SecureStore from 'expo-secure-store';
|
||||||
|
|
||||||
let fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
|
let fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
|
||||||
|
@ -63,47 +64,6 @@ test('recoverLogin success saved', () => {
|
||||||
return expect(c.recoverLogin()).resolves.toBe('token2');
|
return expect(c.recoverLogin()).resolves.toBe('token2');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('isRequestResponseValid', () => {
|
|
||||||
let json = {
|
|
||||||
error: 0,
|
|
||||||
data: {}
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeTrue();
|
|
||||||
json = {
|
|
||||||
error: 1,
|
|
||||||
data: {}
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeTrue();
|
|
||||||
json = {
|
|
||||||
error: 50,
|
|
||||||
data: {}
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeTrue();
|
|
||||||
json = {
|
|
||||||
error: 50,
|
|
||||||
data: {truc: 'machin'}
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeTrue();
|
|
||||||
json = {
|
|
||||||
message: 'coucou'
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeFalse();
|
|
||||||
json = {
|
|
||||||
error: 'coucou',
|
|
||||||
data: {truc: 'machin'}
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeFalse();
|
|
||||||
json = {
|
|
||||||
error: 0,
|
|
||||||
data: 'coucou'
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeFalse();
|
|
||||||
json = {
|
|
||||||
error: 0,
|
|
||||||
};
|
|
||||||
expect(c.isResponseValid(json)).toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("isConnectionResponseValid", () => {
|
test("isConnectionResponseValid", () => {
|
||||||
let json = {
|
let json = {
|
||||||
error: 0,
|
error: 0,
|
||||||
|
|
45
__tests__/utils/WebData.js
Normal file
45
__tests__/utils/WebData.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {isResponseValid} from "../../src/utils/WebData";
|
||||||
|
|
||||||
|
let fetch = require('isomorphic-fetch'); // fetch is not implemented in nodeJS but in react-native
|
||||||
|
|
||||||
|
test('isRequestResponseValid', () => {
|
||||||
|
let json = {
|
||||||
|
error: 0,
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeTrue();
|
||||||
|
json = {
|
||||||
|
error: 1,
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeTrue();
|
||||||
|
json = {
|
||||||
|
error: 50,
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeTrue();
|
||||||
|
json = {
|
||||||
|
error: 50,
|
||||||
|
data: {truc: 'machin'}
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeTrue();
|
||||||
|
json = {
|
||||||
|
message: 'coucou'
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeFalse();
|
||||||
|
json = {
|
||||||
|
error: 'coucou',
|
||||||
|
data: {truc: 'machin'}
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeFalse();
|
||||||
|
json = {
|
||||||
|
error: 0,
|
||||||
|
data: 'coucou'
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeFalse();
|
||||||
|
json = {
|
||||||
|
error: 0,
|
||||||
|
};
|
||||||
|
expect(isResponseValid(json)).toBeFalse();
|
||||||
|
});
|
|
@ -1,13 +1,18 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import ConnectionManager, {ERROR_TYPE} from "../../managers/ConnectionManager";
|
import ConnectionManager from "../../managers/ConnectionManager";
|
||||||
|
import {ERROR_TYPE} from "../../utils/WebData";
|
||||||
import ErrorView from "../Custom/ErrorView";
|
import ErrorView from "../Custom/ErrorView";
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
links: Array<{link: string, mandatory: boolean}>,
|
requests: Array<{
|
||||||
|
link: string,
|
||||||
|
params: Object,
|
||||||
|
mandatory: boolean
|
||||||
|
}>,
|
||||||
renderFunction: Function,
|
renderFunction: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,15 +35,15 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
super(props);
|
super(props);
|
||||||
this.connectionManager = ConnectionManager.getInstance();
|
this.connectionManager = ConnectionManager.getInstance();
|
||||||
this.props.navigation.addListener('focus', this.onScreenFocus);
|
this.props.navigation.addListener('focus', this.onScreenFocus);
|
||||||
this.fetchedData = new Array(this.props.links.length);
|
this.fetchedData = new Array(this.props.requests.length);
|
||||||
this.errors = new Array(this.props.links.length);
|
this.errors = new Array(this.props.requests.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes screen if user changed
|
* Refreshes screen if user changed
|
||||||
*/
|
*/
|
||||||
onScreenFocus = () => {
|
onScreenFocus = () => {
|
||||||
if (this.currentUserToken !== this.connectionManager.getToken()){
|
if (this.currentUserToken !== this.connectionManager.getToken()) {
|
||||||
this.currentUserToken = this.connectionManager.getToken();
|
this.currentUserToken = this.connectionManager.getToken();
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
}
|
}
|
||||||
|
@ -55,8 +60,10 @@ 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++) {
|
for (let i = 0; i < this.props.requests.length; i++) {
|
||||||
this.connectionManager.authenticatedRequest(this.props.links[i].link, null, null)
|
this.connectionManager.authenticatedRequest(
|
||||||
|
this.props.requests[i].link,
|
||||||
|
this.props.requests[i].params)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.onRequestFinished(data, i, -1);
|
this.onRequestFinished(data, i, -1);
|
||||||
})
|
})
|
||||||
|
@ -65,7 +72,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < this.props.links.length; i++) {
|
for (let i = 0; i < this.props.requests.length; i++) {
|
||||||
this.onRequestFinished(null, i, ERROR_TYPE.BAD_TOKEN);
|
this.onRequestFinished(null, i, ERROR_TYPE.BAD_TOKEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +89,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
* @param error The error code received
|
* @param error The error code received
|
||||||
*/
|
*/
|
||||||
onRequestFinished(data: Object | null, index: number, error: number) {
|
onRequestFinished(data: Object | null, index: number, error: number) {
|
||||||
if (index >= 0 && index < this.props.links.length){
|
if (index >= 0 && index < this.props.requests.length) {
|
||||||
this.fetchedData[index] = data;
|
this.fetchedData[index] = data;
|
||||||
this.errors[index] = error;
|
this.errors[index] = error;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +127,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
allRequestsValid() {
|
allRequestsValid() {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
for (let i = 0; i < this.fetchedData.length; i++) {
|
for (let i = 0; i < this.fetchedData.length; i++) {
|
||||||
if (this.fetchedData[i] === null && this.props.links[i].mandatory) {
|
if (this.fetchedData[i] === null && this.props.requests[i].mandatory) {
|
||||||
valid = false;
|
valid = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +144,7 @@ class AuthenticatedScreen extends React.Component<Props, State> {
|
||||||
*/
|
*/
|
||||||
getError() {
|
getError() {
|
||||||
for (let i = 0; i < this.errors.length; i++) {
|
for (let i = 0; i < this.errors.length; i++) {
|
||||||
if (this.errors[i] !== 0 && this.props.links[i].mandatory) {
|
if (this.errors[i] !== 0 && this.props.requests[i].mandatory) {
|
||||||
return this.errors[i];
|
return this.errors[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,7 @@ export default class VoteSelect extends React.PureComponent<Props, State> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
ConnectionManager.getInstance().authenticatedRequest(
|
ConnectionManager.getInstance().authenticatedRequest(
|
||||||
"elections/vote",
|
"elections/vote",
|
||||||
["vote"],
|
{"vote": parseInt(this.state.selectedTeam)})
|
||||||
[parseInt(this.state.selectedTeam)])
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.onVoteDialogDismiss();
|
this.onVoteDialogDismiss();
|
||||||
this.props.onVoteSuccess();
|
this.props.onVoteSuccess();
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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";
|
import {ERROR_TYPE} from "../../utils/WebData";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {readData} from "../../utils/WebData";
|
import {ERROR_TYPE, 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 ErrorView from "../Custom/ErrorView";
|
||||||
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
import BasicLoadingScreen from "../Custom/BasicLoadingScreen";
|
||||||
import {ERROR_TYPE} from "../../managers/ConnectionManager";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
|
|
@ -1,23 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as SecureStore from 'expo-secure-store';
|
import * as SecureStore from 'expo-secure-store';
|
||||||
|
import {apiRequest, ERROR_TYPE, isResponseValid} from "../utils/WebData";
|
||||||
export const ERROR_TYPE = {
|
|
||||||
SUCCESS: 0,
|
|
||||||
BAD_CREDENTIALS: 1,
|
|
||||||
BAD_TOKEN: 2,
|
|
||||||
NO_CONSENT: 3,
|
|
||||||
BAD_INPUT: 400,
|
|
||||||
FORBIDDEN: 403,
|
|
||||||
CONNECTION_ERROR: 404,
|
|
||||||
SERVER_ERROR: 500,
|
|
||||||
UNKNOWN: 999,
|
|
||||||
};
|
|
||||||
|
|
||||||
type response_format = {
|
|
||||||
error: number,
|
|
||||||
data: Object,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* champ: error
|
* champ: error
|
||||||
|
@ -30,7 +14,7 @@ type response_format = {
|
||||||
* 500 : SERVER_ERROR -> pb coté serveur
|
* 500 : SERVER_ERROR -> pb coté serveur
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const API_ENDPOINT = "https://www.amicale-insat.fr/api/";
|
|
||||||
const AUTH_PATH = "password";
|
const AUTH_PATH = "password";
|
||||||
|
|
||||||
export default class ConnectionManager {
|
export default class ConnectionManager {
|
||||||
|
@ -126,53 +110,27 @@ export default class ConnectionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(email: string, password: string) {
|
async connect(email: string, password: string) {
|
||||||
let data = {
|
return new Promise((resolve, reject) => {
|
||||||
|
const data = {
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
};
|
};
|
||||||
return new Promise((resolve, reject) => {
|
apiRequest(AUTH_PATH, 'POST', data)
|
||||||
fetch(API_ENDPOINT + AUTH_PATH, {
|
.then((response) => {
|
||||||
method: 'POST',
|
this.saveLogin(email, response.token)
|
||||||
headers: new Headers({
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}),
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
}).then(async (response) => response.json())
|
|
||||||
.then((response: response_format) => {
|
|
||||||
if (this.isConnectionResponseValid(response)) {
|
|
||||||
if (response.error === ERROR_TYPE.SUCCESS) {
|
|
||||||
this.saveLogin(email, response.data.token)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
reject(ERROR_TYPE.UNKNOWN);
|
reject(ERROR_TYPE.UNKNOWN);
|
||||||
});
|
});
|
||||||
} else
|
|
||||||
reject(response.error);
|
|
||||||
} else
|
|
||||||
reject(ERROR_TYPE.CONNECTION_ERROR);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => reject(error));
|
||||||
reject(ERROR_TYPE.CONNECTION_ERROR);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isResponseValid(response: response_format) {
|
isConnectionResponseValid(response: Object) {
|
||||||
let valid = response !== undefined
|
let valid = isResponseValid(response);
|
||||||
&& response.error !== undefined
|
|
||||||
&& typeof response.error === "number";
|
|
||||||
|
|
||||||
valid = valid
|
|
||||||
&& response.data !== undefined
|
|
||||||
&& typeof response.data === "object";
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
isConnectionResponseValid(response: response_format) {
|
|
||||||
let valid = this.isResponseValid(response);
|
|
||||||
|
|
||||||
if (valid && response.error === ERROR_TYPE.SUCCESS)
|
if (valid && response.error === ERROR_TYPE.SUCCESS)
|
||||||
valid = valid
|
valid = valid
|
||||||
|
@ -182,45 +140,17 @@ export default class ConnectionManager {
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
generatePostArguments(keys: Array<string>, values: Array<string>) {
|
async authenticatedRequest(path: string, params: Object) {
|
||||||
let data = {};
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
data[keys[i]] = values[i];
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async authenticatedRequest(path: string, keys: Array<string>|null, values: Array<any>|null) {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (this.getToken() !== null) {
|
if (this.getToken() !== null) {
|
||||||
let data = {};
|
|
||||||
if (keys !== null && values !== null && keys.length === values.length)
|
|
||||||
data = this.generatePostArguments(keys, values);
|
|
||||||
// console.log(data);
|
// console.log(data);
|
||||||
fetch(API_ENDPOINT + path, {
|
let data = {
|
||||||
method: 'POST',
|
|
||||||
headers: new Headers({
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}),
|
|
||||||
body: JSON.stringify({
|
|
||||||
token: this.getToken(),
|
token: this.getToken(),
|
||||||
...data
|
...params
|
||||||
})
|
};
|
||||||
}).then(async (response) => response.json())
|
apiRequest(path, 'POST', data)
|
||||||
.then((response: response_format) => {
|
.then((response) => resolve(response))
|
||||||
// console.log(response);
|
.catch((error) => reject(error));
|
||||||
if (this.isResponseValid(response)) {
|
|
||||||
if (response.error === ERROR_TYPE.SUCCESS)
|
|
||||||
resolve(response.data);
|
|
||||||
else
|
|
||||||
reject(response.error);
|
|
||||||
} else
|
|
||||||
reject(ERROR_TYPE.CONNECTION_ERROR);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
reject(ERROR_TYPE.CONNECTION_ERROR);
|
|
||||||
});
|
|
||||||
} else
|
} else
|
||||||
reject(ERROR_TYPE.UNKNOWN);
|
reject(ERROR_TYPE.UNKNOWN);
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,22 +22,6 @@ function openWebLink(event, link) {
|
||||||
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
|
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
|
||||||
}
|
}
|
||||||
|
|
||||||
const FakeClub = {
|
|
||||||
"category": [
|
|
||||||
3,
|
|
||||||
6,
|
|
||||||
],
|
|
||||||
"description": "<p class=\"ql-align-justify\">Les 100 Tours de l’INSA reviennent en force pour une cinquième édition les 5 et 6 juin prochain !</p><p class=\"ql-align-justify\"><br></p><p class=\"ql-align-justify\">Prépare-toi pour le plus gros évènement de l’année sur le campus de notre belle école qui nous réunit tous autour d’activités folles pour fêter la fin de l’année dans la bonne humeur !</p><p class=\"ql-align-justify\">L’éco-festival tournera autour d’une grande course par équipe qui nous vaut ce doux nom de 100 tours. Ce sera le moment de défier tes potes pour tenter de remporter de nombreux lots, et surtout l’admiration de tous. Mais cela ne s’arrête pas là, puisque tu pourras aussi participer à des activités à sensation, divers ateliers, et de quoi chiller avec tes potes en écoutant de la bonne musique. Tu pourras ensuite enchaîner sur LA soirée de l’année, rythmée par des artistes sur-motivés !</p><p class=\"ql-align-justify\"><br></p><p class=\"ql-align-justify\">Tu es bien entendu le bienvenu si l’envie te prend de rejoindre l’équipe et de nous aider à organiser cet évènement du turfu !</p><p class=\"ql-align-justify\"><br></p><p class=\"ql-align-justify\">La team 100 Tours</p><p class=\"ql-align-justify\"><br></p><p>Contact : <a href=\"mailto:100Tours@amicale-insat.fr\" target=\"_blank\">100tours@amicale-insat.fr</a></p><p>Facebook : Les 100 Tours de l’INSA</p><p>Instagram : 100tours.insatoulouse</p>",
|
|
||||||
"id": 110,
|
|
||||||
"logo": "https://www.amicale-insat.fr/storage/clubLogos/2cca8885dd3bdf902124f038b548962b.jpeg",
|
|
||||||
"name": "100 Tours",
|
|
||||||
"responsibles": [
|
|
||||||
"Juliette Duval",
|
|
||||||
"Emilie Cuminal",
|
|
||||||
"Maxime Doré",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining a club event information page.
|
* Class defining a club event information page.
|
||||||
* If called with data and categories navigation parameters, will use those to display the data.
|
* If called with data and categories navigation parameters, will use those to display the data.
|
||||||
|
@ -132,11 +116,8 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
this.props.navigation.setOptions({title: data.name})
|
this.props.navigation.setOptions({title: data.name})
|
||||||
}
|
}
|
||||||
|
|
||||||
getScreen = (data: Object) => {
|
getScreen = (response: Array<Object>) => {
|
||||||
console.log('fetchedData passed to screen:');
|
let data = response[0];
|
||||||
console.log(data);
|
|
||||||
console.log("Using fake data");
|
|
||||||
data = FakeClub;
|
|
||||||
this.updateHeaderTitle(data);
|
this.updateHeaderTitle(data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -182,16 +163,17 @@ class ClubDisplayScreen extends React.Component<Props, State> {
|
||||||
if (this.shouldFetchData)
|
if (this.shouldFetchData)
|
||||||
return <AuthenticatedScreen
|
return <AuthenticatedScreen
|
||||||
{...this.props}
|
{...this.props}
|
||||||
links={[
|
requests={[
|
||||||
{
|
{
|
||||||
link: 'clubs/' + this.clubId,
|
link: 'clubs/info',
|
||||||
mandatory: true,
|
params: {'id': this.clubId},
|
||||||
|
mandatory: true
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
renderFunction={this.getScreen}
|
renderFunction={this.getScreen}
|
||||||
/>;
|
/>;
|
||||||
else
|
else
|
||||||
return this.getScreen(this.displayData);
|
return this.getScreen([this.displayData]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -193,9 +193,10 @@ class ClubListScreen extends React.Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
{...this.props}
|
{...this.props}
|
||||||
links={[
|
requests={[
|
||||||
{
|
{
|
||||||
link: 'clubs/list',
|
link: 'clubs/list',
|
||||||
|
params: {},
|
||||||
mandatory: true,
|
mandatory: true,
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -265,11 +265,10 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
clubListItem = ({item}: Object) => {
|
clubListItem = ({item}: Object) => {
|
||||||
const onPress = () => this.openClubDetailsScreen(0); // TODO get club id
|
const onPress = () => this.openClubDetailsScreen(item.id);
|
||||||
const isManager = false; // TODO detect if manager
|
|
||||||
let description = i18n.t("profileScreen.isMember");
|
let description = i18n.t("profileScreen.isMember");
|
||||||
let icon = (props) => <List.Icon {...props} icon="chevron-right"/>;
|
let icon = (props) => <List.Icon {...props} icon="chevron-right"/>;
|
||||||
if (isManager) {
|
if (item.is_manager) {
|
||||||
description = i18n.t("profileScreen.isManager");
|
description = i18n.t("profileScreen.isManager");
|
||||||
icon = (props) => <List.Icon {...props} icon="star" color={this.colors.primary}/>;
|
icon = (props) => <List.Icon {...props} icon="star" color={this.colors.primary}/>;
|
||||||
}
|
}
|
||||||
|
@ -289,17 +288,13 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
* @param list The club list
|
* @param list The club list
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
getClubList(list: Array<string>) {
|
getClubList(list: Array<Object>) {
|
||||||
let dataset = [];
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
dataset.push({name: list[i]});
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
//$FlowFixMe
|
//$FlowFixMe
|
||||||
<FlatList
|
<FlatList
|
||||||
renderItem={this.clubListItem}
|
renderItem={this.clubListItem}
|
||||||
keyExtractor={this.clubKeyExtractor}
|
keyExtractor={this.clubKeyExtractor}
|
||||||
data={dataset}
|
data={list}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -308,9 +303,10 @@ class ProfileScreen extends React.Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
{...this.props}
|
{...this.props}
|
||||||
links={[
|
requests={[
|
||||||
{
|
{
|
||||||
link: 'user/profile',
|
link: 'user/profile',
|
||||||
|
params: {},
|
||||||
mandatory: true,
|
mandatory: true,
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -216,13 +216,15 @@ export default class VoteScreen extends React.Component<Props, State> {
|
||||||
<AuthenticatedScreen
|
<AuthenticatedScreen
|
||||||
{...this.props}
|
{...this.props}
|
||||||
ref={this.authRef}
|
ref={this.authRef}
|
||||||
links={[
|
requests={[
|
||||||
{
|
{
|
||||||
link: 'elections/teams',
|
link: 'elections/teams',
|
||||||
|
params: {},
|
||||||
mandatory: false,
|
mandatory: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
link: 'elections/datesString',
|
link: 'elections/datesString',
|
||||||
|
params: {},
|
||||||
mandatory: false,
|
mandatory: false,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
|
||||||
import {Card, withTheme} from 'react-native-paper';
|
import {Card, withTheme} from 'react-native-paper';
|
||||||
import DateManager from "../../managers/DateManager";
|
import DateManager from "../../managers/DateManager";
|
||||||
import ImageModal from 'react-native-image-modal';
|
import ImageModal from 'react-native-image-modal';
|
||||||
|
import BasicLoadingScreen from "../../components/Custom/BasicLoadingScreen";
|
||||||
|
import {apiRequest} from "../../utils/WebData";
|
||||||
|
import ErrorView from "../../components/Custom/ErrorView";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
navigation: Object,
|
navigation: Object,
|
||||||
|
@ -15,23 +18,14 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
loading: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
function openWebLink(event, link) {
|
function openWebLink(event, link) {
|
||||||
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
|
Linking.openURL(link).catch((err) => console.error('Error opening link', err));
|
||||||
}
|
}
|
||||||
|
|
||||||
const FAKE_EVENT = {
|
const CLUB_INFO_PATH = "event/info";
|
||||||
"id": 142,
|
|
||||||
"title": "Soir\u00e9e Impact'INSA",
|
|
||||||
"logo": null,
|
|
||||||
"date_begin": "2020-04-22 19:00",
|
|
||||||
"date_end": "2020-04-22 00:00",
|
|
||||||
"description": "<p>R\u00e9servation salle de boom + PK pour la soir\u00e9e Impact'Insa<\/p>",
|
|
||||||
"club": "Impact Insa",
|
|
||||||
"category_id": 10,
|
|
||||||
"url": "https:\/\/www.amicale-insat.fr\/event\/142\/view"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining a planning event information page.
|
* Class defining a planning event information page.
|
||||||
|
@ -41,31 +35,55 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
|
||||||
displayData: Object;
|
displayData: Object;
|
||||||
shouldFetchData: boolean;
|
shouldFetchData: boolean;
|
||||||
eventId: number;
|
eventId: number;
|
||||||
|
errorCode: number;
|
||||||
|
|
||||||
colors: Object;
|
colors: Object;
|
||||||
|
|
||||||
state = {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.colors = props.theme.colors;
|
this.colors = props.theme.colors;
|
||||||
|
|
||||||
if (this.props.route.params.data !== undefined) {
|
if (this.props.route.params.data !== undefined) {
|
||||||
this.displayData = this.props.route.params.data;
|
this.displayData = this.props.route.params.data;
|
||||||
this.eventId = this.props.route.params.data.eventId;
|
console.log(this.displayData);
|
||||||
|
this.eventId = this.displayData.id;
|
||||||
this.shouldFetchData = false;
|
this.shouldFetchData = false;
|
||||||
|
this.errorCode = 0;
|
||||||
|
this.state = {
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.displayData = FAKE_EVENT;
|
this.displayData = null;
|
||||||
this.eventId = this.props.route.params.eventId;
|
this.eventId = this.props.route.params.eventId;
|
||||||
this.shouldFetchData = true;
|
this.shouldFetchData = true;
|
||||||
console.log(this.eventId);
|
this.errorCode = 0;
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
};
|
||||||
|
this.fetchData();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
fetchData = () => {
|
||||||
// console.log("rendering planningDisplayScreen");
|
this.setState({loading: true});
|
||||||
|
apiRequest(CLUB_INFO_PATH, 'POST', {id: this.eventId})
|
||||||
|
.then(this.onFetchSuccess)
|
||||||
|
.catch(this.onFetchError);
|
||||||
|
};
|
||||||
|
|
||||||
|
onFetchSuccess = (data: Object) => {
|
||||||
|
this.displayData = data;
|
||||||
|
console.log(this.displayData);
|
||||||
|
this.setState({loading: false});
|
||||||
|
};
|
||||||
|
|
||||||
|
onFetchError = (error: number) => {
|
||||||
|
this.errorCode = error;
|
||||||
|
this.setState({loading: false});
|
||||||
|
};
|
||||||
|
|
||||||
|
getContent() {
|
||||||
let subtitle = getFormattedEventTime(
|
let subtitle = getFormattedEventTime(
|
||||||
this.displayData["date_begin"], this.displayData["date_end"]);
|
this.displayData["date_begin"], this.displayData["date_end"]);
|
||||||
let dateString = getDateOnlyString(this.displayData["date_begin"]);
|
let dateString = getDateOnlyString(this.displayData["date_begin"]);
|
||||||
|
@ -106,6 +124,15 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.loading)
|
||||||
|
return <BasicLoadingScreen/>;
|
||||||
|
else if (this.errorCode === 0)
|
||||||
|
return this.getContent();
|
||||||
|
else
|
||||||
|
return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(PlanningDisplayScreen);
|
export default withTheme(PlanningDisplayScreen);
|
||||||
|
|
|
@ -1,5 +1,65 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
export const ERROR_TYPE = {
|
||||||
|
SUCCESS: 0,
|
||||||
|
BAD_CREDENTIALS: 1,
|
||||||
|
BAD_TOKEN: 2,
|
||||||
|
NO_CONSENT: 3,
|
||||||
|
BAD_INPUT: 400,
|
||||||
|
FORBIDDEN: 403,
|
||||||
|
CONNECTION_ERROR: 404,
|
||||||
|
SERVER_ERROR: 500,
|
||||||
|
UNKNOWN: 999,
|
||||||
|
};
|
||||||
|
|
||||||
|
type response_format = {
|
||||||
|
error: number,
|
||||||
|
data: Object,
|
||||||
|
}
|
||||||
|
|
||||||
|
const API_ENDPOINT = "https://www.amicale-insat.fr/api/";
|
||||||
|
|
||||||
|
export async function apiRequest(path: string, method: string, params: ?Object) {
|
||||||
|
if (params === undefined || params === null)
|
||||||
|
params = {};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch(API_ENDPOINT + path, {
|
||||||
|
method: method,
|
||||||
|
headers: new Headers({
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}),
|
||||||
|
body: JSON.stringify({
|
||||||
|
...params
|
||||||
|
})
|
||||||
|
}).then(async (response) => response.json())
|
||||||
|
.then((response: response_format) => {
|
||||||
|
if (isResponseValid(response)) {
|
||||||
|
if (response.error === ERROR_TYPE.SUCCESS)
|
||||||
|
resolve(response.data);
|
||||||
|
else
|
||||||
|
reject(response.error);
|
||||||
|
} else
|
||||||
|
reject(ERROR_TYPE.CONNECTION_ERROR);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
reject(ERROR_TYPE.CONNECTION_ERROR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isResponseValid(response: response_format) {
|
||||||
|
let valid = response !== undefined
|
||||||
|
&& response.error !== undefined
|
||||||
|
&& typeof response.error === "number";
|
||||||
|
|
||||||
|
valid = valid
|
||||||
|
&& response.data !== undefined
|
||||||
|
&& typeof response.data === "object";
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read data from FETCH_URL and return it.
|
* Read data from FETCH_URL and return it.
|
||||||
* If no data was found, returns an empty object
|
* If no data was found, returns an empty object
|
||||||
|
|
Loading…
Reference in a new issue