forked from vergnet/application-amicale
Improve Planning screen components to match linter
This commit is contained in:
parent
a3299c19f7
commit
3ce23726c2
4 changed files with 586 additions and 537 deletions
|
@ -2,166 +2,183 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {View} from 'react-native';
|
import {View} from 'react-native';
|
||||||
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 ImageModal from 'react-native-image-modal';
|
import ImageModal from 'react-native-image-modal';
|
||||||
import BasicLoadingScreen from "../../components/Screens/BasicLoadingScreen";
|
|
||||||
import {apiRequest, ERROR_TYPE} from "../../utils/WebData";
|
|
||||||
import ErrorView from "../../components/Screens/ErrorView";
|
|
||||||
import CustomHTML from "../../components/Overrides/CustomHTML";
|
|
||||||
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
|
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import {StackNavigationProp} from "@react-navigation/stack";
|
import {StackNavigationProp} from '@react-navigation/stack';
|
||||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
|
||||||
import CollapsibleScrollView from "../../components/Collapsible/CollapsibleScrollView";
|
import DateManager from '../../managers/DateManager';
|
||||||
|
import BasicLoadingScreen from '../../components/Screens/BasicLoadingScreen';
|
||||||
|
import {apiRequest, ERROR_TYPE} from '../../utils/WebData';
|
||||||
|
import ErrorView from '../../components/Screens/ErrorView';
|
||||||
|
import CustomHTML from '../../components/Overrides/CustomHTML';
|
||||||
|
import CustomTabBar from '../../components/Tabbar/CustomTabBar';
|
||||||
|
import type {CustomThemeType} from '../../managers/ThemeManager';
|
||||||
|
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
|
||||||
|
import type {PlanningEventType} from '../../utils/Planning';
|
||||||
|
|
||||||
type Props = {
|
type PropsType = {
|
||||||
navigation: StackNavigationProp,
|
navigation: StackNavigationProp,
|
||||||
route: { params: { data: Object, id: number, eventId: number } },
|
route: {params: {data: PlanningEventType, id: number, eventId: number}},
|
||||||
theme: CustomTheme
|
theme: CustomThemeType,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type StateType = {
|
||||||
loading: boolean
|
loading: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CLUB_INFO_PATH = "event/info";
|
const EVENT_INFO_URL = 'event/info';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining a planning event information page.
|
* Class defining a planning event information page.
|
||||||
*/
|
*/
|
||||||
class PlanningDisplayScreen extends React.Component<Props, State> {
|
class PlanningDisplayScreen extends React.Component<PropsType, StateType> {
|
||||||
|
displayData: null | PlanningEventType;
|
||||||
|
|
||||||
displayData: Object;
|
shouldFetchData: boolean;
|
||||||
shouldFetchData: boolean;
|
|
||||||
eventId: number;
|
|
||||||
errorCode: number;
|
|
||||||
|
|
||||||
/**
|
eventId: number;
|
||||||
* Generates data depending on whether the screen was opened from the planning or from a link
|
|
||||||
*
|
|
||||||
* @param props
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
if (this.props.route.params.data != null) {
|
errorCode: number;
|
||||||
this.displayData = this.props.route.params.data;
|
|
||||||
this.eventId = this.displayData.id;
|
|
||||||
this.shouldFetchData = false;
|
|
||||||
this.errorCode = 0;
|
|
||||||
this.state = {
|
|
||||||
loading: false,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.displayData = null;
|
|
||||||
this.eventId = this.props.route.params.eventId;
|
|
||||||
this.shouldFetchData = true;
|
|
||||||
this.errorCode = 0;
|
|
||||||
this.state = {
|
|
||||||
loading: true,
|
|
||||||
};
|
|
||||||
this.fetchData();
|
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Generates data depending on whether the screen was opened from the planning or from a link
|
||||||
|
*
|
||||||
|
* @param props
|
||||||
|
*/
|
||||||
|
constructor(props: PropsType) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
if (props.route.params.data != null) {
|
||||||
|
this.displayData = props.route.params.data;
|
||||||
|
this.eventId = this.displayData.id;
|
||||||
|
this.shouldFetchData = false;
|
||||||
|
this.errorCode = 0;
|
||||||
|
this.state = {
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.displayData = null;
|
||||||
|
this.eventId = props.route.params.eventId;
|
||||||
|
this.shouldFetchData = true;
|
||||||
|
this.errorCode = 0;
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
};
|
||||||
|
this.fetchData();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches data for the current event id from the API
|
* Hides loading and saves fetched data
|
||||||
*/
|
*
|
||||||
fetchData = () => {
|
* @param data Received data
|
||||||
this.setState({loading: true});
|
*/
|
||||||
apiRequest(CLUB_INFO_PATH, 'POST', {id: this.eventId})
|
onFetchSuccess = (data: PlanningEventType) => {
|
||||||
.then(this.onFetchSuccess)
|
this.displayData = data;
|
||||||
.catch(this.onFetchError);
|
this.setState({loading: false});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides loading and saves fetched data
|
* Hides loading and saves the error code
|
||||||
*
|
*
|
||||||
* @param data Received data
|
* @param error
|
||||||
*/
|
*/
|
||||||
onFetchSuccess = (data: Object) => {
|
onFetchError = (error: number) => {
|
||||||
this.displayData = data;
|
this.errorCode = error;
|
||||||
this.setState({loading: false});
|
this.setState({loading: false});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides loading and saves the error code
|
* Gets content to display
|
||||||
*
|
*
|
||||||
* @param error
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
onFetchError = (error: number) => {
|
getContent(): React.Node {
|
||||||
this.errorCode = error;
|
const {theme} = this.props;
|
||||||
this.setState({loading: false});
|
const {displayData} = this;
|
||||||
};
|
if (displayData == null) return null;
|
||||||
|
let subtitle = getFormattedEventTime(
|
||||||
|
displayData.date_begin,
|
||||||
|
displayData.date_end,
|
||||||
|
);
|
||||||
|
const dateString = getDateOnlyString(displayData.date_begin);
|
||||||
|
if (dateString !== null)
|
||||||
|
subtitle += ` | ${DateManager.getInstance().getTranslatedDate(
|
||||||
|
dateString,
|
||||||
|
)}`;
|
||||||
|
return (
|
||||||
|
<CollapsibleScrollView style={{paddingLeft: 5, paddingRight: 5}} hasTab>
|
||||||
|
<Card.Title title={displayData.title} subtitle={subtitle} />
|
||||||
|
{displayData.logo !== null ? (
|
||||||
|
<View style={{marginLeft: 'auto', marginRight: 'auto'}}>
|
||||||
|
<ImageModal
|
||||||
|
resizeMode="contain"
|
||||||
|
imageBackgroundColor={theme.colors.background}
|
||||||
|
style={{
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
}}
|
||||||
|
source={{
|
||||||
|
uri: displayData.logo,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
|
||||||
/**
|
{displayData.description !== null ? (
|
||||||
* Gets content to display
|
<Card.Content
|
||||||
*
|
style={{paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||||
* @returns {*}
|
<CustomHTML html={displayData.description} />
|
||||||
*/
|
</Card.Content>
|
||||||
getContent() {
|
) : (
|
||||||
let subtitle = getFormattedEventTime(
|
<View />
|
||||||
this.displayData["date_begin"], this.displayData["date_end"]);
|
)}
|
||||||
let dateString = getDateOnlyString(this.displayData["date_begin"]);
|
</CollapsibleScrollView>
|
||||||
if (dateString !== null)
|
);
|
||||||
subtitle += ' | ' + DateManager.getInstance().getTranslatedDate(dateString);
|
}
|
||||||
return (
|
|
||||||
<CollapsibleScrollView
|
|
||||||
style={{paddingLeft: 5, paddingRight: 5}}
|
|
||||||
hasTab={true}
|
|
||||||
>
|
|
||||||
<Card.Title
|
|
||||||
title={this.displayData.title}
|
|
||||||
subtitle={subtitle}
|
|
||||||
/>
|
|
||||||
{this.displayData.logo !== null ?
|
|
||||||
<View style={{marginLeft: 'auto', marginRight: 'auto'}}>
|
|
||||||
<ImageModal
|
|
||||||
resizeMode="contain"
|
|
||||||
imageBackgroundColor={this.props.theme.colors.background}
|
|
||||||
style={{
|
|
||||||
width: 300,
|
|
||||||
height: 300,
|
|
||||||
}}
|
|
||||||
source={{
|
|
||||||
uri: this.displayData.logo,
|
|
||||||
}}
|
|
||||||
/></View>
|
|
||||||
: <View/>}
|
|
||||||
|
|
||||||
{this.displayData.description !== null ?
|
/**
|
||||||
<Card.Content style={{paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
* Shows an error view and use a custom message if the event does not exist
|
||||||
<CustomHTML html={this.displayData.description}/>
|
*
|
||||||
</Card.Content>
|
* @returns {*}
|
||||||
: <View/>}
|
*/
|
||||||
</CollapsibleScrollView>
|
getErrorView(): React.Node {
|
||||||
);
|
const {navigation} = this.props;
|
||||||
}
|
if (this.errorCode === ERROR_TYPE.BAD_INPUT)
|
||||||
|
return (
|
||||||
|
<ErrorView
|
||||||
|
navigation={navigation}
|
||||||
|
showRetryButton={false}
|
||||||
|
message={i18n.t('screens.planning.invalidEvent')}
|
||||||
|
icon="calendar-remove"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ErrorView
|
||||||
|
navigation={navigation}
|
||||||
|
errorCode={this.errorCode}
|
||||||
|
onRefresh={this.fetchData}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows an error view and use a custom message if the event does not exist
|
* Fetches data for the current event id from the API
|
||||||
*
|
*/
|
||||||
* @returns {*}
|
fetchData = () => {
|
||||||
*/
|
this.setState({loading: true});
|
||||||
getErrorView() {
|
apiRequest(EVENT_INFO_URL, 'POST', {id: this.eventId})
|
||||||
if (this.errorCode === ERROR_TYPE.BAD_INPUT)
|
.then(this.onFetchSuccess)
|
||||||
return <ErrorView {...this.props} showRetryButton={false} message={i18n.t("screens.planning.invalidEvent")}
|
.catch(this.onFetchError);
|
||||||
icon={"calendar-remove"}/>;
|
};
|
||||||
else
|
|
||||||
return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render(): React.Node {
|
||||||
if (this.state.loading)
|
const {loading} = this.state;
|
||||||
return <BasicLoadingScreen/>;
|
if (loading) return <BasicLoadingScreen />;
|
||||||
else if (this.errorCode === 0)
|
if (this.errorCode === 0) return this.getContent();
|
||||||
return this.getContent();
|
return this.getErrorView();
|
||||||
else
|
}
|
||||||
return this.getErrorView();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(PlanningDisplayScreen);
|
export default withTheme(PlanningDisplayScreen);
|
||||||
|
|
|
@ -2,259 +2,282 @@
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {BackHandler, View} from 'react-native';
|
import {BackHandler, View} from 'react-native';
|
||||||
import i18n from "i18n-js";
|
import i18n from 'i18n-js';
|
||||||
import {LocaleConfig} from 'react-native-calendars';
|
import {Agenda, LocaleConfig} from 'react-native-calendars';
|
||||||
import {readData} from "../../utils/WebData";
|
|
||||||
import type {eventObject} from "../../utils/Planning";
|
|
||||||
import {
|
|
||||||
generateEventAgenda,
|
|
||||||
getCurrentDateString,
|
|
||||||
getDateOnlyString,
|
|
||||||
getFormattedEventTime,
|
|
||||||
} from '../../utils/Planning';
|
|
||||||
import {Avatar, Divider, List} from 'react-native-paper';
|
import {Avatar, Divider, List} from 'react-native-paper';
|
||||||
import CustomAgenda from "../../components/Overrides/CustomAgenda";
|
import {StackNavigationProp} from '@react-navigation/stack';
|
||||||
import {StackNavigationProp} from "@react-navigation/stack";
|
import {readData} from '../../utils/WebData';
|
||||||
import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
|
import type {PlanningEventType} from '../../utils/Planning';
|
||||||
import MascotPopup from "../../components/Mascot/MascotPopup";
|
import {
|
||||||
import AsyncStorageManager from "../../managers/AsyncStorageManager";
|
generateEventAgenda,
|
||||||
|
getCurrentDateString,
|
||||||
|
getDateOnlyString,
|
||||||
|
getFormattedEventTime,
|
||||||
|
} from '../../utils/Planning';
|
||||||
|
import CustomAgenda from '../../components/Overrides/CustomAgenda';
|
||||||
|
import {MASCOT_STYLE} from '../../components/Mascot/Mascot';
|
||||||
|
import MascotPopup from '../../components/Mascot/MascotPopup';
|
||||||
|
import AsyncStorageManager from '../../managers/AsyncStorageManager';
|
||||||
|
|
||||||
LocaleConfig.locales['fr'] = {
|
LocaleConfig.locales.fr = {
|
||||||
monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
|
monthNames: [
|
||||||
monthNamesShort: ['Janv.', 'Févr.', 'Mars', 'Avril', 'Mai', 'Juin', 'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
|
'Janvier',
|
||||||
dayNames: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
|
'Février',
|
||||||
dayNamesShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
|
'Mars',
|
||||||
today: 'Aujourd\'hui'
|
'Avril',
|
||||||
|
'Mai',
|
||||||
|
'Juin',
|
||||||
|
'Juillet',
|
||||||
|
'Août',
|
||||||
|
'Septembre',
|
||||||
|
'Octobre',
|
||||||
|
'Novembre',
|
||||||
|
'Décembre',
|
||||||
|
],
|
||||||
|
monthNamesShort: [
|
||||||
|
'Janv.',
|
||||||
|
'Févr.',
|
||||||
|
'Mars',
|
||||||
|
'Avril',
|
||||||
|
'Mai',
|
||||||
|
'Juin',
|
||||||
|
'Juil.',
|
||||||
|
'Août',
|
||||||
|
'Sept.',
|
||||||
|
'Oct.',
|
||||||
|
'Nov.',
|
||||||
|
'Déc.',
|
||||||
|
],
|
||||||
|
dayNames: [
|
||||||
|
'Dimanche',
|
||||||
|
'Lundi',
|
||||||
|
'Mardi',
|
||||||
|
'Mercredi',
|
||||||
|
'Jeudi',
|
||||||
|
'Vendredi',
|
||||||
|
'Samedi',
|
||||||
|
],
|
||||||
|
dayNamesShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
|
||||||
|
today: "Aujourd'hui",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PropsType = {
|
||||||
type Props = {
|
navigation: StackNavigationProp,
|
||||||
navigation: StackNavigationProp,
|
|
||||||
}
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
refreshing: boolean,
|
|
||||||
agendaItems: Object,
|
|
||||||
calendarShowing: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FETCH_URL = "https://www.amicale-insat.fr/api/event/list";
|
type StateType = {
|
||||||
|
refreshing: boolean,
|
||||||
|
agendaItems: {[key: string]: Array<PlanningEventType>},
|
||||||
|
calendarShowing: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
const FETCH_URL = 'https://www.amicale-insat.fr/api/event/list';
|
||||||
const AGENDA_MONTH_SPAN = 3;
|
const AGENDA_MONTH_SPAN = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class defining the app's planning screen
|
* Class defining the app's planning screen
|
||||||
*/
|
*/
|
||||||
class PlanningScreen extends React.Component<Props, State> {
|
class PlanningScreen extends React.Component<PropsType, StateType> {
|
||||||
|
agendaRef: null | Agenda;
|
||||||
|
|
||||||
agendaRef: Object;
|
lastRefresh: Date;
|
||||||
|
|
||||||
lastRefresh: Date;
|
minTimeBetweenRefresh = 60;
|
||||||
minTimeBetweenRefresh = 60;
|
|
||||||
|
|
||||||
state = {
|
currentDate = getDateOnlyString(getCurrentDateString());
|
||||||
refreshing: false,
|
|
||||||
agendaItems: {},
|
constructor(props: PropsType) {
|
||||||
calendarShowing: false,
|
super(props);
|
||||||
|
if (i18n.currentLocale().startsWith('fr')) {
|
||||||
|
LocaleConfig.defaultLocale = 'fr';
|
||||||
|
}
|
||||||
|
this.state = {
|
||||||
|
refreshing: false,
|
||||||
|
agendaItems: {},
|
||||||
|
calendarShowing: false,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
currentDate = getDateOnlyString(getCurrentDateString());
|
/**
|
||||||
|
* Captures focus and blur events to hook on android back button
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
const {navigation} = this.props;
|
||||||
|
this.onRefresh();
|
||||||
|
navigation.addListener('focus', () => {
|
||||||
|
BackHandler.addEventListener(
|
||||||
|
'hardwareBackPress',
|
||||||
|
this.onBackButtonPressAndroid,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
navigation.addListener('blur', () => {
|
||||||
|
BackHandler.removeEventListener(
|
||||||
|
'hardwareBackPress',
|
||||||
|
this.onBackButtonPressAndroid,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props: any) {
|
/**
|
||||||
super(props);
|
* Overrides default android back button behaviour to close the calendar if it was open.
|
||||||
if (i18n.currentLocale().startsWith("fr")) {
|
*
|
||||||
LocaleConfig.defaultLocale = 'fr';
|
* @return {boolean}
|
||||||
}
|
*/
|
||||||
|
onBackButtonPressAndroid = (): boolean => {
|
||||||
|
const {calendarShowing} = this.state;
|
||||||
|
if (calendarShowing && this.agendaRef != null) {
|
||||||
|
this.agendaRef.chooseDay(this.agendaRef.state.selectedDay);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Captures focus and blur events to hook on android back button
|
* Refreshes data and shows an animation while doing it
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
onRefresh = () => {
|
||||||
this.onRefresh();
|
let canRefresh;
|
||||||
this.props.navigation.addListener(
|
if (this.lastRefresh !== undefined)
|
||||||
'focus',
|
canRefresh =
|
||||||
() =>
|
(new Date().getTime() - this.lastRefresh.getTime()) / 1000 >
|
||||||
BackHandler.addEventListener(
|
this.minTimeBetweenRefresh;
|
||||||
'hardwareBackPress',
|
else canRefresh = true;
|
||||||
this.onBackButtonPressAndroid
|
|
||||||
)
|
if (canRefresh) {
|
||||||
);
|
this.setState({refreshing: true});
|
||||||
this.props.navigation.addListener(
|
readData(FETCH_URL)
|
||||||
'blur',
|
.then((fetchedData: Array<PlanningEventType>) => {
|
||||||
() =>
|
this.setState({
|
||||||
BackHandler.removeEventListener(
|
refreshing: false,
|
||||||
'hardwareBackPress',
|
agendaItems: generateEventAgenda(fetchedData, AGENDA_MONTH_SPAN),
|
||||||
this.onBackButtonPressAndroid
|
});
|
||||||
)
|
this.lastRefresh = new Date();
|
||||||
);
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.setState({
|
||||||
|
refreshing: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides default android back button behaviour to close the calendar if it was open.
|
* Callback used when receiving the agenda ref
|
||||||
*
|
*
|
||||||
* @return {boolean}
|
* @param ref
|
||||||
*/
|
*/
|
||||||
onBackButtonPressAndroid = () => {
|
onAgendaRef = (ref: Agenda) => {
|
||||||
if (this.state.calendarShowing) {
|
this.agendaRef = ref;
|
||||||
this.agendaRef.chooseDay(this.agendaRef.state.selectedDay);
|
};
|
||||||
return true;
|
|
||||||
} else {
|
/**
|
||||||
return false;
|
* Callback used when a button is pressed to toggle the calendar
|
||||||
}
|
*
|
||||||
|
* @param isCalendarOpened True is the calendar is already open, false otherwise
|
||||||
|
*/
|
||||||
|
onCalendarToggled = (isCalendarOpened: boolean) => {
|
||||||
|
this.setState({calendarShowing: isCalendarOpened});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an event render item
|
||||||
|
*
|
||||||
|
* @param item The current event to render
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
getRenderItem = (item: PlanningEventType): React.Node => {
|
||||||
|
const {navigation} = this.props;
|
||||||
|
const onPress = () => {
|
||||||
|
navigation.navigate('planning-information', {
|
||||||
|
data: item,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
if (item.logo !== null) {
|
||||||
/**
|
return (
|
||||||
* Function used to check if a row has changed
|
<View>
|
||||||
*
|
<Divider />
|
||||||
* @param r1
|
<List.Item
|
||||||
* @param r2
|
title={item.title}
|
||||||
* @return {boolean}
|
description={getFormattedEventTime(item.date_begin, item.date_end)}
|
||||||
*/
|
left={(): React.Node => (
|
||||||
rowHasChanged(r1: Object, r2: Object) {
|
<Avatar.Image
|
||||||
return false;
|
source={{uri: item.logo}}
|
||||||
// if (r1 !== undefined && r2 !== undefined)
|
style={{backgroundColor: 'transparent'}}
|
||||||
// return r1.title !== r2.title;
|
/>
|
||||||
// else return !(r1 === undefined && r2 === undefined);
|
)}
|
||||||
|
onPress={onPress}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Divider />
|
||||||
|
<List.Item
|
||||||
|
title={item.title}
|
||||||
|
description={getFormattedEventTime(item.date_begin, item.date_end)}
|
||||||
|
onPress={onPress}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes data and shows an animation while doing it
|
* Gets an empty render item for an empty date
|
||||||
*/
|
*
|
||||||
onRefresh = () => {
|
* @return {*}
|
||||||
let canRefresh;
|
*/
|
||||||
if (this.lastRefresh !== undefined)
|
getRenderEmptyDate = (): React.Node => <Divider />;
|
||||||
canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh;
|
|
||||||
else
|
|
||||||
canRefresh = true;
|
|
||||||
|
|
||||||
if (canRefresh) {
|
render(): React.Node {
|
||||||
this.setState({refreshing: true});
|
const {state, props} = this;
|
||||||
readData(FETCH_URL)
|
return (
|
||||||
.then((fetchedData) => {
|
<View style={{flex: 1}}>
|
||||||
this.setState({
|
<CustomAgenda
|
||||||
refreshing: false,
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
agendaItems: generateEventAgenda(fetchedData, AGENDA_MONTH_SPAN)
|
{...props}
|
||||||
});
|
// the list of items that have to be displayed in agenda. If you want to render item as empty date
|
||||||
this.lastRefresh = new Date();
|
// the value of date key kas to be an empty array []. If there exists no value for date key it is
|
||||||
})
|
// considered that the date in question is not yet loaded
|
||||||
.catch(() => {
|
items={state.agendaItems}
|
||||||
this.setState({
|
// initially selected day
|
||||||
refreshing: false,
|
selected={this.currentDate}
|
||||||
});
|
// Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
|
||||||
});
|
minDate={this.currentDate}
|
||||||
}
|
// Max amount of months allowed to scroll to the past. Default = 50
|
||||||
};
|
pastScrollRange={1}
|
||||||
|
// Max amount of months allowed to scroll to the future. Default = 50
|
||||||
/**
|
futureScrollRange={AGENDA_MONTH_SPAN}
|
||||||
* Callback used when receiving the agenda ref
|
// If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
|
||||||
*
|
onRefresh={this.onRefresh}
|
||||||
* @param ref
|
// callback that fires when the calendar is opened or closed
|
||||||
*/
|
onCalendarToggled={this.onCalendarToggled}
|
||||||
onAgendaRef = (ref: Object) => {
|
// Set this true while waiting for new data from a refresh
|
||||||
this.agendaRef = ref;
|
refreshing={state.refreshing}
|
||||||
}
|
renderItem={this.getRenderItem}
|
||||||
|
renderEmptyDate={this.getRenderEmptyDate}
|
||||||
/**
|
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
|
||||||
* Callback used when a button is pressed to toggle the calendar
|
firstDay={1}
|
||||||
*
|
// ref to this agenda in order to handle back button event
|
||||||
* @param isCalendarOpened True is the calendar is already open, false otherwise
|
onRef={this.onAgendaRef}
|
||||||
*/
|
/>
|
||||||
onCalendarToggled = (isCalendarOpened: boolean) => {
|
<MascotPopup
|
||||||
this.setState({calendarShowing: isCalendarOpened});
|
prefKey={AsyncStorageManager.PREFERENCES.eventsShowBanner.key}
|
||||||
}
|
title={i18n.t('screens.planning.mascotDialog.title')}
|
||||||
|
message={i18n.t('screens.planning.mascotDialog.message')}
|
||||||
/**
|
icon="party-popper"
|
||||||
* Gets an event render item
|
buttons={{
|
||||||
*
|
action: null,
|
||||||
* @param item The current event to render
|
cancel: {
|
||||||
* @return {*}
|
message: i18n.t('screens.planning.mascotDialog.button'),
|
||||||
*/
|
icon: 'check',
|
||||||
getRenderItem = (item: eventObject) => {
|
},
|
||||||
const onPress = this.props.navigation.navigate.bind(this, 'planning-information', {data: item});
|
}}
|
||||||
if (item.logo !== null) {
|
emotion={MASCOT_STYLE.HAPPY}
|
||||||
return (
|
/>
|
||||||
<View>
|
</View>
|
||||||
<Divider/>
|
);
|
||||||
<List.Item
|
}
|
||||||
title={item.title}
|
|
||||||
description={getFormattedEventTime(item["date_begin"], item["date_end"])}
|
|
||||||
left={() => <Avatar.Image
|
|
||||||
source={{uri: item.logo}}
|
|
||||||
style={{backgroundColor: 'transparent'}}
|
|
||||||
/>}
|
|
||||||
onPress={onPress}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<Divider/>
|
|
||||||
<List.Item
|
|
||||||
title={item.title}
|
|
||||||
description={getFormattedEventTime(item["date_begin"], item["date_end"])}
|
|
||||||
onPress={onPress}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an empty render item for an empty date
|
|
||||||
*
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
getRenderEmptyDate = () => <Divider/>;
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<View style={{flex: 1}}>
|
|
||||||
<CustomAgenda
|
|
||||||
{...this.props}
|
|
||||||
// the list of items that have to be displayed in agenda. If you want to render item as empty date
|
|
||||||
// the value of date key kas to be an empty array []. If there exists no value for date key it is
|
|
||||||
// considered that the date in question is not yet loaded
|
|
||||||
items={this.state.agendaItems}
|
|
||||||
// initially selected day
|
|
||||||
selected={this.currentDate}
|
|
||||||
// Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
|
|
||||||
minDate={this.currentDate}
|
|
||||||
// Max amount of months allowed to scroll to the past. Default = 50
|
|
||||||
pastScrollRange={1}
|
|
||||||
// Max amount of months allowed to scroll to the future. Default = 50
|
|
||||||
futureScrollRange={AGENDA_MONTH_SPAN}
|
|
||||||
// If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
|
|
||||||
onRefresh={this.onRefresh}
|
|
||||||
// callback that fires when the calendar is opened or closed
|
|
||||||
onCalendarToggled={this.onCalendarToggled}
|
|
||||||
// Set this true while waiting for new data from a refresh
|
|
||||||
refreshing={this.state.refreshing}
|
|
||||||
renderItem={this.getRenderItem}
|
|
||||||
renderEmptyDate={this.getRenderEmptyDate}
|
|
||||||
rowHasChanged={this.rowHasChanged}
|
|
||||||
// If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
|
|
||||||
firstDay={1}
|
|
||||||
// ref to this agenda in order to handle back button event
|
|
||||||
onRef={this.onAgendaRef}
|
|
||||||
/>
|
|
||||||
<MascotPopup
|
|
||||||
prefKey={AsyncStorageManager.PREFERENCES.eventsShowBanner.key}
|
|
||||||
title={i18n.t("screens.planning.mascotDialog.title")}
|
|
||||||
message={i18n.t("screens.planning.mascotDialog.message")}
|
|
||||||
icon={"party-popper"}
|
|
||||||
buttons={{
|
|
||||||
action: null,
|
|
||||||
cancel: {
|
|
||||||
message: i18n.t("screens.planning.mascotDialog.button"),
|
|
||||||
icon: "check",
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
emotion={MASCOT_STYLE.HAPPY}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PlanningScreen;
|
export default PlanningScreen;
|
||||||
|
|
|
@ -1,74 +1,20 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
export type eventObject = {
|
export type PlanningEventType = {
|
||||||
id: number,
|
id: number,
|
||||||
title: string,
|
title: string,
|
||||||
logo: string,
|
logo: string,
|
||||||
date_begin: string,
|
date_begin: string,
|
||||||
date_end: string,
|
date_end: string,
|
||||||
description: string,
|
description: string,
|
||||||
club: string,
|
club: string,
|
||||||
category_id: number,
|
category_id: number,
|
||||||
url: string,
|
url: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Regex used to check date string validity
|
// Regex used to check date string validity
|
||||||
const dateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/;
|
const dateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/;
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current day string representation in the format
|
|
||||||
* YYYY-MM-DD
|
|
||||||
*
|
|
||||||
* @return {string} The string representation
|
|
||||||
*/
|
|
||||||
export function getCurrentDateString(): string {
|
|
||||||
return dateToString(new Date(Date.now()), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given date is before the other.
|
|
||||||
*
|
|
||||||
* @param event1Date Event 1 date in format YYYY-MM-DD HH:MM
|
|
||||||
* @param event2Date Event 2 date in format YYYY-MM-DD HH:MM
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
export function isEventBefore(event1Date: string, event2Date: string): boolean {
|
|
||||||
let date1 = stringToDate(event1Date);
|
|
||||||
let date2 = stringToDate(event2Date);
|
|
||||||
if (date1 !== null && date2 !== null)
|
|
||||||
return date1 < date2;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets only the date part of the given event date string in the format
|
|
||||||
* YYYY-MM-DD HH:MM
|
|
||||||
*
|
|
||||||
* @param dateString The string to get the date from
|
|
||||||
* @return {string|null} Date in format YYYY:MM:DD or null if given string is invalid
|
|
||||||
*/
|
|
||||||
export function getDateOnlyString(dateString: string): string | null {
|
|
||||||
if (isEventDateStringFormatValid(dateString))
|
|
||||||
return dateString.split(" ")[0];
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets only the time part of the given event date string in the format
|
|
||||||
* YYYY-MM-DD HH:MM
|
|
||||||
*
|
|
||||||
* @param dateString The string to get the date from
|
|
||||||
* @return {string|null} Time in format HH:MM or null if given string is invalid
|
|
||||||
*/
|
|
||||||
export function getTimeOnlyString(dateString: string): string | null {
|
|
||||||
if (isEventDateStringFormatValid(dateString))
|
|
||||||
return dateString.split(" ")[1];
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the given date string is in the format
|
* Checks if the given date string is in the format
|
||||||
* YYYY-MM-DD HH:MM
|
* YYYY-MM-DD HH:MM
|
||||||
|
@ -77,9 +23,11 @@ export function getTimeOnlyString(dateString: string): string | null {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
export function isEventDateStringFormatValid(dateString: ?string): boolean {
|
export function isEventDateStringFormatValid(dateString: ?string): boolean {
|
||||||
return dateString !== undefined
|
return (
|
||||||
&& dateString !== null
|
dateString !== undefined &&
|
||||||
&& dateRegExp.test(dateString);
|
dateString !== null &&
|
||||||
|
dateRegExp.test(dateString)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,26 +38,20 @@ export function isEventDateStringFormatValid(dateString: ?string): boolean {
|
||||||
* @return {Date|null} The date object or null if the given string is invalid
|
* @return {Date|null} The date object or null if the given string is invalid
|
||||||
*/
|
*/
|
||||||
export function stringToDate(dateString: string): Date | null {
|
export function stringToDate(dateString: string): Date | null {
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
if (isEventDateStringFormatValid(dateString)) {
|
if (isEventDateStringFormatValid(dateString)) {
|
||||||
let stringArray = dateString.split(' ');
|
const stringArray = dateString.split(' ');
|
||||||
let dateArray = stringArray[0].split('-');
|
const dateArray = stringArray[0].split('-');
|
||||||
let timeArray = stringArray[1].split(':');
|
const timeArray = stringArray[1].split(':');
|
||||||
date.setFullYear(
|
date.setFullYear(
|
||||||
parseInt(dateArray[0]),
|
parseInt(dateArray[0], 10),
|
||||||
parseInt(dateArray[1]) - 1, // Month range from 0 to 11
|
parseInt(dateArray[1], 10) - 1, // Month range from 0 to 11
|
||||||
parseInt(dateArray[2])
|
parseInt(dateArray[2], 10),
|
||||||
);
|
);
|
||||||
date.setHours(
|
date.setHours(parseInt(timeArray[0], 10), parseInt(timeArray[1], 10), 0, 0);
|
||||||
parseInt(timeArray[0]),
|
} else date = null;
|
||||||
parseInt(timeArray[1]),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
} else
|
|
||||||
date = null;
|
|
||||||
|
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,13 +63,61 @@ export function stringToDate(dateString: string): Date | null {
|
||||||
* @return {string} The converted string
|
* @return {string} The converted string
|
||||||
*/
|
*/
|
||||||
export function dateToString(date: Date, isUTC: boolean): string {
|
export function dateToString(date: Date, isUTC: boolean): string {
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0'); //January is 0!
|
const month = String(date.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const h = isUTC ? date.getUTCHours() : date.getHours();
|
const h = isUTC ? date.getUTCHours() : date.getHours();
|
||||||
const hours = String(h).padStart(2, '0');
|
const hours = String(h).padStart(2, '0');
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes;
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current day string representation in the format
|
||||||
|
* YYYY-MM-DD
|
||||||
|
*
|
||||||
|
* @return {string} The string representation
|
||||||
|
*/
|
||||||
|
export function getCurrentDateString(): string {
|
||||||
|
return dateToString(new Date(Date.now()), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given date is before the other.
|
||||||
|
*
|
||||||
|
* @param event1Date Event 1 date in format YYYY-MM-DD HH:MM
|
||||||
|
* @param event2Date Event 2 date in format YYYY-MM-DD HH:MM
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export function isEventBefore(event1Date: string, event2Date: string): boolean {
|
||||||
|
const date1 = stringToDate(event1Date);
|
||||||
|
const date2 = stringToDate(event2Date);
|
||||||
|
if (date1 !== null && date2 !== null) return date1 < date2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets only the date part of the given event date string in the format
|
||||||
|
* YYYY-MM-DD HH:MM
|
||||||
|
*
|
||||||
|
* @param dateString The string to get the date from
|
||||||
|
* @return {string|null} Date in format YYYY:MM:DD or null if given string is invalid
|
||||||
|
*/
|
||||||
|
export function getDateOnlyString(dateString: string): string | null {
|
||||||
|
if (isEventDateStringFormatValid(dateString)) return dateString.split(' ')[0];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets only the time part of the given event date string in the format
|
||||||
|
* YYYY-MM-DD HH:MM
|
||||||
|
*
|
||||||
|
* @param dateString The string to get the date from
|
||||||
|
* @return {string|null} Time in format HH:MM or null if given string is invalid
|
||||||
|
*/
|
||||||
|
export function getTimeOnlyString(dateString: string): string | null {
|
||||||
|
if (isEventDateStringFormatValid(dateString)) return dateString.split(' ')[1];
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,26 +134,34 @@ export function dateToString(date: Date, isUTC: boolean): string {
|
||||||
* @return {string} Formatted string or "/ - /" on error
|
* @return {string} Formatted string or "/ - /" on error
|
||||||
*/
|
*/
|
||||||
export function getFormattedEventTime(start: string, end: string): string {
|
export function getFormattedEventTime(start: string, end: string): string {
|
||||||
let formattedStr = '/ - /';
|
let formattedStr = '/ - /';
|
||||||
let startDate = stringToDate(start);
|
const startDate = stringToDate(start);
|
||||||
let endDate = stringToDate(end);
|
const endDate = stringToDate(end);
|
||||||
|
|
||||||
if (startDate !== null && endDate !== null && startDate.getTime() !== endDate.getTime()) {
|
if (
|
||||||
formattedStr = String(startDate.getHours()).padStart(2, '0') + ':'
|
startDate !== null &&
|
||||||
+ String(startDate.getMinutes()).padStart(2, '0') + ' - ';
|
endDate !== null &&
|
||||||
if (endDate.getFullYear() > startDate.getFullYear()
|
startDate.getTime() !== endDate.getTime()
|
||||||
|| endDate.getMonth() > startDate.getMonth()
|
) {
|
||||||
|| endDate.getDate() > startDate.getDate())
|
formattedStr = `${String(startDate.getHours()).padStart(2, '0')}:${String(
|
||||||
formattedStr += '23:59';
|
startDate.getMinutes(),
|
||||||
else
|
).padStart(2, '0')} - `;
|
||||||
formattedStr += String(endDate.getHours()).padStart(2, '0') + ':'
|
if (
|
||||||
+ String(endDate.getMinutes()).padStart(2, '0');
|
endDate.getFullYear() > startDate.getFullYear() ||
|
||||||
} else if (startDate !== null)
|
endDate.getMonth() > startDate.getMonth() ||
|
||||||
formattedStr =
|
endDate.getDate() > startDate.getDate()
|
||||||
String(startDate.getHours()).padStart(2, '0') + ':'
|
)
|
||||||
+ String(startDate.getMinutes()).padStart(2, '0');
|
formattedStr += '23:59';
|
||||||
|
else
|
||||||
|
formattedStr += `${String(endDate.getHours()).padStart(2, '0')}:${String(
|
||||||
|
endDate.getMinutes(),
|
||||||
|
).padStart(2, '0')}`;
|
||||||
|
} else if (startDate !== null)
|
||||||
|
formattedStr = `${String(startDate.getHours()).padStart(2, '0')}:${String(
|
||||||
|
startDate.getMinutes(),
|
||||||
|
).padStart(2, '0')}`;
|
||||||
|
|
||||||
return formattedStr
|
return formattedStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,13 +174,19 @@ export function getFormattedEventTime(start: string, end: string): string {
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
export function isDescriptionEmpty(description: ?string): boolean {
|
export function isDescriptionEmpty(description: ?string): boolean {
|
||||||
if (description !== undefined && description !== null) {
|
if (description !== undefined && description !== null) {
|
||||||
return description
|
return (
|
||||||
.split('<p>').join('') // Equivalent to a replace all
|
description
|
||||||
.split('</p>').join('')
|
.split('<p>')
|
||||||
.split('<br>').join('').trim() === '';
|
.join('') // Equivalent to a replace all
|
||||||
} else
|
.split('</p>')
|
||||||
return true;
|
.join('')
|
||||||
|
.split('<br>')
|
||||||
|
.join('')
|
||||||
|
.trim() === ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,17 +197,43 @@ export function isDescriptionEmpty(description: ?string): boolean {
|
||||||
* @param numberOfMonths The number of months to create, starting from the current date
|
* @param numberOfMonths The number of months to create, starting from the current date
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
export function generateEmptyCalendar(numberOfMonths: number): Object {
|
export function generateEmptyCalendar(
|
||||||
let end = new Date(Date.now());
|
numberOfMonths: number,
|
||||||
end.setMonth(end.getMonth() + numberOfMonths);
|
): {[key: string]: Array<PlanningEventType>} {
|
||||||
let daysOfYear = {};
|
const end = new Date(Date.now());
|
||||||
for (let d = new Date(Date.now()); d <= end; d.setDate(d.getDate() + 1)) {
|
end.setMonth(end.getMonth() + numberOfMonths);
|
||||||
const dateString = getDateOnlyString(
|
const daysOfYear = {};
|
||||||
dateToString(new Date(d), false));
|
for (let d = new Date(Date.now()); d <= end; d.setDate(d.getDate() + 1)) {
|
||||||
if (dateString !== null)
|
const dateString = getDateOnlyString(dateToString(new Date(d), false));
|
||||||
daysOfYear[dateString] = []
|
if (dateString !== null) daysOfYear[dateString] = [];
|
||||||
|
}
|
||||||
|
return daysOfYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds events to the given array depending on their starting date.
|
||||||
|
*
|
||||||
|
* Events starting before are added at the front.
|
||||||
|
*
|
||||||
|
* @param eventArray The array to hold sorted events
|
||||||
|
* @param event The event to add to the array
|
||||||
|
*/
|
||||||
|
export function pushEventInOrder(
|
||||||
|
eventArray: Array<PlanningEventType>,
|
||||||
|
event: PlanningEventType,
|
||||||
|
) {
|
||||||
|
if (eventArray.length === 0) eventArray.push(event);
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < eventArray.length; i += 1) {
|
||||||
|
if (isEventBefore(event.date_begin, eventArray[i].date_begin)) {
|
||||||
|
eventArray.splice(i, 0, event);
|
||||||
|
break;
|
||||||
|
} else if (i === eventArray.length - 1) {
|
||||||
|
eventArray.push(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return daysOfYear;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -217,40 +247,17 @@ export function generateEmptyCalendar(numberOfMonths: number): Object {
|
||||||
* @param numberOfMonths The number of months to create the agenda for
|
* @param numberOfMonths The number of months to create the agenda for
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
export function generateEventAgenda(eventList: Array<eventObject>, numberOfMonths: number): Object {
|
export function generateEventAgenda(
|
||||||
let agendaItems = generateEmptyCalendar(numberOfMonths);
|
eventList: Array<PlanningEventType>,
|
||||||
for (let i = 0; i < eventList.length; i++) {
|
numberOfMonths: number,
|
||||||
const dateString = getDateOnlyString(eventList[i].date_begin);
|
): {[key: string]: Array<PlanningEventType>} {
|
||||||
if (dateString !== null) {
|
const agendaItems = generateEmptyCalendar(numberOfMonths);
|
||||||
const eventArray = agendaItems[dateString];
|
for (let i = 0; i < eventList.length; i += 1) {
|
||||||
if (eventArray !== undefined)
|
const dateString = getDateOnlyString(eventList[i].date_begin);
|
||||||
pushEventInOrder(eventArray, eventList[i]);
|
if (dateString != null) {
|
||||||
}
|
const eventArray = agendaItems[dateString];
|
||||||
|
if (eventArray != null) pushEventInOrder(eventArray, eventList[i]);
|
||||||
}
|
|
||||||
return agendaItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds events to the given array depending on their starting date.
|
|
||||||
*
|
|
||||||
* Events starting before are added at the front.
|
|
||||||
*
|
|
||||||
* @param eventArray The array to hold sorted events
|
|
||||||
* @param event The event to add to the array
|
|
||||||
*/
|
|
||||||
export function pushEventInOrder(eventArray: Array<eventObject>, event: eventObject): Object {
|
|
||||||
if (eventArray.length === 0)
|
|
||||||
eventArray.push(event);
|
|
||||||
else {
|
|
||||||
for (let i = 0; i < eventArray.length; i++) {
|
|
||||||
if (isEventBefore(event.date_begin, eventArray[i].date_begin)) {
|
|
||||||
eventArray.splice(i, 0, event);
|
|
||||||
break;
|
|
||||||
} else if (i === eventArray.length - 1) {
|
|
||||||
eventArray.push(event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return agendaItems;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,15 +100,17 @@ export async function apiRequest(
|
||||||
* If no data was found, returns an empty object
|
* If no data was found, returns an empty object
|
||||||
*
|
*
|
||||||
* @param url The urls to fetch data from
|
* @param url The urls to fetch data from
|
||||||
* @return Promise<{...}>
|
* @return Promise<any>
|
||||||
*/
|
*/
|
||||||
export async function readData(url: string): Promise<{...}> {
|
// eslint-disable-next-line flowtype/no-weak-types
|
||||||
return new Promise(
|
export async function readData(url: string): Promise<any> {
|
||||||
(resolve: (response: {...}) => void, reject: () => void) => {
|
// eslint-disable-next-line flowtype/no-weak-types
|
||||||
fetch(url)
|
return new Promise((resolve: (response: any) => void, reject: () => void) => {
|
||||||
.then(async (response: Response): Promise<{...}> => response.json())
|
fetch(url)
|
||||||
.then((data: {...}): void => resolve(data))
|
// eslint-disable-next-line flowtype/no-weak-types
|
||||||
.catch((): void => reject());
|
.then(async (response: Response): Promise<any> => response.json())
|
||||||
},
|
// eslint-disable-next-line flowtype/no-weak-types
|
||||||
);
|
.then((data: any): void => resolve(data))
|
||||||
|
.catch((): void => reject());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue