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,40 +2,43 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
|
||||
import {Card, withTheme} from 'react-native-paper';
|
||||
import DateManager from "../../managers/DateManager";
|
||||
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 {StackNavigationProp} from "@react-navigation/stack";
|
||||
import type {CustomTheme} from "../../managers/ThemeManager";
|
||||
import CollapsibleScrollView from "../../components/Collapsible/CollapsibleScrollView";
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import {getDateOnlyString, getFormattedEventTime} from '../../utils/Planning';
|
||||
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,
|
||||
route: { params: { data: Object, id: number, eventId: number } },
|
||||
theme: CustomTheme
|
||||
route: {params: {data: PlanningEventType, id: number, eventId: number}},
|
||||
theme: CustomThemeType,
|
||||
};
|
||||
|
||||
type State = {
|
||||
loading: boolean
|
||||
type StateType = {
|
||||
loading: boolean,
|
||||
};
|
||||
|
||||
const CLUB_INFO_PATH = "event/info";
|
||||
const EVENT_INFO_URL = 'event/info';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
eventId: number;
|
||||
|
||||
errorCode: number;
|
||||
|
||||
/**
|
||||
|
@ -43,11 +46,11 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
|
|||
*
|
||||
* @param props
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
|
||||
if (this.props.route.params.data != null) {
|
||||
this.displayData = this.props.route.params.data;
|
||||
if (props.route.params.data != null) {
|
||||
this.displayData = props.route.params.data;
|
||||
this.eventId = this.displayData.id;
|
||||
this.shouldFetchData = false;
|
||||
this.errorCode = 0;
|
||||
|
@ -56,33 +59,22 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
|
|||
};
|
||||
} else {
|
||||
this.displayData = null;
|
||||
this.eventId = this.props.route.params.eventId;
|
||||
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
|
||||
*/
|
||||
fetchData = () => {
|
||||
this.setState({loading: true});
|
||||
apiRequest(CLUB_INFO_PATH, 'POST', {id: this.eventId})
|
||||
.then(this.onFetchSuccess)
|
||||
.catch(this.onFetchError);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides loading and saves fetched data
|
||||
*
|
||||
* @param data Received data
|
||||
*/
|
||||
onFetchSuccess = (data: Object) => {
|
||||
onFetchSuccess = (data: PlanningEventType) => {
|
||||
this.displayData = data;
|
||||
this.setState({loading: false});
|
||||
};
|
||||
|
@ -102,41 +94,46 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
|
|||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
getContent() {
|
||||
getContent(): React.Node {
|
||||
const {theme} = this.props;
|
||||
const {displayData} = this;
|
||||
if (displayData == null) return null;
|
||||
let subtitle = getFormattedEventTime(
|
||||
this.displayData["date_begin"], this.displayData["date_end"]);
|
||||
let dateString = getDateOnlyString(this.displayData["date_begin"]);
|
||||
displayData.date_begin,
|
||||
displayData.date_end,
|
||||
);
|
||||
const dateString = getDateOnlyString(displayData.date_begin);
|
||||
if (dateString !== null)
|
||||
subtitle += ' | ' + DateManager.getInstance().getTranslatedDate(dateString);
|
||||
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 ?
|
||||
<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={this.props.theme.colors.background}
|
||||
imageBackgroundColor={theme.colors.background}
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
}}
|
||||
source={{
|
||||
uri: this.displayData.logo,
|
||||
uri: displayData.logo,
|
||||
}}
|
||||
/></View>
|
||||
: <View/>}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{this.displayData.description !== null ?
|
||||
<Card.Content style={{paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||
<CustomHTML html={this.displayData.description}/>
|
||||
{displayData.description !== null ? (
|
||||
<Card.Content
|
||||
style={{paddingBottom: CustomTabBar.TAB_BAR_HEIGHT + 20}}>
|
||||
<CustomHTML html={displayData.description} />
|
||||
</Card.Content>
|
||||
: <View/>}
|
||||
) : (
|
||||
<View />
|
||||
)}
|
||||
</CollapsibleScrollView>
|
||||
);
|
||||
}
|
||||
|
@ -146,20 +143,40 @@ class PlanningDisplayScreen extends React.Component<Props, State> {
|
|||
*
|
||||
* @returns {*}
|
||||
*/
|
||||
getErrorView() {
|
||||
getErrorView(): React.Node {
|
||||
const {navigation} = this.props;
|
||||
if (this.errorCode === ERROR_TYPE.BAD_INPUT)
|
||||
return <ErrorView {...this.props} showRetryButton={false} message={i18n.t("screens.planning.invalidEvent")}
|
||||
icon={"calendar-remove"}/>;
|
||||
else
|
||||
return <ErrorView {...this.props} errorCode={this.errorCode} onRefresh={this.fetchData}/>;
|
||||
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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.loading)
|
||||
return <BasicLoadingScreen/>;
|
||||
else if (this.errorCode === 0)
|
||||
return this.getContent();
|
||||
else
|
||||
/**
|
||||
* Fetches data for the current event id from the API
|
||||
*/
|
||||
fetchData = () => {
|
||||
this.setState({loading: true});
|
||||
apiRequest(EVENT_INFO_URL, 'POST', {id: this.eventId})
|
||||
.then(this.onFetchSuccess)
|
||||
.catch(this.onFetchError);
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {loading} = this.state;
|
||||
if (loading) return <BasicLoadingScreen />;
|
||||
if (this.errorCode === 0) return this.getContent();
|
||||
return this.getErrorView();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,91 +2,120 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import {BackHandler, View} from 'react-native';
|
||||
import i18n from "i18n-js";
|
||||
import {LocaleConfig} from 'react-native-calendars';
|
||||
import {readData} from "../../utils/WebData";
|
||||
import type {eventObject} from "../../utils/Planning";
|
||||
import i18n from 'i18n-js';
|
||||
import {Agenda, LocaleConfig} from 'react-native-calendars';
|
||||
import {Avatar, Divider, List} from 'react-native-paper';
|
||||
import {StackNavigationProp} from '@react-navigation/stack';
|
||||
import {readData} from '../../utils/WebData';
|
||||
import type {PlanningEventType} from '../../utils/Planning';
|
||||
import {
|
||||
generateEventAgenda,
|
||||
getCurrentDateString,
|
||||
getDateOnlyString,
|
||||
getFormattedEventTime,
|
||||
} from '../../utils/Planning';
|
||||
import {Avatar, Divider, List} from 'react-native-paper';
|
||||
import CustomAgenda from "../../components/Overrides/CustomAgenda";
|
||||
import {StackNavigationProp} from "@react-navigation/stack";
|
||||
import {MASCOT_STYLE} from "../../components/Mascot/Mascot";
|
||||
import MascotPopup from "../../components/Mascot/MascotPopup";
|
||||
import AsyncStorageManager from "../../managers/AsyncStorageManager";
|
||||
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'] = {
|
||||
monthNames: ['Janvier', 'Février', 'Mars', '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'],
|
||||
LocaleConfig.locales.fr = {
|
||||
monthNames: [
|
||||
'Janvier',
|
||||
'Février',
|
||||
'Mars',
|
||||
'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'
|
||||
today: "Aujourd'hui",
|
||||
};
|
||||
|
||||
|
||||
type Props = {
|
||||
type PropsType = {
|
||||
navigation: StackNavigationProp,
|
||||
}
|
||||
};
|
||||
|
||||
type State = {
|
||||
type StateType = {
|
||||
refreshing: boolean,
|
||||
agendaItems: Object,
|
||||
agendaItems: {[key: string]: Array<PlanningEventType>},
|
||||
calendarShowing: boolean,
|
||||
};
|
||||
|
||||
const FETCH_URL = "https://www.amicale-insat.fr/api/event/list";
|
||||
const FETCH_URL = 'https://www.amicale-insat.fr/api/event/list';
|
||||
const AGENDA_MONTH_SPAN = 3;
|
||||
|
||||
/**
|
||||
* Class defining the app's planning screen
|
||||
*/
|
||||
class PlanningScreen extends React.Component<Props, State> {
|
||||
|
||||
agendaRef: Object;
|
||||
class PlanningScreen extends React.Component<PropsType, StateType> {
|
||||
agendaRef: null | Agenda;
|
||||
|
||||
lastRefresh: Date;
|
||||
|
||||
minTimeBetweenRefresh = 60;
|
||||
|
||||
state = {
|
||||
currentDate = getDateOnlyString(getCurrentDateString());
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
if (i18n.currentLocale().startsWith('fr')) {
|
||||
LocaleConfig.defaultLocale = 'fr';
|
||||
}
|
||||
this.state = {
|
||||
refreshing: false,
|
||||
agendaItems: {},
|
||||
calendarShowing: false,
|
||||
};
|
||||
|
||||
currentDate = getDateOnlyString(getCurrentDateString());
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
if (i18n.currentLocale().startsWith("fr")) {
|
||||
LocaleConfig.defaultLocale = 'fr';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures focus and blur events to hook on android back button
|
||||
*/
|
||||
componentDidMount() {
|
||||
const {navigation} = this.props;
|
||||
this.onRefresh();
|
||||
this.props.navigation.addListener(
|
||||
'focus',
|
||||
() =>
|
||||
navigation.addListener('focus', () => {
|
||||
BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid
|
||||
)
|
||||
this.onBackButtonPressAndroid,
|
||||
);
|
||||
this.props.navigation.addListener(
|
||||
'blur',
|
||||
() =>
|
||||
});
|
||||
navigation.addListener('blur', () => {
|
||||
BackHandler.removeEventListener(
|
||||
'hardwareBackPress',
|
||||
this.onBackButtonPressAndroid
|
||||
)
|
||||
this.onBackButtonPressAndroid,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,46 +123,33 @@ class PlanningScreen extends React.Component<Props, State> {
|
|||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
onBackButtonPressAndroid = () => {
|
||||
if (this.state.calendarShowing) {
|
||||
onBackButtonPressAndroid = (): boolean => {
|
||||
const {calendarShowing} = this.state;
|
||||
if (calendarShowing && this.agendaRef != null) {
|
||||
this.agendaRef.chooseDay(this.agendaRef.state.selectedDay);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function used to check if a row has changed
|
||||
*
|
||||
* @param r1
|
||||
* @param r2
|
||||
* @return {boolean}
|
||||
*/
|
||||
rowHasChanged(r1: Object, r2: Object) {
|
||||
return false;
|
||||
// if (r1 !== undefined && r2 !== undefined)
|
||||
// return r1.title !== r2.title;
|
||||
// else return !(r1 === undefined && r2 === undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes data and shows an animation while doing it
|
||||
*/
|
||||
onRefresh = () => {
|
||||
let canRefresh;
|
||||
if (this.lastRefresh !== undefined)
|
||||
canRefresh = (new Date().getTime() - this.lastRefresh.getTime()) / 1000 > this.minTimeBetweenRefresh;
|
||||
else
|
||||
canRefresh = true;
|
||||
canRefresh =
|
||||
(new Date().getTime() - this.lastRefresh.getTime()) / 1000 >
|
||||
this.minTimeBetweenRefresh;
|
||||
else canRefresh = true;
|
||||
|
||||
if (canRefresh) {
|
||||
this.setState({refreshing: true});
|
||||
readData(FETCH_URL)
|
||||
.then((fetchedData) => {
|
||||
.then((fetchedData: Array<PlanningEventType>) => {
|
||||
this.setState({
|
||||
refreshing: false,
|
||||
agendaItems: generateEventAgenda(fetchedData, AGENDA_MONTH_SPAN)
|
||||
agendaItems: generateEventAgenda(fetchedData, AGENDA_MONTH_SPAN),
|
||||
});
|
||||
this.lastRefresh = new Date();
|
||||
})
|
||||
|
@ -150,9 +166,9 @@ class PlanningScreen extends React.Component<Props, State> {
|
|||
*
|
||||
* @param ref
|
||||
*/
|
||||
onAgendaRef = (ref: Object) => {
|
||||
onAgendaRef = (ref: Agenda) => {
|
||||
this.agendaRef = ref;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback used when a button is pressed to toggle the calendar
|
||||
|
@ -161,7 +177,7 @@ class PlanningScreen extends React.Component<Props, State> {
|
|||
*/
|
||||
onCalendarToggled = (isCalendarOpened: boolean) => {
|
||||
this.setState({calendarShowing: isCalendarOpened});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an event render item
|
||||
|
@ -169,53 +185,61 @@ class PlanningScreen extends React.Component<Props, State> {
|
|||
* @param item The current event to render
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderItem = (item: eventObject) => {
|
||||
const onPress = this.props.navigation.navigate.bind(this, 'planning-information', {data: item});
|
||||
getRenderItem = (item: PlanningEventType): React.Node => {
|
||||
const {navigation} = this.props;
|
||||
const onPress = () => {
|
||||
navigation.navigate('planning-information', {
|
||||
data: item,
|
||||
});
|
||||
};
|
||||
if (item.logo !== null) {
|
||||
return (
|
||||
<View>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
<List.Item
|
||||
title={item.title}
|
||||
description={getFormattedEventTime(item["date_begin"], item["date_end"])}
|
||||
left={() => <Avatar.Image
|
||||
description={getFormattedEventTime(item.date_begin, item.date_end)}
|
||||
left={(): React.Node => (
|
||||
<Avatar.Image
|
||||
source={{uri: item.logo}}
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
/>}
|
||||
/>
|
||||
)}
|
||||
onPress={onPress}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
<Divider/>
|
||||
<Divider />
|
||||
<List.Item
|
||||
title={item.title}
|
||||
description={getFormattedEventTime(item["date_begin"], item["date_end"])}
|
||||
description={getFormattedEventTime(item.date_begin, item.date_end)}
|
||||
onPress={onPress}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an empty render item for an empty date
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
getRenderEmptyDate = () => <Divider/>;
|
||||
getRenderEmptyDate = (): React.Node => <Divider />;
|
||||
|
||||
render() {
|
||||
render(): React.Node {
|
||||
const {state, props} = this;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<CustomAgenda
|
||||
{...this.props}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...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}
|
||||
items={state.agendaItems}
|
||||
// initially selected day
|
||||
selected={this.currentDate}
|
||||
// Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
|
||||
|
@ -229,10 +253,9 @@ class PlanningScreen extends React.Component<Props, State> {
|
|||
// 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}
|
||||
refreshing={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
|
||||
|
@ -240,15 +263,15 @@ class PlanningScreen extends React.Component<Props, State> {
|
|||
/>
|
||||
<MascotPopup
|
||||
prefKey={AsyncStorageManager.PREFERENCES.eventsShowBanner.key}
|
||||
title={i18n.t("screens.planning.mascotDialog.title")}
|
||||
message={i18n.t("screens.planning.mascotDialog.message")}
|
||||
icon={"party-popper"}
|
||||
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",
|
||||
}
|
||||
message: i18n.t('screens.planning.mascotDialog.button'),
|
||||
icon: 'check',
|
||||
},
|
||||
}}
|
||||
emotion={MASCOT_STYLE.HAPPY}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
export type eventObject = {
|
||||
export type PlanningEventType = {
|
||||
id: number,
|
||||
title: string,
|
||||
logo: string,
|
||||
|
@ -15,6 +15,63 @@ export type eventObject = {
|
|||
// Regex used to check date string validity
|
||||
const dateRegExp = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/;
|
||||
|
||||
/**
|
||||
* Checks if the given date string is in the format
|
||||
* YYYY-MM-DD HH:MM
|
||||
*
|
||||
* @param dateString The string to check
|
||||
* @return {boolean}
|
||||
*/
|
||||
export function isEventDateStringFormatValid(dateString: ?string): boolean {
|
||||
return (
|
||||
dateString !== undefined &&
|
||||
dateString !== null &&
|
||||
dateRegExp.test(dateString)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given date string to a date object.<br>
|
||||
* Accepted format: YYYY-MM-DD HH:MM
|
||||
*
|
||||
* @param dateString The string to convert
|
||||
* @return {Date|null} The date object or null if the given string is invalid
|
||||
*/
|
||||
export function stringToDate(dateString: string): Date | null {
|
||||
let date = new Date();
|
||||
if (isEventDateStringFormatValid(dateString)) {
|
||||
const stringArray = dateString.split(' ');
|
||||
const dateArray = stringArray[0].split('-');
|
||||
const timeArray = stringArray[1].split(':');
|
||||
date.setFullYear(
|
||||
parseInt(dateArray[0], 10),
|
||||
parseInt(dateArray[1], 10) - 1, // Month range from 0 to 11
|
||||
parseInt(dateArray[2], 10),
|
||||
);
|
||||
date.setHours(parseInt(timeArray[0], 10), parseInt(timeArray[1], 10), 0, 0);
|
||||
} else date = null;
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a date object to a string in the format
|
||||
* YYYY-MM-DD HH-MM
|
||||
*
|
||||
* @param date The date object to convert
|
||||
* @param isUTC Whether to treat the date as UTC
|
||||
* @return {string} The converted string
|
||||
*/
|
||||
export function dateToString(date: Date, isUTC: boolean): string {
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||
const year = date.getFullYear();
|
||||
const h = isUTC ? date.getUTCHours() : date.getHours();
|
||||
const hours = String(h).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current day string representation in the format
|
||||
* YYYY-MM-DD
|
||||
|
@ -33,11 +90,9 @@ export function getCurrentDateString(): string {
|
|||
* @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
|
||||
const date1 = stringToDate(event1Date);
|
||||
const date2 = stringToDate(event2Date);
|
||||
if (date1 !== null && date2 !== null) return date1 < date2;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -49,9 +104,7 @@ export function isEventBefore(event1Date: string, event2Date: string): boolean {
|
|||
* @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
|
||||
if (isEventDateStringFormatValid(dateString)) return dateString.split(' ')[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -63,73 +116,10 @@ export function getDateOnlyString(dateString: string): string | null {
|
|||
* @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
|
||||
if (isEventDateStringFormatValid(dateString)) return dateString.split(' ')[1];
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given date string is in the format
|
||||
* YYYY-MM-DD HH:MM
|
||||
*
|
||||
* @param dateString The string to check
|
||||
* @return {boolean}
|
||||
*/
|
||||
export function isEventDateStringFormatValid(dateString: ?string): boolean {
|
||||
return dateString !== undefined
|
||||
&& dateString !== null
|
||||
&& dateRegExp.test(dateString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given date string to a date object.<br>
|
||||
* Accepted format: YYYY-MM-DD HH:MM
|
||||
*
|
||||
* @param dateString The string to convert
|
||||
* @return {Date|null} The date object or null if the given string is invalid
|
||||
*/
|
||||
export function stringToDate(dateString: string): Date | null {
|
||||
let date = new Date();
|
||||
if (isEventDateStringFormatValid(dateString)) {
|
||||
let stringArray = dateString.split(' ');
|
||||
let dateArray = stringArray[0].split('-');
|
||||
let timeArray = stringArray[1].split(':');
|
||||
date.setFullYear(
|
||||
parseInt(dateArray[0]),
|
||||
parseInt(dateArray[1]) - 1, // Month range from 0 to 11
|
||||
parseInt(dateArray[2])
|
||||
);
|
||||
date.setHours(
|
||||
parseInt(timeArray[0]),
|
||||
parseInt(timeArray[1]),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
} else
|
||||
date = null;
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a date object to a string in the format
|
||||
* YYYY-MM-DD HH-MM
|
||||
*
|
||||
* @param date The date object to convert
|
||||
* @param isUTC Whether to treat the date as UTC
|
||||
* @return {string} The converted string
|
||||
*/
|
||||
export function dateToString(date: Date, isUTC: boolean): string {
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); //January is 0!
|
||||
const year = date.getFullYear();
|
||||
const h = isUTC ? date.getUTCHours() : date.getHours();
|
||||
const hours = String(h).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string corresponding to the event start and end times in the following format:
|
||||
*
|
||||
|
@ -145,25 +135,33 @@ export function dateToString(date: Date, isUTC: boolean): string {
|
|||
*/
|
||||
export function getFormattedEventTime(start: string, end: string): string {
|
||||
let formattedStr = '/ - /';
|
||||
let startDate = stringToDate(start);
|
||||
let endDate = stringToDate(end);
|
||||
const startDate = stringToDate(start);
|
||||
const endDate = stringToDate(end);
|
||||
|
||||
if (startDate !== null && endDate !== null && startDate.getTime() !== endDate.getTime()) {
|
||||
formattedStr = String(startDate.getHours()).padStart(2, '0') + ':'
|
||||
+ String(startDate.getMinutes()).padStart(2, '0') + ' - ';
|
||||
if (endDate.getFullYear() > startDate.getFullYear()
|
||||
|| endDate.getMonth() > startDate.getMonth()
|
||||
|| endDate.getDate() > startDate.getDate())
|
||||
if (
|
||||
startDate !== null &&
|
||||
endDate !== null &&
|
||||
startDate.getTime() !== endDate.getTime()
|
||||
) {
|
||||
formattedStr = `${String(startDate.getHours()).padStart(2, '0')}:${String(
|
||||
startDate.getMinutes(),
|
||||
).padStart(2, '0')} - `;
|
||||
if (
|
||||
endDate.getFullYear() > startDate.getFullYear() ||
|
||||
endDate.getMonth() > startDate.getMonth() ||
|
||||
endDate.getDate() > startDate.getDate()
|
||||
)
|
||||
formattedStr += '23:59';
|
||||
else
|
||||
formattedStr += String(endDate.getHours()).padStart(2, '0') + ':'
|
||||
+ String(endDate.getMinutes()).padStart(2, '0');
|
||||
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');
|
||||
formattedStr = `${String(startDate.getHours()).padStart(2, '0')}:${String(
|
||||
startDate.getMinutes(),
|
||||
).padStart(2, '0')}`;
|
||||
|
||||
return formattedStr
|
||||
return formattedStr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,11 +175,17 @@ export function getFormattedEventTime(start: string, end: string): string {
|
|||
*/
|
||||
export function isDescriptionEmpty(description: ?string): boolean {
|
||||
if (description !== undefined && description !== null) {
|
||||
return description
|
||||
.split('<p>').join('') // Equivalent to a replace all
|
||||
.split('</p>').join('')
|
||||
.split('<br>').join('').trim() === '';
|
||||
} else
|
||||
return (
|
||||
description
|
||||
.split('<p>')
|
||||
.join('') // Equivalent to a replace all
|
||||
.split('</p>')
|
||||
.join('')
|
||||
.split('<br>')
|
||||
.join('')
|
||||
.trim() === ''
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -193,19 +197,45 @@ export function isDescriptionEmpty(description: ?string): boolean {
|
|||
* @param numberOfMonths The number of months to create, starting from the current date
|
||||
* @return {Object}
|
||||
*/
|
||||
export function generateEmptyCalendar(numberOfMonths: number): Object {
|
||||
let end = new Date(Date.now());
|
||||
export function generateEmptyCalendar(
|
||||
numberOfMonths: number,
|
||||
): {[key: string]: Array<PlanningEventType>} {
|
||||
const end = new Date(Date.now());
|
||||
end.setMonth(end.getMonth() + numberOfMonths);
|
||||
let daysOfYear = {};
|
||||
const daysOfYear = {};
|
||||
for (let d = new Date(Date.now()); d <= end; d.setDate(d.getDate() + 1)) {
|
||||
const dateString = getDateOnlyString(
|
||||
dateToString(new Date(d), false));
|
||||
if (dateString !== null)
|
||||
daysOfYear[dateString] = []
|
||||
const dateString = getDateOnlyString(dateToString(new Date(d), false));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an object with an array of eventObject at each key.
|
||||
* Each key is a date string in the format
|
||||
|
@ -217,40 +247,17 @@ export function generateEmptyCalendar(numberOfMonths: number): Object {
|
|||
* @param numberOfMonths The number of months to create the agenda for
|
||||
* @return {Object}
|
||||
*/
|
||||
export function generateEventAgenda(eventList: Array<eventObject>, numberOfMonths: number): Object {
|
||||
let agendaItems = generateEmptyCalendar(numberOfMonths);
|
||||
for (let i = 0; i < eventList.length; i++) {
|
||||
export function generateEventAgenda(
|
||||
eventList: Array<PlanningEventType>,
|
||||
numberOfMonths: number,
|
||||
): {[key: string]: Array<PlanningEventType>} {
|
||||
const agendaItems = generateEmptyCalendar(numberOfMonths);
|
||||
for (let i = 0; i < eventList.length; i += 1) {
|
||||
const dateString = getDateOnlyString(eventList[i].date_begin);
|
||||
if (dateString !== null) {
|
||||
if (dateString != null) {
|
||||
const eventArray = agendaItems[dateString];
|
||||
if (eventArray !== undefined)
|
||||
pushEventInOrder(eventArray, eventList[i]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,15 +100,17 @@ export async function apiRequest(
|
|||
* If no data was found, returns an empty object
|
||||
*
|
||||
* @param url The urls to fetch data from
|
||||
* @return Promise<{...}>
|
||||
* @return Promise<any>
|
||||
*/
|
||||
export async function readData(url: string): Promise<{...}> {
|
||||
return new Promise(
|
||||
(resolve: (response: {...}) => void, reject: () => void) => {
|
||||
// eslint-disable-next-line flowtype/no-weak-types
|
||||
export async function readData(url: string): Promise<any> {
|
||||
// eslint-disable-next-line flowtype/no-weak-types
|
||||
return new Promise((resolve: (response: any) => void, reject: () => void) => {
|
||||
fetch(url)
|
||||
.then(async (response: Response): Promise<{...}> => response.json())
|
||||
.then((data: {...}): void => resolve(data))
|
||||
// eslint-disable-next-line flowtype/no-weak-types
|
||||
.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