Add an Event to Expo Calendar? - react-native

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.');
}
}

Related

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")
})
}

Why is InterstitialAd not loaded after the first trigger?

I manage to get the first ad to show, but app crashed the next time I try to trigger an ad. And gives me this error: Error: InterstitialAd.show() The requested InterstitialAd has not loaded and could not be shown
In App.js
componentDidMount() {
const eventListener = interstitial.onAdEvent(type => {
if (type === AdEventType.LOADED) {
this.setState({
setLoaded: true,
});
}
});
interstitial.load();
eventListener();
}
showAds = () => {
interstitial.show();
// No advert ready to show yet
if (!this.state.loaded) {
console.log('null');
return null;
}
};
// This trigger is within another function
this.showAds();
I have a class component so I use ComponentDidMount instead of useEffect. Might that cause some troubles?
UPDATE:
this.state = {
loaded: false,
setLoaded: false,
Listener: null,
};
The above state is an attempt to redo
const [loaded, setLoaded] = useState(false);
constructor () {
super();
this.Listener=null
}
componentDidMount() {
this.Listener = interstitial.onAdEvent(type => {
if (type === AdEventType.LOADED) {
this.setState({
loaded: true,
});
}else if(type === AdEventType.CLOSED){
this.loadAd()
}
});
this.loadAd()
}
componentWillUnmount(){
if(this.Listener!==null){
this.Listener()
}
}
loadAd = () =>{
this.setState({
loaded: false,
});
interstitial.load();
}
showAds = () => {
if (!this.state.loaded) {
console.log('null');
return null;
}else{
interstitial.show();
}
};

Expo Location, Expo Task Manager with Context API react native

import React, { createContext, useContext, useState, useEffect } from 'react';
import {
authenticationService,
getUserProfileService,
} from '../services/user-service';
import { getDonationHistory } from '../services/donation-service';
import AsyncStorage from '#react-native-async-storage/async-storage';
import * as TaskManager from 'expo-task-manager';
import * as Location from 'expo-location';
import * as BackgroundFetch from 'expo-background-fetch';
const UserInfoContext = createContext();
const UserInfoProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [userProfile, setUserProfile] = useState({
donorId: '',
icNo: '',
fName: '',
lName: '',
bloodType: '',
});
const [errorMessage, setErrorMessage] = useState(null);
const [location, setLocation] = useState({ latitude: '', longitude: '' });
let updateLocation = (loc) => {
setLocation(loc);
};
console.log(location);
const sendBackgroundLocation = async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status === 'granted') {
const { status } = await Location.requestBackgroundPermissionsAsync();
if (status === 'granted') {
await Location.startLocationUpdatesAsync('LocationUpdate', {
accuracy: Location.Accuracy.Balanced,
timeInterval: 10000,
distanceInterval: 1,
foregroundService: {
notificationTitle: 'Live Tracker',
notificationBody: 'Live Tracker is on.',
},
});
}
}
};
const _requestLocationPermission = async () => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status == 'granted') {
let { status } = await Location.requestBackgroundPermissionsAsync();
if (status == 'granted') {
} else {
console.log('Permission to access location was denied');
}
} else {
console.log('Permission to access location was denied');
}
})();
};
sendBackgroundLocation();
const getUserProfile = () => {
return userProfile;
};
useEffect(() => {
(async () => await _requestLocationPermission())();
retrieveAuthTokens();
retrieveUserProfile();
});
let retrieveAuthTokens = async () => {
try {
const authTokens = await AsyncStorage.getItem('authTokens');
authTokens ? setIsLoggedIn(true) : setIsLoggedIn(false);
} catch (error) {
console.log(error.message);
}
};
let retrieveUserProfile = async () => {
try {
const userProfile = JSON.parse(await AsyncStorage.getItem('userProfile'));
userProfile
? setUserProfile({
donorId: userProfile.donorId,
icNo: userProfile.appUser.username,
fName: userProfile.fName,
lName: userProfile.lName,
bloodType: userProfile.bloodType,
})
: null;
} catch (error) {
console.log(error.message);
}
};
let loginUser = (values) => {
authenticationService(values)
.then(async (data) => {
if (data !== undefined && data !== null) {
const tokens = data.data;
await AsyncStorage.setItem('authTokens', JSON.stringify(tokens));
getProfile(values.icNo);
getHistories(userProfile.donorId);
setErrorMessage(null);
} else {
setErrorMessage('Wrong email/password!');
}
})
.catch((error) => console.log(error.message));
};
let logoutUser = async () => {
try {
await AsyncStorage.clear().then(console.log('clear'));
setIsLoggedIn(false);
} catch (error) {
console.log(error.message);
}
};
getProfile = (icNo) => {
getUserProfileService(icNo)
.then(async (res) => {
if (res !== undefined && res !== null) {
const profile = res.data;
await AsyncStorage.setItem('userProfile', JSON.stringify(profile));
setIsLoggedIn(true);
}
})
.catch((error) => console.log(error.message));
};
const getHistories = (userId) => {
getDonationHistory(userId)
.then(async (res) => {
if (res !== undefined && res !== null) {
const historyData = res.data;
await AsyncStorage.setItem(
'donationHistories',
JSON.stringify(historyData)
);
} else {
console.log('no data');
}
})
.catch((error) => console.log(error.message));
};
let contextData = {
loginUser: loginUser,
logoutUser: logoutUser,
isLoggedIn: isLoggedIn,
errorMessage: errorMessage,
userProfile: userProfile,
getHistories: getHistories,
};
return (
<UserInfoContext.Provider value={contextData}>
{children}
</UserInfoContext.Provider>
);
};
export const useUserInfo = () => useContext(UserInfoContext);
export default UserInfoProvider;
function myTask() {
try {
const backendData = 'Simulated fetch ' + Math.random();
return backendData
? BackgroundFetch.BackgroundFetchResult.NewData
: BackgroundFetch.BackgroundFetchResult.NoData;
} catch (err) {
return BackgroundFetch.BackgroundFetchResult.Failed;
}
}
async function initBackgroundFetch(taskName, interval = 60 * 15) {
try {
if (!TaskManager.isTaskDefined(taskName)) {
TaskManager.defineTask(taskName, ({ data, error }) => {
if (error) {
console.log('Error bg', error);
return;
}
if (data) {
const { locations } = data;
console.log(
locations[0].coords.latitude,
locations[0].coords.longitude
);
//-----------------doesnt work ----------------------------
UserInfoProvider.updateLocation({
latitude: locations[0].coords.latitude,
longitude: locations[0].coords.longitude,
});
//-----------------doesnt work ----------------------------
}
});
}
const options = {
minimumInterval: interval, // in seconds
};
await BackgroundFetch.registerTaskAsync(taskName, options);
} catch (err) {
console.log('registerTaskAsync() failed:', err);
}
}
initBackgroundFetch('LocationUpdate', 5);
I'm trying to update the location state in the UserInfoProvider and this location info will be sent to the firestore along with the userprofile details retrieved in this provider.
However, it seems that i cant access the
UserInfoProvider.updateLocation()
outside the UserInfoProvider component.
Is there anyway I can get both the userprofile info and the location info retrieved from background task together and send them to firestore?
At the moment, only the
console.log(
locations[0].coords.latitude,
locations[0].coords.longitude
);
in the background task seems to be working.
Error I got:
TaskManager: Task "LocationUpdate" failed:, [TypeError: undefined is
not a function (near '...UserInfoProvider.updateLocation...')] at
node_modules\react-native\Libraries\LogBox\LogBox.js:149:8 in
registerError at
node_modules\react-native\Libraries\LogBox\LogBox.js:60:8 in errorImpl
at node_modules\react-native\Libraries\LogBox\LogBox.js:34:4 in
console.error at
node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in
error at node_modules\expo-task-manager\build\TaskManager.js:143:16 in
eventEmitter.addListener$argument_1 at
node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch at
node_modules\regenerator-runtime\runtime.js:294:29 in invoke at
node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch at
node_modules\regenerator-runtime\runtime.js:155:27 in invoke at
node_modules\regenerator-runtime\runtime.js:190:16 in
PromiseImpl$argument_0 at
node_modules\react-native\node_modules\promise\setimmediate\core.js:45:6
in tryCallTwo at
node_modules\react-native\node_modules\promise\setimmediate\core.js:200:22 in doResolve at
node_modules\react-native\node_modules\promise\setimmediate\core.js:66:11
in Promise at node_modules\regenerator-runtime\runtime.js:189:15 in
callInvokeWithMethodAndArg at
node_modules\regenerator-runtime\runtime.js:212:38 in enqueue at
node_modules\regenerator-runtime\runtime.js:239:8 in exports.async at
node_modules\expo-task-manager\build\TaskManager.js:133:57 in
eventEmitter.addListener$argument_1 at
node_modules\react-native\Libraries\vendor\emitter_EventEmitter.js:135:10 in EventEmitter#emit at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:414:4
in __callFunction at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:113:6
in __guard$argument_0 at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:365:10
in __guard at
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:4
in callFunctionReturnFlushedQueue
By reviewing UserInfoProvider context value:
let contextData = {
loginUser: loginUser,
logoutUser: logoutUser,
isLoggedIn: isLoggedIn,
errorMessage: errorMessage,
userProfile: userProfile,
getHistories: getHistories,
};
updateLocation function not available and you can't access.
You need to use useUserInfo to consume UserInfoProvider value.

Adding appointment to calendar using Expo createEventAsync IOS

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))
}

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);
}
};