Reputation: 528
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.
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
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
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