123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- /*
- * 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 <https://www.gnu.org/licenses/>.
- */
-
- import * as React from 'react';
- import { BackHandler, StyleSheet, View } from 'react-native';
- 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 {
- generateEventAgenda,
- getCurrentDateString,
- getDateOnlyString,
- getTimeOnlyString,
- PlanningEventType,
- } from '../../utils/Planning';
- import CustomAgenda from '../../components/Overrides/CustomAgenda';
- import { MASCOT_STYLE } from '../../components/Mascot/Mascot';
- import MascotPopup from '../../components/Mascot/MascotPopup';
- import GENERAL_STYLES from '../../constants/Styles';
- import Urls from '../../constants/Urls';
-
- 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",
- };
-
- type PropsType = {
- navigation: StackNavigationProp<any>;
- };
-
- type StateType = {
- refreshing: boolean;
- agendaItems: { [key: string]: Array<PlanningEventType> };
- calendarShowing: boolean;
- };
-
- const AGENDA_MONTH_SPAN = 3;
-
- const styles = StyleSheet.create({
- icon: {
- backgroundColor: 'transparent',
- },
- });
-
- /**
- * Class defining the app's planning screen
- */
- class PlanningScreen extends React.Component<PropsType, StateType> {
- agendaRef: null | Agenda<any>;
-
- lastRefresh: Date | null;
-
- minTimeBetweenRefresh = 60;
-
- currentDate: string | null;
-
- constructor(props: PropsType) {
- super(props);
- if (i18n.currentLocale().startsWith('fr')) {
- LocaleConfig.defaultLocale = 'fr';
- }
- this.agendaRef = null;
- this.currentDate = getDateOnlyString(getCurrentDateString());
- this.lastRefresh = null;
- this.state = {
- refreshing: false,
- agendaItems: {},
- calendarShowing: false,
- };
- }
-
- /**
- * 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
- );
- });
- }
-
- /**
- * Overrides default android back button behaviour to close the calendar if it was open.
- *
- * @return {boolean}
- */
- onBackButtonPressAndroid = (): boolean => {
- const { calendarShowing } = this.state;
- if (calendarShowing && this.agendaRef != null) {
- // @ts-ignore
- this.agendaRef.chooseDay(this.agendaRef.state.selectedDay);
- return true;
- }
- return false;
- };
-
- /**
- * Refreshes data and shows an animation while doing it
- */
- onRefresh = () => {
- let canRefresh;
- if (this.lastRefresh) {
- canRefresh =
- (new Date().getTime() - this.lastRefresh.getTime()) / 1000 >
- this.minTimeBetweenRefresh;
- } else {
- canRefresh = true;
- }
-
- if (canRefresh) {
- this.setState({ refreshing: true });
- readData<Array<PlanningEventType>>(Urls.amicale.events)
- .then((fetchedData) => {
- this.setState({
- refreshing: false,
- agendaItems: generateEventAgenda(fetchedData, AGENDA_MONTH_SPAN),
- });
- this.lastRefresh = new Date();
- })
- .catch(() => {
- this.setState({
- refreshing: false,
- });
- });
- }
- };
-
- /**
- * Callback used when receiving the agenda ref
- *
- * @param ref
- */
- onAgendaRef = (ref: Agenda<any>) => {
- this.agendaRef = ref;
- };
-
- /**
- * 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) => {
- const { navigation } = this.props;
- const onPress = () => {
- navigation.navigate('planning-information', {
- data: item,
- });
- };
- const logo = item.logo;
- if (logo) {
- return (
- <View>
- <Divider />
- <List.Item
- title={item.title}
- description={getTimeOnlyString(item.date_begin)}
- left={() => (
- <Avatar.Image source={{ uri: logo }} style={styles.icon} />
- )}
- onPress={onPress}
- />
- </View>
- );
- }
- return (
- <View>
- <Divider />
- <List.Item
- title={item.title}
- description={getTimeOnlyString(item.date_begin)}
- onPress={onPress}
- />
- </View>
- );
- };
-
- /**
- * Gets an empty render item for an empty date
- *
- * @return {*}
- */
- getRenderEmptyDate = () => <Divider />;
-
- render() {
- const { state, props } = this;
- return (
- <View style={GENERAL_STYLES.flex}>
- <CustomAgenda
- {...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={state.agendaItems}
- // initially selected day
- selected={this.currentDate ? this.currentDate : undefined}
- // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined
- minDate={this.currentDate ? this.currentDate : undefined}
- // 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={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.
- firstDay={1}
- // ref to this agenda in order to handle back button event
- onRef={this.onAgendaRef}
- rowHasChanged={(r1: PlanningEventType, r2: PlanningEventType) =>
- r1.id !== r2.id
- }
- />
- <MascotPopup
- title={i18n.t('screens.planning.mascotDialog.title')}
- message={i18n.t('screens.planning.mascotDialog.message')}
- icon="party-popper"
- buttons={{
- cancel: {
- message: i18n.t('screens.planning.mascotDialog.button'),
- icon: 'check',
- },
- }}
- emotion={MASCOT_STYLE.HAPPY}
- />
- </View>
- );
- }
- }
-
- export default PlanningScreen;
|