diff --git a/src/components/Amicale/AuthenticatedScreen.tsx b/src/components/Amicale/AuthenticatedScreen.tsx deleted file mode 100644 index 4232496..0000000 --- a/src/components/Amicale/AuthenticatedScreen.tsx +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2019 - 2020 Arnaud Vergnet. - * - * This file is part of Campus INSAT. - * - * Campus INSAT is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Campus INSAT is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Campus INSAT. If not, see . - */ - -import * as React from 'react'; -import { StackNavigationProp } from '@react-navigation/stack'; -import ConnectionManager from '../../managers/ConnectionManager'; -import { ERROR_TYPE } from '../../utils/WebData'; -import ErrorView from '../Screens/ErrorView'; -import BasicLoadingScreen from '../Screens/BasicLoadingScreen'; -import i18n from 'i18n-js'; - -type PropsType = { - navigation: StackNavigationProp; - requests: Array<{ - link: string; - params: object; - mandatory: boolean; - }>; - renderFunction: (data: Array) => React.ReactNode; - errorViewOverride?: Array<{ - errorCode: number; - message: string; - icon: string; - showRetryButton: boolean; - }> | null; -}; - -type StateType = { - loading: boolean; -}; - -class AuthenticatedScreen extends React.Component, StateType> { - static defaultProps = { - errorViewOverride: null, - }; - - currentUserToken: string | null; - - connectionManager: ConnectionManager; - - errors: Array; - - fetchedData: Array; - - constructor(props: PropsType) { - super(props); - this.state = { - loading: true, - }; - this.currentUserToken = null; - this.connectionManager = ConnectionManager.getInstance(); - props.navigation.addListener('focus', this.onScreenFocus); - this.fetchedData = new Array(props.requests.length); - this.errors = new Array(props.requests.length); - } - - /** - * Refreshes screen if user changed - */ - onScreenFocus = () => { - if (this.currentUserToken !== this.connectionManager.getToken()) { - this.currentUserToken = this.connectionManager.getToken(); - this.fetchData(); - } - }; - - /** - * Callback used when a request finishes, successfully or not. - * Saves data and error code. - * If the token is invalid, logout the user and open the login screen. - * If the last request was received, stop the loading screen. - * - * @param data The data fetched from the server - * @param index The index for the data - * @param error The error code received - */ - onRequestFinished(data: T | null, index: number, error?: number) { - const { props } = this; - if (index >= 0 && index < props.requests.length) { - this.fetchedData[index] = data; - this.errors[index] = error != null ? error : ERROR_TYPE.SUCCESS; - } - // Token expired, logout user - if (error === ERROR_TYPE.BAD_TOKEN) { - this.connectionManager.disconnect(); - } - - if (this.allRequestsFinished()) { - this.setState({ loading: false }); - } - } - - /** - * Gets the error to render. - * Non-mandatory requests are ignored. - * - * - * @return {number} The error code or ERROR_TYPE.SUCCESS if no error was found - */ - getError(): number { - const { props } = this; - for (let i = 0; i < this.errors.length; i += 1) { - if ( - this.errors[i] !== ERROR_TYPE.SUCCESS && - props.requests[i].mandatory - ) { - return this.errors[i]; - } - } - return ERROR_TYPE.SUCCESS; - } - - /** - * Gets the error view to display in case of error - * - * @return {*} - */ - getErrorRender() { - const { props } = this; - const errorCode = this.getError(); - let shouldOverride = false; - let override = null; - const overrideList = props.errorViewOverride; - if (overrideList != null) { - for (let i = 0; i < overrideList.length; i += 1) { - if (overrideList[i].errorCode === errorCode) { - shouldOverride = true; - override = overrideList[i]; - break; - } - } - } - - if (shouldOverride && override != null) { - return ( - - ); - } - return ( - - ); - } - - /** - * Fetches the data from the server. - * - * If the user is not logged in errorCode is set to BAD_TOKEN and all requests fail. - * - * If the user is logged in, send all requests. - */ - fetchData = () => { - const { state, props } = this; - if (!state.loading) { - this.setState({ loading: true }); - } - - if (this.connectionManager.isLoggedIn()) { - for (let i = 0; i < props.requests.length; i += 1) { - this.connectionManager - .authenticatedRequest( - props.requests[i].link, - props.requests[i].params - ) - .then((response: T): void => this.onRequestFinished(response, i)) - .catch((error: number): void => - this.onRequestFinished(null, i, error) - ); - } - } else { - for (let i = 0; i < props.requests.length; i += 1) { - this.onRequestFinished(null, i, ERROR_TYPE.BAD_TOKEN); - } - } - }; - - /** - * Checks if all requests finished processing - * - * @return {boolean} True if all finished - */ - allRequestsFinished(): boolean { - let finished = true; - this.errors.forEach((error: number | null) => { - if (error == null) { - finished = false; - } - }); - return finished; - } - - /** - * Reloads the data, to be called using ref by parent components - */ - reload() { - this.fetchData(); - } - - render() { - const { state, props } = this; - if (state.loading) { - return ; - } - if (this.getError() === ERROR_TYPE.SUCCESS) { - return props.renderFunction(this.fetchedData); - } - return this.getErrorRender(); - } -} - -export default AuthenticatedScreen; diff --git a/src/components/Dialogs/ErrorDialog.tsx b/src/components/Dialogs/ErrorDialog.tsx index 45ff687..3b4c4d1 100644 --- a/src/components/Dialogs/ErrorDialog.tsx +++ b/src/components/Dialogs/ErrorDialog.tsx @@ -39,7 +39,7 @@ function ErrorDialog(props: PropsType) { visible={props.visible} onDismiss={props.onDismiss} title={i18n.t('errors.title')} - message={getErrorMessage(props)} + message={getErrorMessage(props).message} /> ); } diff --git a/src/constants/Urls.tsx b/src/constants/Urls.tsx index 017ca4e..2058fa1 100644 --- a/src/constants/Urls.tsx +++ b/src/constants/Urls.tsx @@ -33,7 +33,7 @@ const APP_IMAGES_ENDPOINT = STUDENT_SERVER + '~amicale_app/images/'; export default { amicale: { - api: APP_ENDPOINT, + api: AMICALE_ENDPOINT, resetPassword: AMICALE_SERVER + 'password/reset', events: AMICALE_ENDPOINT + 'event/list', }, diff --git a/src/managers/ConnectionManager.ts b/src/managers/ConnectionManager.ts index 6961e11..ee11ff2 100644 --- a/src/managers/ConnectionManager.ts +++ b/src/managers/ConnectionManager.ts @@ -163,7 +163,9 @@ export default class ConnectionManager { }); } }) - .catch(reject); + .catch((err) => { + reject(err); + }); } ); } @@ -177,7 +179,7 @@ export default class ConnectionManager { */ async authenticatedRequest( path: string, - params: { [key: string]: any } + params?: { [key: string]: any } ): Promise { return new Promise( ( diff --git a/src/screens/Amicale/Clubs/ClubDisplayScreen.tsx b/src/screens/Amicale/Clubs/ClubDisplayScreen.tsx index 50aefc9..65cd4b0 100644 --- a/src/screens/Amicale/Clubs/ClubDisplayScreen.tsx +++ b/src/screens/Amicale/Clubs/ClubDisplayScreen.tsx @@ -29,13 +29,13 @@ import { } from 'react-native-paper'; import i18n from 'i18n-js'; import { StackNavigationProp } from '@react-navigation/stack'; -import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen'; import CustomHTML from '../../../components/Overrides/CustomHTML'; import { TAB_BAR_HEIGHT } from '../../../components/Tabbar/CustomTabBar'; import type { ClubCategoryType, ClubType } from './ClubListScreen'; -import { ERROR_TYPE } from '../../../utils/WebData'; import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView'; import ImageGalleryButton from '../../../components/Media/ImageGalleryButton'; +import RequestScreen from '../../../components/Screens/RequestScreen'; +import ConnectionManager from '../../../managers/ConnectionManager'; type PropsType = { navigation: StackNavigationProp; @@ -49,6 +49,8 @@ type PropsType = { theme: ReactNativePaper.Theme; }; +type ResponseType = ClubType; + const AMICALE_MAIL = 'clubs@amicale-insat.fr'; const styles = StyleSheet.create({ @@ -88,7 +90,7 @@ const styles = StyleSheet.create({ * If called with clubId parameter, will fetch the information on the server */ class ClubDisplayScreen extends React.Component { - displayData: ClubType | null; + displayData: ClubType | undefined; categories: Array | null; @@ -98,7 +100,7 @@ class ClubDisplayScreen extends React.Component { constructor(props: PropsType) { super(props); - this.displayData = null; + this.displayData = undefined; this.categories = null; this.clubId = props.route.params?.clubId ? props.route.params.clubId : 0; this.shouldFetchData = true; @@ -236,9 +238,8 @@ class ClubDisplayScreen extends React.Component { ); } - getScreen = (response: Array) => { - let data: ClubType | null = response[0]; - if (data != null) { + getScreen = (data: ResponseType | undefined) => { + if (data) { this.updateHeaderTitle(data); return ( @@ -264,7 +265,7 @@ class ClubDisplayScreen extends React.Component { ); } - return null; + return ; }; /** @@ -278,31 +279,20 @@ class ClubDisplayScreen extends React.Component { } render() { - const { props } = this; if (this.shouldFetchData) { return ( - + ConnectionManager.getInstance().authenticatedRequest( + 'clubs/info', + { id: this.clubId } + ) + } + render={this.getScreen} /> ); } - return this.getScreen([this.displayData]); + return this.getScreen(this.displayData); } } diff --git a/src/screens/Amicale/Clubs/ClubListScreen.tsx b/src/screens/Amicale/Clubs/ClubListScreen.tsx index 886a11a..7bf8a76 100644 --- a/src/screens/Amicale/Clubs/ClubListScreen.tsx +++ b/src/screens/Amicale/Clubs/ClubListScreen.tsx @@ -22,7 +22,6 @@ import { Platform } from 'react-native'; import { Searchbar } from 'react-native-paper'; import i18n from 'i18n-js'; import { StackNavigationProp } from '@react-navigation/stack'; -import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen'; import ClubListItem from '../../../components/Lists/Clubs/ClubListItem'; import { isItemInCategoryFilter, @@ -32,7 +31,8 @@ import ClubListHeader from '../../../components/Lists/Clubs/ClubListHeader'; import MaterialHeaderButtons, { Item, } from '../../../components/Overrides/CustomHeaderButton'; -import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList'; +import ConnectionManager from '../../../managers/ConnectionManager'; +import WebSectionList from '../../../components/Screens/WebSectionList'; export type ClubCategoryType = { id: number; @@ -58,6 +58,11 @@ type StateType = { currentSearchString: string; }; +type ResponseType = { + categories: Array; + clubs: Array; +}; + const LIST_ITEM_HEIGHT = 96; class ClubListScreen extends React.Component { @@ -146,30 +151,13 @@ class ClubListScreen extends React.Component { ); }; - getScreen = ( - data: Array<{ - categories: Array; - clubs: Array; - } | null> - ) => { - let categoryList: Array = []; - let clubList: Array = []; - if (data[0] != null) { - categoryList = data[0].categories; - clubList = data[0].clubs; + createDataset = (data: ResponseType | undefined) => { + if (data) { + this.categories = data?.categories; + return [{ title: '', data: data.clubs }]; + } else { + return [{ title: '', data: [] }]; } - this.categories = categoryList; - return ( - - ); }; /** @@ -177,15 +165,19 @@ class ClubListScreen extends React.Component { * * @returns {*} */ - getListHeader() { + getListHeader(data: ResponseType | undefined) { const { state } = this; - return ( - - ); + if (data) { + return ( + + ); + } else { + return null; + } } /** @@ -223,15 +215,6 @@ class ClubListScreen extends React.Component { keyExtractor = (item: ClubType): string => item.id.toString(); - itemLayout = ( - _data: Array | null | undefined, - index: number - ): { length: number; offset: number; index: number } => ({ - length: LIST_ITEM_HEIGHT, - offset: LIST_ITEM_HEIGHT * index, - index, - }); - /** * Updates the search string and category filter, saving them to the State. * @@ -282,18 +265,20 @@ class ClubListScreen extends React.Component { } render() { - const { props } = this; return ( - + ConnectionManager.getInstance().authenticatedRequest( + 'clubs/list' + ) + } + createDataset={this.createDataset} + keyExtractor={this.keyExtractor} + renderItem={this.getRenderItem} + renderListHeaderComponent={(data) => this.getListHeader(data)} + // Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration + removeClippedSubviews={true} + itemHeight={LIST_ITEM_HEIGHT} /> ); } diff --git a/src/screens/Amicale/Equipment/EquipmentListScreen.tsx b/src/screens/Amicale/Equipment/EquipmentListScreen.tsx index 54d5b98..7c51e7a 100644 --- a/src/screens/Amicale/Equipment/EquipmentListScreen.tsx +++ b/src/screens/Amicale/Equipment/EquipmentListScreen.tsx @@ -22,13 +22,14 @@ import { StyleSheet, View } from 'react-native'; import { Button } from 'react-native-paper'; import { StackNavigationProp } from '@react-navigation/stack'; import i18n from 'i18n-js'; -import AuthenticatedScreen from '../../../components/Amicale/AuthenticatedScreen'; import EquipmentListItem from '../../../components/Lists/Equipment/EquipmentListItem'; import MascotPopup from '../../../components/Mascot/MascotPopup'; import { MASCOT_STYLE } from '../../../components/Mascot/Mascot'; import AsyncStorageManager from '../../../managers/AsyncStorageManager'; -import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList'; import GENERAL_STYLES from '../../../constants/Styles'; +import ConnectionManager from '../../../managers/ConnectionManager'; +import { ApiRejectType } from '../../../utils/WebData'; +import WebSectionList from '../../../components/Screens/WebSectionList'; type PropsType = { navigation: StackNavigationProp; @@ -52,6 +53,11 @@ export type RentedDeviceType = { end: string; }; +type ResponseType = { + devices: Array; + locations?: Array; +}; + const LIST_ITEM_HEIGHT = 64; const styles = StyleSheet.create({ @@ -65,10 +71,6 @@ const styles = StyleSheet.create({ class EquipmentListScreen extends React.Component { userRents: null | Array; - authRef: { current: null | AuthenticatedScreen }; - - canRefresh: boolean; - constructor(props: PropsType) { super(props); this.userRents = null; @@ -77,22 +79,8 @@ class EquipmentListScreen extends React.Component { AsyncStorageManager.PREFERENCES.equipmentShowMascot.key ), }; - this.canRefresh = false; - this.authRef = React.createRef(); - props.navigation.addListener('focus', this.onScreenFocus); } - onScreenFocus = () => { - if ( - this.canRefresh && - this.authRef.current && - this.authRef.current.reload - ) { - this.authRef.current.reload(); - } - this.canRefresh = true; - }; - getRenderItem = ({ item }: { item: DeviceType }) => { const { navigation } = this.props; return ( @@ -139,37 +127,17 @@ class EquipmentListScreen extends React.Component { keyExtractor = (item: DeviceType): string => item.id.toString(); - /** - * Gets the main screen component with the fetched data - * - * @param data The data fetched from the server - * @returns {*} - */ - getScreen = ( - data: Array< - | { devices: Array } - | { locations: Array } - | null - > - ) => { - const [allDevices, userRents] = data; - if (userRents) { - this.userRents = (userRents as { - locations: Array; - }).locations; + createDataset = (data: ResponseType | undefined) => { + if (data) { + const userRents = data.locations; + + if (userRents) { + this.userRents = userRents; + } + return [{ title: '', data: data.devices }]; + } else { + return [{ title: '', data: [] }]; } - return ( - }).devices - : null - } - /> - ); }; showMascotDialog = () => { @@ -184,26 +152,46 @@ class EquipmentListScreen extends React.Component { this.setState({ mascotDialogVisible: false }); }; + request = () => { + return new Promise( + ( + resolve: (data: ResponseType) => void, + reject: (error: ApiRejectType) => void + ) => { + ConnectionManager.getInstance() + .authenticatedRequest<{ devices: Array }>('location/all') + .then((devicesData) => { + ConnectionManager.getInstance() + .authenticatedRequest<{ + locations: Array; + }>('location/my') + .then((rentsData) => { + resolve({ + devices: devicesData.devices, + locations: rentsData.locations, + }); + }) + .catch(() => + resolve({ + devices: devicesData.devices, + }) + ); + }) + .catch(reject); + } + ); + }; + render() { - const { props, state } = this; + const { state } = this; return ( - this.getListHeader()} /> { this.onInputChange(false, value); }; props.navigation.addListener('focus', this.onScreenFocus); + // TODO remove this.state = { - email: '', - password: '', + email: 'vergnet@etud.insa-toulouse.fr', + password: 'IGtt25ùj', isEmailValidated: false, isPasswordValidated: false, loading: false, dialogVisible: false, - dialogError: REQUEST_STATUS.SUCCESS, + dialogError: { status: REQUEST_STATUS.SUCCESS }, mascotDialogVisible: AsyncStorageManager.getBool( AsyncStorageManager.PREFERENCES.loginShowMascot.key ), @@ -338,9 +339,11 @@ class LoginScreen extends React.Component { * @param error The error given by the login request */ showErrorDialog = (error: ApiRejectType) => { + console.log(error); + this.setState({ dialogVisible: true, - dialogError: error.status, + dialogError: error, }); }; @@ -460,7 +463,8 @@ class LoginScreen extends React.Component { diff --git a/src/screens/Amicale/ProfileScreen.tsx b/src/screens/Amicale/ProfileScreen.tsx index 4d533e5..c329767 100644 --- a/src/screens/Amicale/ProfileScreen.tsx +++ b/src/screens/Amicale/ProfileScreen.tsx @@ -30,7 +30,6 @@ import { } from 'react-native-paper'; import i18n from 'i18n-js'; import { StackNavigationProp } from '@react-navigation/stack'; -import AuthenticatedScreen from '../../components/Amicale/AuthenticatedScreen'; import LogoutDialog from '../../components/Amicale/LogoutDialog'; import MaterialHeaderButtons, { Item, @@ -42,6 +41,8 @@ import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatLis import type { ServiceItemType } from '../../managers/ServicesManager'; import GENERAL_STYLES from '../../constants/Styles'; import Urls from '../../constants/Urls'; +import RequestScreen from '../../components/Screens/RequestScreen'; +import ConnectionManager from '../../managers/ConnectionManager'; type PropsType = { navigation: StackNavigationProp; @@ -89,7 +90,7 @@ const styles = StyleSheet.create({ }); class ProfileScreen extends React.Component { - data: ProfileDataType | null; + data: ProfileDataType | undefined; flatListData: Array<{ id: string }>; @@ -97,7 +98,7 @@ class ProfileScreen extends React.Component { constructor(props: PropsType) { super(props); - this.data = null; + this.data = undefined; this.flatListData = [{ id: '0' }, { id: '1' }, { id: '2' }, { id: '3' }]; const services = new ServicesManager(props.navigation); this.amicaleDataset = services.getAmicaleServices([SERVICES_KEY.PROFILE]); @@ -134,21 +135,25 @@ class ProfileScreen extends React.Component { * @param data The data fetched from the server * @returns {*} */ - getScreen = (data: Array) => { + getScreen = (data: ProfileDataType | undefined) => { const { dialogVisible } = this.state; - this.data = data[0]; - return ( - - - - - ); + if (data) { + this.data = data; + return ( + + + + + ); + } else { + return ; + } }; getRenderItem = ({ item }: { item: { id: string } }) => { @@ -482,18 +487,12 @@ class ProfileScreen extends React.Component { } render() { - const { navigation } = this.props; return ( - + request={() => + ConnectionManager.getInstance().authenticatedRequest('user/profile') + } + render={this.getScreen} /> ); } diff --git a/src/screens/Amicale/VoteScreen.tsx b/src/screens/Amicale/VoteScreen.tsx index e28b951..5e546e2 100644 --- a/src/screens/Amicale/VoteScreen.tsx +++ b/src/screens/Amicale/VoteScreen.tsx @@ -18,11 +18,9 @@ */ import * as React from 'react'; -import { RefreshControl, StyleSheet, View } from 'react-native'; -import { StackNavigationProp } from '@react-navigation/stack'; +import { StyleSheet, View } from 'react-native'; import i18n from 'i18n-js'; import { Button } from 'react-native-paper'; -import AuthenticatedScreen from '../../components/Amicale/AuthenticatedScreen'; import { getTimeOnlyString, stringToDate } from '../../utils/Planning'; import VoteTease from '../../components/Amicale/Vote/VoteTease'; import VoteSelect from '../../components/Amicale/Vote/VoteSelect'; @@ -32,8 +30,11 @@ import { MASCOT_STYLE } from '../../components/Mascot/Mascot'; import MascotPopup from '../../components/Mascot/MascotPopup'; import AsyncStorageManager from '../../managers/AsyncStorageManager'; import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable'; -import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList'; import GENERAL_STYLES from '../../constants/Styles'; +import ConnectionManager from '../../managers/ConnectionManager'; +import WebSectionList, { + SectionListDataType, +} from '../../components/Screens/WebSectionList'; export type VoteTeamType = { id: number; @@ -60,6 +61,11 @@ type VoteDatesObjectType = { date_result_end: Date; }; +type ResponseType = { + teams?: TeamResponseType; + dates?: VoteDatesStringType; +}; + // const FAKE_DATE = { // "date_begin": "2020-08-19 15:50", // "date_end": "2020-08-19 15:50", @@ -108,11 +114,7 @@ type VoteDatesObjectType = { // ], // }; -const MIN_REFRESH_TIME = 5 * 1000; - -type PropsType = { - navigation: StackNavigationProp; -}; +type PropsType = {}; type StateType = { hasVoted: boolean; @@ -135,23 +137,21 @@ export default class VoteScreen extends React.Component { hasVoted: boolean; - datesString: null | VoteDatesStringType; + datesString: undefined | VoteDatesStringType; - dates: null | VoteDatesObjectType; + dates: undefined | VoteDatesObjectType; today: Date; - mainFlatListData: Array<{ key: string }>; + mainFlatListData: SectionListDataType<{ key: string }>; - lastRefresh: Date | null; - - authRef: { current: null | AuthenticatedScreen }; + refreshData: () => void; constructor(props: PropsType) { super(props); this.teams = []; - this.datesString = null; - this.dates = null; + this.datesString = undefined; + this.dates = undefined; this.state = { hasVoted: false, mascotDialogVisible: AsyncStorageManager.getBool( @@ -160,9 +160,10 @@ export default class VoteScreen extends React.Component { }; this.hasVoted = false; this.today = new Date(); - this.authRef = React.createRef(); - this.lastRefresh = null; - this.mainFlatListData = [{ key: 'main' }, { key: 'info' }]; + this.refreshData = () => undefined; + this.mainFlatListData = [ + { title: '', data: [{ key: 'main' }, { key: 'info' }] }, + ]; } /** @@ -201,37 +202,32 @@ export default class VoteScreen extends React.Component { return this.getContent(); }; - getScreen = (data: Array) => { - const { state } = this; + createDataset = ( + data: ResponseType | undefined, + _loading: boolean, + _lastRefreshDate: Date | undefined, + refreshData: () => void + ) => { // data[0] = FAKE_TEAMS2; // data[1] = FAKE_DATE; - this.lastRefresh = new Date(); + this.refreshData = refreshData; + if (data) { + const { teams, dates } = data; - const teams = data[0] as TeamResponseType | null; - const dateStrings = data[1] as VoteDatesStringType | null; + if (dates && dates.date_begin == null) { + this.datesString = undefined; + } else { + this.datesString = dates; + } - if (dateStrings != null && dateStrings.date_begin == null) { - this.datesString = null; - } else { - this.datesString = dateStrings; + if (teams) { + this.teams = teams.teams; + this.hasVoted = teams.has_voted; + } + + this.generateDateObject(); } - - if (teams != null) { - this.teams = teams.teams; - this.hasVoted = teams.has_voted; - } - - this.generateDateObject(); - return ( - - } - extraData={state.hasVoted.toString()} - renderItem={this.getMainRenderItem} - /> - ); + return this.mainFlatListData; }; getContent() { @@ -261,7 +257,7 @@ export default class VoteScreen extends React.Component { ); } @@ -327,23 +323,6 @@ export default class VoteScreen extends React.Component { ); } - /** - * Reloads vote data if last refresh delta is smaller than the minimum refresh time - */ - reloadData = () => { - let canRefresh; - const { lastRefresh } = this; - if (lastRefresh != null) { - canRefresh = - new Date().getTime() - lastRefresh.getTime() > MIN_REFRESH_TIME; - } else { - canRefresh = true; - } - if (canRefresh && this.authRef.current != null) { - this.authRef.current.reload(); - } - }; - showMascotDialog = () => { this.setState({ mascotDialogVisible: true }); }; @@ -403,13 +382,36 @@ export default class VoteScreen extends React.Component { date_result_end: dateResultEnd, }; } else { - this.dates = null; + this.dates = undefined; } } else { - this.dates = null; + this.dates = undefined; } } + request = () => { + return new Promise((resolve: (data: ResponseType) => void) => { + ConnectionManager.getInstance() + .authenticatedRequest('elections/dates') + .then((datesData) => { + ConnectionManager.getInstance() + .authenticatedRequest('elections/teams') + .then((teamsData) => { + resolve({ + dates: datesData, + teams: teamsData, + }); + }) + .catch(() => + resolve({ + dates: datesData, + }) + ); + }) + .catch(() => resolve({})); + }); + }; + /** * Renders the authenticated screen. * @@ -418,25 +420,14 @@ export default class VoteScreen extends React.Component { * @returns {*} */ render() { - const { props, state } = this; + const { state } = this; return ( - - navigation={props.navigation} - ref={this.authRef} - requests={[ - { - link: 'elections/teams', - params: {}, - mandatory: false, - }, - { - link: 'elections/dates', - params: {}, - mandatory: false, - }, - ]} - renderFunction={this.getScreen} + = { - error: number; - data: T; + status: REQUEST_STATUS; + error?: API_REQUEST_CODES; + data?: T; }; export type ApiRejectType = { @@ -87,6 +88,8 @@ export async function apiRequest( if (params != null) { requestParams = { ...params }; } + console.log(Urls.amicale.api + path); + fetch(Urls.amicale.api + path, { method, headers: new Headers({ @@ -95,12 +98,20 @@ export async function apiRequest( }), body: JSON.stringify(requestParams), }) - .then( - async (response: Response): Promise> => - response.json() - ) + .then((response: Response) => { + const status = response.status; + if (status === REQUEST_STATUS.SUCCESS) { + return response.json().then( + (data): ApiResponseType => { + return { status: status, error: data.error, data: data.data }; + } + ); + } else { + return { status: status }; + } + }) .then((response: ApiResponseType) => { - if (isApiResponseValid(response)) { + if (isApiResponseValid(response) && response.data) { if (response.error === API_REQUEST_CODES.SUCCESS) { resolve(response.data); } else { @@ -111,15 +122,15 @@ export async function apiRequest( } } else { reject({ - status: REQUEST_STATUS.SERVER_ERROR, + status: response.status, }); } }) - .catch(() => + .catch(() => { reject({ - status: REQUEST_STATUS.SERVER_ERROR, - }) - ); + status: REQUEST_STATUS.CONNECTION_ERROR, + }); + }); } ); }