AIK
AIK

Reputation: 528

React Native Calendar Agenda week view not rendering properly

I started using react native calendars for my app. I used Agenda in one of my screens. There is a strange issue however when selecting dates. I have set pastScrollRange and futureScrollRange. The problem is whenever i select any date from the first month in calendar, it's showing me the last week of next month. For example if January 2017 is the first month in calendar, after selecting any date it shows me the last week of February 2017. Similarly when i select any date from the last month in calendar, it shows me the first week of that same month. I have been trying to figure out the solution but nothing seems to help. Any guidance will be highly appreciated.

Kindly look at the below screenshots. In the below screenshots December 2017 is the first month in calendar and December 2021 is the last month in calendar.

Image1 Image2

Here is my code.

import React, { useState, useEffect } from "react";
import { View, Text, Image, TouchableOpacity, SafeAreaView, StyleSheet, Platform } from "react-native";
import { Agenda } from "react-native-calendars";
import Toast from "react-native-simple-toast";
import moment from "moment";
import "../../../Utils/Globals";
import { SERVICES_FOR_DATE_URL, CLIENT_ID_KEY } from "../../../Config/Routes";
import { requestData } from "../../../Api/ApiRequests";
import ServiceEvent from "./ServiceEvent";
//import { checkLocationPermission, requestLocationPermission, getDistance } from "../../../Location/LocationManager";
import Geolocation from "react-native-geolocation-service";
import Icon from "react-native-vector-icons/FontAwesome";

let watchId = "";

const ServicesCalendarScreen = (props) => {

    //let date = moment();
    //date = moment().format("MMMM YYYY");
    //date = moment().month() + moment().year();
    //Toast.show(""+date, Toast.LONG);

    //events state hook for selected day events
    const [events, setEvents] = useState(null);

    //set selected month and year
    const [date, setDate] = useState(moment().format("MMMM YYYY"));

    //hook for setting selected date to pass to onRefresh prop of agenda to refresh the selected date contents
    const [dateToRefresh, setDateToRefresh] = useState(moment().format("YYYY-MM-DD"));

    const getSelectedDayEvents = (date) => {

        let serviceDate = moment(date);
        serviceDate = serviceDate.format("DD/MM/YYYY");

        const data = { ClientId : CLIENT_ID, ServiceDate : serviceDate };

        //returned result with fetch api
        requestData(PROVIDER_URL + SERVICES_FOR_DATE_URL, data).then((result) => {
            console.log(result);
            if (result.status == "Successful") {
                if (result.data != "") {
                    //changing the received result object so that calendar can view the event on selected date
                    let modifiedData = { [date] : result.data };
                    setEvents(modifiedData);
                    console.log(modifiedData);
                    //to change the month and year on top of agenda
                    //setDate(moment(date).format("MMMM YYYY"));
                }
                else {
                    let modifiedData = { [date] : result.data };
                    setEvents(modifiedData);
                }
            }
            else {
                Toast.show(result.message ? result.message : result, Toast.SHORT);
            }

        })
        .catch((error) => {
            console.log(error);
        });
    }

    //this condition is implemented when we need to refresh data when we appear from another screen
    if (typeof props.navigation.state.params !== "undefined") {
        const refreshData = props.navigation.state.params.refreshData;
        const selectedDate = props.navigation.state.params.selectedDate;
        props.navigation.state.params = undefined;
        let date = selectedDate.replace(/\//g, "-");
        date = moment(selectedDate, "DD-MM-YYYY").format("YYYY-MM-DD");
        //date = date.format("YYYY-MM-DD");
        getSelectedDayEvents(date);
        // if (refreshData) {
        //     let currentDate = moment();
        //     currentDate = currentDate.format("YYYY-MM-DD");
        //     getSelectedDayEvents(currentDate);
        // }
    }

    //getting the service date to populate the selected day calendar events
    useEffect(() => {

        let currentDate = moment();
        currentDate = currentDate.format("YYYY-MM-DD");
        getSelectedDayEvents(currentDate);

        // if (Platform.OS === "android") {
        //     //checking the status if user has location permission enabled, if not then show the permission popup to user
        //     requestLocationPermission().then((status) => {
        //         console.log(status);
        //         if (status === "granted") {
        //             //getting location response through promise from another file
        //             // const watchID = await getPosition().then((position) => {
        //             //     console.log(position);
        //             // })
        //             // .catch((error) => {
        //             //     console.log(error);
        //             // });
        //             // console.log(watchID);

        //             //options for location
        //             const options = {
        //                 enableHighAccuracy: true, 
        //                 timeout : 15000, 
        //                 interval : 30000
        //             };

        //             //for iOS only
        //             if (Platform.OS === "ios")
        //                 setRNConfiguration(options);

        //             //continuously get location after every 30 seconds
        //             watchID = Geolocation.watchPosition((position) => {
        //                 const currentLat = position.coords.latitude;
        //                 const currentLng = position.coords.longitude;
        //                 const residentLat = -37.901639;
        //                 const residentLng = 145.054707;
        //                 Toast.show("Location Updated", Toast.LONG);
        //                 getDistance(currentLat, currentLng, residentLat, residentLng).then((data) => {
        //                     console.log(data);
        //                 })
        //                 .catch((error) => {
        //                     console.log(error);
        //                 });
        //             },
        //             (error) => {
        //                 console.log(error);
        //             },
        //             options);
        //         }
        //         else {
        //             console.log("No permission allowed for accessing location");
        //         }

        //     })
        //     .catch((error) => {
        //         console.log(error);
        //     });
        // }
        // else {

        // }
    }, []);

    // useEffect(() => {
    //     checkLocationPermission().then((permissionStatus) => {
    //         switch (permissionStatus) {
    //             case "granted":
    //                 break;
    //             case "denied":
    //                 break;
    //             case "blocked":
    //                 break;
    //             case "unavailable":
    //                 break;
    //         }
    //     })
    //     .catch((error) => {
    //         console.log(error);
    //     });
    // });

    return (
        <View style = {{ flex : 1 }}>
            <View style = { styles.dateViewStyle }>
                <TouchableOpacity 
                    style = { styles.dateButtonStyle }
                    //onPress = {() => openCalendar ? setOpenCalendar(false) : setOpenCalendar(true) }
                    >
                    <Text style = { styles.dateStyle }>{ date }</Text>
                </TouchableOpacity>
            </View>
            <Agenda items = { events } /*shouldOpenCalendar = { openCalendar }*/
                // Enable or disable scrolling of calendar list
                scrollEnabled = { false }
                // Max amount of months allowed to scroll to the past. Default = 50
                pastScrollRange = { 12 }
                // Max amount of months allowed to scroll to the future. Default = 50
                futureScrollRange = { 12 }
                // callback that gets called when items for a certain month should be loaded (month became visible)
                // loadItemsForMonth={(month) => {
                //         let currentDate = moment();
                //         currentDate = currentDate.format("YYYY-MM-DD");
                //         if (currentDate == month.dateString) {
                //             getSelectedDayEvents(month.dateString);
                //         }
                //         console.log(currentDate);
                //     }
                // }
                // callback that fires when the calendar is opened or closed
                onCalendarToggled = {(calendarOpened) => calendarOpened ? setDate("") : null }
                // callback that gets called on day press
                onDayPress={(day)=>{
                        getSelectedDayEvents(day.dateString);
                        //to change the month and year on top of agenda
                        setDate(moment(day.dateString).format("MMMM YYYY"));
                        //set the date in case onRefresh is executed
                        setDateToRefresh(day.dateString);
                    }
                }
                // callback that gets called when day changes while scrolling agenda list
                onDayChange={(day)=>{
                        console.log('day changed')
                    }
                }
                // specify how each item should be rendered in agenda
                renderItem={(item, firstItemInDay) => {
                        return (<ServiceEvent eventDetails = { item } navigation = { props.navigation } />);
                    }
                }
                // specify how each date should be rendered. day can be undefined if the item is not first in that day.
                // renderDay={(day, item) => {
                //         return (<View><Text>Text</Text></View>);
                //     }
                // }
                // specify how empty date content with no items should be rendered
                renderEmptyDate = {() => {
                    return (
                        <View style = { styles.viewStyle }>
                            <Icon name = "exclamation-triangle" size = { 30 } color = "#2079B3"/>
                            <Text style = { styles.textStyle }>No services on this date</Text>
                        </View>
                    );
                }}
                // specify how agenda knob should look like
                renderKnob={() => { 
                    return (
                        <TouchableOpacity /*onPress = {() => openCalendar ? setOpenCalendar(false) : setOpenCalendar(true)}*/>
                            <Icon name = "chevron-down" size = { 20 } color = "#2079B3" />
                        </TouchableOpacity>
                    ); 
                }}
                // specify what should be rendered instead of ActivityIndicator
                // renderEmptyData = {() => {
                //     return (<View><Text>No service on this date</Text></View>);
                // }}
                // specify your item comparison function for increased performance
                rowHasChanged={(r1, r2) => {return r1 !== r2;}}

                // By default, agenda dates are marked if they have at least one item, but you can override this if needed
                // markedDates={{
                //     '2019-11-22': {selected: true, marked: true},
                //     '2019-11-23': {marked: true},
                //     '2019-11-24': {disabled: true}
                // }}

                // If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make sure to also set the refreshing prop correctly.
                onRefresh={() => getSelectedDayEvents(dateToRefresh)}

                //styling agenda calendar view
                theme = {{ 
                    textDayFontSize: 16,
                    textMonthFontSize: 20,
                    textDayHeaderFontSize: 16
                }} 

            />
        </View>

    );
}

const styles = StyleSheet.create({
    dateViewStyle : {
        flexDirection : "row",
        justifyContent : "center",
        height : "auto"
    },
    dateStyle : {
        color : "#2079B3",
        fontSize : 18,
        padding : 10,
        margin : 5,
        borderRadius: 5
    },
    viewStyle : {
        flexDirection : "row",
        justifyContent : "center",
        padding : 5,
        marginTop : 30,
        height : 50
    },
    textStyle : {
        fontSize : 18,
        margin : 5
    }
});

export default ServicesCalendarScreen;

Upvotes: 4

Views: 12651

Answers (2)

Omid Tashakor
Omid Tashakor

Reputation: 1

I find this because the last calendar page is still in stack navigation and the hight not correctly calculated. so I remove all data from stack navigation(You can find the exact same calendar page and remove it). after that navigate to that page and everything loos fine.

import {NavigationActions, StackActions} from 'react-navigation';
const resetAction = StackActions.reset({
      index: 0,
      actions: [NavigationActions.navigate({ routeName: 'name of calendar component' })],
    });
    navigation.dispatch(resetAction)

no need any change in style.

Upvotes: 0

Ishita Singh
Ishita Singh

Reputation: 337

There two things that you can do:

  • Update the version "react-native-calendars": "1.220.0" The weird scrolling behaviour of the header when calendar is collapsed is fixed here.

  • You can set style for to the theme, like in my case I handled separately for iOS and Android:

'stylesheet.calendar.header': { week: { marginTop: Platform.OS=='ios'?6:2, flexDirection: 'row', justifyContent: 'space-between' } }

Eg:

<Agenda
theme={{
'stylesheet.calendar.header': { week: { marginTop: Platform.OS=='ios'?6:2, flexDirection: 'row', justifyContent: 'space-between' } }
}}>

For reference my Agenda component:

<Agenda
                    // 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.props.planList}
                    renderItem={(item, firstItemInDay) => this.renderItem(item, firstItemInDay)}
                    renderDay={(day, item) =>this.renderItemDay(day, item)}
                    renderEmptyDate={() =>this.renderEmptyDate()}
                    rowHasChanged={(r1, r2) =>this.rowHasChanged(r1,r2)}
                    onDayPress={this.onDaySelected.bind(this)}
                    minDate={this.props.minDate? this.props.minDate : Moment(today).format('YYYY-MM-DD')}
                    maxDate ={this.props.maxDate? this.props.maxDate : Moment(today).format('YYYY-MM-DD')}
                    renderKnob={() => {return (<View style={{height:14, padding:4}}><View style={{height:'100%', width:40, backgroundColor:'#DCDCDC', borderRadius:4,
                        borderWidth: 1,
                        borderColor: '#DCDCDC'}} /></View>);}}
                    markedDates={this.props.markedDates}
                    theme={{
                        backgroundColor: '#ffffff',
                        calendarBackground: '#f8f5f0',
                        selectedDayBackgroundColor: '#E0D2BC',
                        selectedDayTextColor: '#000000',
                        todayTextColor: '#000000',
                        textDisabledColor: '#888888',
                        dayTextColor: '#000000',
                        agendaKnobColor: '#DCDCDC',
                        dotColor: COLORS.GREEN,
                        selectedDotColor: COLORS.PRIMARY,
                        'stylesheet.calendar.header': { week: { marginTop: Platform.OS=='ios'?6:2, flexDirection: 'row', justifyContent: 'space-between' } }

                    }}

                />

Upvotes: 1

Related Questions