Adding appointment to calendar using Expo createEventAsync IOS - react-native

SDK Version: 38
Platforms(Android/iOS):
I am having difficulties getting this code to return a calendar ID, I’d appreciate help from anyone as there seems to be very little information about the new calendar API change.
async obtainCalendarPermission() {
let permission = await Permissions.getAsync(Permissions.CALENDAR)
if (permission.status !== 'granted') {
permission = await Permissions.askAsync(Permissions.CALENDAR)
return
}
if (permission.status !== 'granted') {
permission = await Permissions.askAsync(Permissions.REMINDERS)
return
if (permission.status !== 'granted') {
Alert.alert('Permission not granted to calendar')
}
}
return permission
}
async function getDefaultCalendarSource() {
const calendars = await Calendar.getCalendarsAsync()
const defaultCalendars = calendars.filter(
(each) => each.source.name === 'Default',
)
return defaultCalendars[0].source
}
async addReservationToCalendar(date){
await this.obtainCalendarPermission()
const permission = await Permissions.askAsync(Permissions.REMINDERS)
if (permission.status !== 'granted')
var calendars = Calendar.getCalendarsAsync().then(calendars => console.log(calendars))
const defaultCalendarSource = Platform.OS === 'ios' ? await getDefaultCalendarSource(): { isLocalAccount: true, name: 'Expo Calendar' };
console.log(defaultCalendarSource ,+'emeka')
let dateMs = Date.parse(date)
let startDate = new Date(dateMs)
let endDate = new Date(dateMs + 2 * 60 * 60 * 1000)
const calendarId = await Calendar.createEventAsync(
defaultCalendarSource.id,
{
title: 'Con Fusion Table Reservation',
startDate: startDate,
endDate: endDate,
timeZone: 'Asia/Hong_Kong',
location:
'121, Clear Water Bay Road, Clear Water Bay, Kowloon, Hong Kong',
},
)}

In my case I decided to implement EVENTS for android and REMINDERS for iOS. I show you my solution, but it's a bit different. It works perfectly on both platforms.
I'm using:
"expo": "46.0.13"
"expo-calendar": "10.3.0"
"react-native": "0.69.6"
useEffect(() => {
(async () => {
if (Platform.OS === 'ios') {
await requestTrackingPermissionsAsync();
const remindersPermissions = await Calendar.requestRemindersPermissionsAsync();
if(remindersPermissions.status !== 'granted')
Alert.alert('Reminders access required', 'You must accept access to reminders in order to create reminders.',
[
{ text: "Cancel", style: "cancel" },
{ text: "OK", onPress: async () => await Calendar.requestRemindersPermissionsAsync() }
])
}
const calendarPermissions = await Calendar.requestCalendarPermissionsAsync();
if(calendarPermissions.status === 'granted') {
createCalendar()
} else {
Alert.alert('Access to calendars required', 'You must allow access to the calendar if you want to create reminders.',
[
{ text: "Cancel", style: "cancel" },
{ text: "OK", onPress: async () => await Calendar.requestCalendarPermissionsAsync() }
])
}
})();
}, [])
const getDefaultCalendarSource = async() => {
const defaultCalendar = await Calendar.getDefaultCalendarAsync();
return defaultCalendar.source;
}
const createCalendar = async () => {
const calendarEntityType = Platform.OS === 'android' ? Calendar.EntityTypes.EVENT : Calendar.EntityTypes.REMINDER;
const calendars = Platform.OS === 'android' ? await Calendar.getCalendarsAsync() : await Calendar.getCalendarsAsync(calendarEntityType);
const existCalendar = calendars.find(calendar => calendar.title === DEFAULT_CALENDAR_NAME);
if (!existCalendar) {
const defaultCalendarSource = Platform.OS === 'ios' ? await getDefaultCalendarSource() : { isLocalAccount: true, name: DEFAULT_CALENDAR_NAME };
const newCalendarId = await Calendar.createCalendarAsync({
title: DEFAULT_CALENDAR_NAME,
name: DEFAULT_CALENDAR_NAME,
color: primerColor,
entityType: calendarEntityType,
accessLevel: Calendar.CalendarAccessLevel.OWNER,
sourceId: defaultCalendarSource.id,
source: defaultCalendarSource,
ownerAccount: DEFAULT_CALENDAR_NAME,
});
globalContext.setCalendarId(newCalendarId)
} else {
const calendarIndex = calendars.findIndex(calendar => calendar.title === DEFAULT_CALENDAR_NAME);
globalContext.setCalendarId(calendars[calendarIndex].id);
}
}
And to create Events/Reminders I have this function:
const onSetReminder = async (reminderDate) => {
let reminderId;
let eventId;
Platform.OS === 'ios' && (async() => {
reminderId = await Calendar.createReminderAsync(globalContext.calendarId, {
title: `Reminder Title`,
calendarId: globalContext.calendarId,
startDate: reminderDate,
dueDate: reminderDate,
alarms: [{
relativeOffset: -60,
method: Calendar.AlarmMethod.ALERT
}],
});
})();
Platform.OS === 'android' && (async() => {
eventId = await Calendar.createEventAsync(globalContext.calendarId, {
title: `Event Title`,
accessLevel: Calendar.EventAccessLevel.OWNER,
calendarId: globalContext.calendarId,
startDate: reminderDate,
endDate: reminderDate,
alarms: [{
method: Calendar.AlarmMethod.ALERT,
relativeOffset: -60,
}],
});
})();
};

I have solved it. I was having a hard time getting the calendar ID as it is a requirement for the new createEventAsync() API. this is the complete code that solved the problem for me, I am pasting it here so that someone can use or improve on it.
It generates a unique calendar ID and saves reservations to the calendar on IOS, I did not test it on android.
async function getDefaultCalendarSource() {
await Calendar.getCalendarsAsync().then((id) => console.log(id))
}
async obtainCalendarPermission() {
let permission = await Permissions.getAsync(Permissions.CALENDAR)
if (permission.status !== 'granted') {
permission = await Permissions.askAsync(Permissions.CALENDAR)
return
}
if (permission.status !== 'granted') {
permission = await Permissions.askAsync(Permissions.REMINDERS)
return
if (permission.status !== 'granted') {
Alert.alert('Permission not granted to calendar')
}
}
return permission
}
async addReservationToCalendar(date) {
await this.obtainCalendarPermission()
var dateMs = Date.parse(date)
var startDate = new Date(dateMs)
var endDate = new Date(dateMs + 2 * 60 * 60 * 1000)
getDefaultCalendarSource()
const newCalendar = await Calendar.createCalendarAsync({
title: 'Test Reservation',
color: '#512DA8',
entityType: Calendar.EntityTypes.EVENT,
sourceId: getDefaultCalendarSource.id,
source: getDefaultCalendarSource,
name: 'Restauran Reservation',
ownerAccount: 'personal',
accessLevel: Calendar.CalendarAccessLevel.OWNER,
})
.then((id) => {
Calendar.createEventAsync(id, {
title: 'Table Reservation',
startDate: startDate,
endDate: endDate,
timeZone: 'Asia/Hong_Kong',
location:
'121, Clear Water Bay Road, Clear Water Bay, Kowloon, Hong Kong',
}).catch((err) => console.log(err))
// console.log(`calendar ID is: ${id}`)
})
.catch((err) => console.log(err))
}

Related

useState is keep resetting- React Native

When I opened the app the first time and allow permission and switched to another screen the permission variable is still true and not resetting But when I closed the app and reopen the permission variable is first false and then became true.
` const DeviceLocation = (props) => {
const [hasPermission, setHasPermission] = useState(false);
const requestPermission = async () => {
const permissions =
Platform.OS === 'ios'
? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION;
const isPermissionGranted = await checkPermission(permissions);
if (isPermissionGranted === 'granted') {
setHasPermission(true);
} else if (isPermissionGranted === 'denied') {
const result = await askPermission(permissions);
if (result === 'granted') {
setHasPermission(true);
}
} else if (isPermissionGranted === 'blocked') {
setPermisionBlocked(true);
setHasPermission(false);
} else {
setHasPermission(false);
setPositionLoading(false);
}
};
useEffect(() => {
{
isScreenFocused && requestPermission();
}
}, [isScreenFocused]);
const getPosition = useCallback(() => {
setPositionLoading(true);
if (hasPermission) {
Geolocation.getCurrentPosition(
(position) => {
setCurrentPosition({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
});
setPositionLoading(false);
},
(error) => {
console.log(error.code, error.message);
},
{
accuracy: {android: 'high', ios: 'bestForNavigation'},
distanceFilter: 1,
maximumAge: 10000,
},
);
} else {
setPositionLoading(false);
}
}, [setCurrentPosition, hasPermission]);your text
useEffect(() => {
getPosition();
}, [hasPermission]);`
close the app and reopen the app the permission variable should be true if the user already accepts the permission.
I have tried to set the hasPermission value into AsyncStorage but I still get hasPermission as false first time when the app reopens.

Multiple useEffect in react-native to achieve mentioned functionality

I need help with the async nature of Async storage and axios api. Here's the functionality that I am trying to achieve ->
send request to two separate api to get some data.
display that data on the screen with some additional text
api request are authenticated so a token is passed as Authentication Header
I have attached the current implementation, I am having the a number of errors in this
Errors:
Login_token not set in state after fetching from Async Storage.
Data not set in state after api call
both resulting in either failed api calls or undefined state errors on render
This is my code.
import React, { FunctionComponent, useEffect, useCallback, useState} from 'react';
import { StyleSheet, View} from 'react-native';
// chat
import { GiftedChat } from 'react-native-gifted-chat';
// navigation
import { RootStackParamList } from '../../navigators/RootStack';
import { StackScreenProps } from '#react-navigation/stack';
export type Props = StackScreenProps<RootStackParamList, "Chat">;
// api
import { Convo_details, Send_Msg, List_Msg, Expert_Public_Profile } from '../../api/UserApi';
import Spinner from 'react-native-loading-spinner-overlay';
import AsyncStorage from '#react-native-async-storage/async-storage';
import uuid from 'react-native-uuid';
const Chat: FunctionComponent<Props> = ({ navigation, route, ...props }) => {
// console.log(props.route.params);
const [login_token, setlogin_token] = useState('')
const [conversation_id, setconversation_id] = useState('')
const [conversation_details, setconversation_details] = useState({})
const [currentuser, setcurrentuser] = useState({})
const [loading, setLoading] = useState(false);
const [expertuid, setexpertuid] = useState('')
const [ExpertProfile, setExpertProfile] = useState({})
const [messages, setMessages] = useState([]);
useEffect(() => {
getlogintoken()
console.log("####################################","getlogintoken");
}, [])
/* conversationid */
useEffect(() => {
if (route.params != null) {
setconversation_id(route.params[0])
}
console.log("####################################","conversation id");
}, [])
/* expert uid */
useEffect(() => {
if (route.params != null) {
setexpertuid(route.params[1])
}
console.log("####################################","expert uid");
}, [])
/* expert public profile */
useEffect(() => {
getexpertpublicprofile()
getConvo_details()
console.log("####################################","convo_details");
}, [])
useEffect(() => {
// get current user
AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
console.log("####################################","current user");
}, [])
// set welcome msg
useEffect(() => {
if (Object.keys(conversation_details).length != 0 && Object.keys(ExpertProfile).length != 0)
setwelcomemsg()
}, [])
const onSend = useCallback(async (messages = []) => {
// console.log(messages[0].text);
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
const data = {
conversation_id: "f98d6851-a713-4f58-9118-77a779ff175f",//conversation_id,
message_type: "TEXT",
body: messages[0].text
}
const res: any = await Send_Msg(data, login_token)
.catch(error => {
alert(`Send_Msg -> ${error}`)
console.log(error);
return
})
if (res.status == 200) {
console.log(res.data);
} else console.log(res);
}, [])
const getexpertpublicprofile = async () => {
setLoading(true)
const res: any = await Expert_Public_Profile(expertuid, login_token)
.catch(error => {
setLoading(false)
console.log("Expert public profile ->");
alert(`Expert public profile ->${error.message}`)
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setExpertProfile(res.data)
else {
alert(`get expert public profile${res.data.message}`)
console.log("getexpertpublicprofile -->");
console.log(res.data);
}
}
const getlogintoken = () => {
AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getConvo_details = async () => {
setLoading(true)
const res: any = await Convo_details(conversation_id, login_token)
.catch(error => {
setLoading(false)
alert(`Convo_details-->${error.message}`)
console.log("Convo_details -->");
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setconversation_details(res.data)
else {
alert(`get convo details-> ${res.data.message}`)
console.log("getConvo_details -->");
console.log(res.data);
}
}
const setwelcomemsg = () => {
try {
let user = JSON.parse(currentuser)
let messages = [
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `About Me - ${ExpertProfile.bio}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `My name is ${conversation_details.recipient.name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `Hi ${user.full_name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
}]
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
} catch (error) {
console.log("try -> set welcome msg");
console.log(error);
return
}
}
return (
<View style={styles.maincontainer}>
<Spinner
visible={loading}
textContent={'Loading...'}
textStyle={{ color: '#FFF' }}
/>
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user={{
_id: currentuser.user_uid,
}}
isTyping={false}
scrollToBottom={true}
showAvatarForEveryMessage={true}
renderAvatar={() => null}
/>
</View>
);
}
export default Chat;
const styles = StyleSheet.create({
maincontainer: {
flex: 1,
},
});
When axios returns, it usually give the response as res.data, so in your case, try either res.data or res.data.yourToken (I'm not sure how it's your object).
Gurav,
As far as your code above, The api call's will trigger even before you get currentuser or loginToken. You have to handle the api call after getting the currentuser and loginToken. This can be gracefully handled with async, await.
example code:
useEffect(() => {
getData()
}, [])
useEffect(() => {
if(login_token && currentuser) {
//The api call goes here after you get the logintoken andcurrentuser.
// The above condition is just an example but will vary based on your requirements
}
}, [login_token, currentuser])
const getData = async () => {
await getlogintoken()
await getcurrentuser()
}
const getlogintoken = async () => {
await AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getcurrentuser = async () => {
await AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
}

Currently, it continues to run after 1min. I want it stop if the condition is met

Below, I want to clearInterval if processingStatus[0] === "DELIVERED".
useEffect(() => {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
// Fetching data changes after 1 Minute
const interval = setInterval(() => {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
// nextStep();
}, 60000);
if (processingStatus[0] === "DELIVERED") {
return () => clearInterval(interval);
}
}, [dispatch]);
You can use useRef(), like this
const YourComponent = () => {
const processingStatus = useSelector(...);
const processingStatusRef = useRef(null);
processingStatusRef.current = processingStatus[0];
useEffect(() => {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
// Fetching data changes after 1 Minute
const interval = setInterval(() => {
if (processingStatusRef.current === "DELIVERED") {
clearInterval(interval)
} else {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
}
}, 60000);
return () => clearInterval(interval);
}, [dispatch]);
}

Add an Event to Expo Calendar?

I'm trying to add an event using Expo Calendar but i'm getting an error. When i call my function openActionSheet the catch block is executed and getting the Alert with the message Error getting device calendars.. When i'm consoling the error i'm getting undefined is not a function (near '...showActionSheetWithOptions...').
My function's code is :
async openActionSheet() {
const { status } = await Permissions.askAsync(Permissions.CALENDAR, Permissions.REMINDERS);
if (status === 'granted') {
Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT)
.then(calendars => calendars.filter(cal => cal.allowsModifications))
.then((calendars) => {
const { showActionSheetWithOptions, navigation } = this.props;
const options = calendars.map(cal => (Platform.OS === 'ios' ? cal.title : cal.source.name)).concat(['Cancel']);
const cancelButtonIndex = calendars.length;
showActionSheetWithOptions({
title: 'Select a calendar:',
options,
cancelButtonIndex,
},
(buttonIndex) => {
const orderId = navigation.getParam('orderId');
const { orderSet } = navigation.getParam('orderSet');
const eventConfig = {
title: `OrderId: ${orderId}` || 'UNKNOWN_NAME',
startDate: new Date(navigation.getParam('orderDate')),
endDate: new Date(navigation.getParam('orderDate')),
location: `${orderSet.location}` || 'UNKNOWN_LOCATION',
notes: '',
};
if (buttonIndex !== cancelButtonIndex) {
Calendar.createEventAsync(calendars[buttonIndex].id, eventConfig)
.then(() => Alert.alert('Success', 'Event has been added to your calendar.'))
.catch(() => {
Alert.alert('Error', 'Event was not saved.');
});
}
});
})
.catch((e) => {
console.log('ERROR');
console.log(e);
Alert.alert('Error', 'Error getting device calendars.');
});
} else {
Alert.alert('Error', 'You have to authorize the application to access your calendar.');
}
}

Pusher chatKit onMessage hook fails in Expo app

I am using React Native with Expo, and I am able to create users + rooms and send messages to them with the following code:
const hooks = {
onMessage: message => {
console.log("message", message);
this.setState({
messages: [...this.state.messages, message]
});
},
onUserStartedTyping: user => {
this.setState({
usersWhoAreTyping: [...this.state.usersWhoAreTyping, user.name]
});
},
onUserStoppedTyping: user => {
this.setState({
usersWhoAreTyping: this.state.usersWhoAreTyping.filter(
username => username !== user.name
)
});
},
onPresenceChange: () => this.forceUpdate()
};
class SetupChatKit extends React.Component {
constructor(props) {
super(props);
this.state = {
chatManager: null,
currentUser: {},
currentRoom: {},
messages: [],
usersWhoAreTyping: []
};
}
componentDidMount() {
const { userId, name } = this.props;
this.instantiateChatManager(userId);
this.createChatKitUser({ userId, name });
}
joinOrCreateChatKitRoom = (mode, chatKitRoomId, title) => {
const { chatManager } = this.state;
return chatManager
.connect()
.then(currentUser => {
this.setState({ currentUser });
if (mode === "join") {
return currentUser.joinRoom({ roomId: chatKitRoomId, hooks });
}
return currentUser.createRoom({
name: title,
private: false,
hooks
});
})
.then(currentRoom => {
this.setState({ currentRoom });
return currentRoom.id;
})
.catch(error => console.error("error", error));
};
instantiateChatManager = userId => {
const chatManager = new Chatkit.ChatManager({
instanceLocator: "v1:us1:9c8d8a28-7103-40cf-bbe4-727eb1a2b598",
userId,
tokenProvider: new Chatkit.TokenProvider({
url: `http://${baseUrl}:3000/api/authenticate`
})
});
this.setState({ chatManager });
};
My problem is that console.log("message", message); never gets called, even when I manually add messages to the room via the online control panel.
I've tried logging from chatManager, and that looks like the following:
As you can see from the documentation, the onMessage hook needs to be attached on subscribeRoom, not when joining a room.
https://docs.pusher.com/chatkit/reference/javascript#connection-hooks
So probably add subscribeToRoom() after the first success promise in your joinOrCreateChatKitRoom() method.
I refactored the code with async/await and used .subscribetoRoom() like so:
joinOrCreateChatKitRoom = async (mode, chatKitRoomId, title) => {
const { chatManager } = this.state;
try {
const currentUser = await chatManager.connect();
this.setState({ currentUser });
let currentRoom;
if (mode === "join") {
currentRoom = await currentUser.joinRoom({
roomId: chatKitRoomId
});
} else {
currentRoom = await currentUser.createRoom({
name: title,
private: false
});
}
this.setState({ currentRoom });
await currentUser.subscribeToRoom({
roomId: currentRoom.id,
hooks: {
onMessage: message => {
this.setState({
messages: [...this.state.messages, message]
});
},
onUserStartedTyping: user => {
this.setState({
usersWhoAreTyping: [...this.state.usersWhoAreTyping, user.name]
});
},
onUserStoppedTyping: user => {
this.setState({
usersWhoAreTyping: this.state.usersWhoAreTyping.filter(
username => username !== user.name
)
});
},
onPresenceChange: () => this.forceUpdate()
}
});
return currentRoom.id;
} catch (error) {
console.error("error creating chatManager", error);
}
};