1import { StackScreenProps } from '@react-navigation/stack'; 2import * as Calendar from 'expo-calendar'; 3import React from 'react'; 4import { Alert, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'; 5 6import Button from '../components/Button'; 7import HeadingText from '../components/HeadingText'; 8import ListButton from '../components/ListButton'; 9import MonoText from '../components/MonoText'; 10 11const EventRow: React.FunctionComponent<{ 12 event: Calendar.Event; 13 getEvent: (event: Calendar.Event) => void; 14 getAttendees: (event: Calendar.Event) => void; 15 updateEvent: (event: Calendar.Event) => void; 16 deleteEvent: (event: Calendar.Event) => void; 17 openEventInCalendar: (event: Calendar.Event) => void; 18}> = ({ event, getEvent, getAttendees, updateEvent, deleteEvent, openEventInCalendar }) => ( 19 <View style={styles.eventRow}> 20 <HeadingText>{event.title}</HeadingText> 21 <MonoText>{JSON.stringify(event, null, 2)}</MonoText> 22 <ListButton onPress={() => getEvent(event)} title="Get Event Using ID" /> 23 <ListButton onPress={() => getAttendees(event)} title="Get Attendees for Event" /> 24 <ListButton onPress={() => updateEvent(event)} title="Update Event" /> 25 <ListButton onPress={() => deleteEvent(event)} title="Delete Event" /> 26 {Platform.OS === 'android' && ( 27 <ListButton onPress={() => openEventInCalendar(event)} title="Open in Calendar App" /> 28 )} 29 </View> 30); 31 32interface State { 33 events: Calendar.Event[]; 34} 35 36type Links = { 37 Events: { calendar: Calendar.Calendar }; 38}; 39 40type Props = StackScreenProps<Links, 'Events'>; 41 42export default class EventsScreen extends React.Component<Props, State> { 43 static navigationOptions = { 44 title: 'Events', 45 }; 46 47 readonly state: State = { 48 events: [], 49 }; 50 51 componentDidMount() { 52 const { params } = this.props.route; 53 if (params) { 54 const { id } = params.calendar; 55 if (id) { 56 this._findEvents(id); 57 } 58 } 59 } 60 61 _findEvents = async (id: string) => { 62 const yesterday = new Date(); 63 yesterday.setDate(yesterday.getDate() - 1); 64 const nextYear = new Date(); 65 nextYear.setFullYear(nextYear.getFullYear() + 1); 66 const events = await Calendar.getEventsAsync([id], yesterday, nextYear); 67 this.setState({ events }); 68 }; 69 70 _addEvent = async (recurring: boolean) => { 71 const { calendar } = this.props.route.params!; 72 if (!calendar.allowsModifications) { 73 Alert.alert('This calendar does not allow modifications'); 74 return; 75 } 76 const timeInOneHour = new Date(); 77 timeInOneHour.setHours(timeInOneHour.getHours() + 1); 78 const newEvent: { 79 title: string; 80 location: string; 81 startDate: Date; 82 endDate: Date; 83 notes: string; 84 timeZone: string; 85 recurrenceRule?: { 86 occurrence: number; 87 frequency: string; 88 }; 89 } = { 90 title: 'Celebrate Expo', 91 location: '420 Florence St', 92 startDate: new Date(), 93 endDate: timeInOneHour, 94 notes: "It's cool", 95 timeZone: 'America/Los_Angeles', 96 }; 97 if (recurring) { 98 newEvent.recurrenceRule = { 99 occurrence: 5, 100 frequency: 'daily', 101 }; 102 } 103 try { 104 await Calendar.createEventAsync(calendar.id, newEvent); 105 Alert.alert('Event saved successfully'); 106 this._findEvents(calendar.id); 107 } catch (e) { 108 Alert.alert('Event not saved successfully', e.message); 109 } 110 }; 111 112 _getEvent = async (event: Calendar.Event) => { 113 try { 114 const newEvent = await Calendar.getEventAsync(event.id!, { 115 futureEvents: false, 116 instanceStartDate: event.startDate, 117 }); 118 Alert.alert('Event found using getEventAsync', JSON.stringify(newEvent)); 119 } catch (e) { 120 Alert.alert('Error finding event', e.message); 121 } 122 }; 123 124 _getAttendees = async (event: Calendar.Event) => { 125 try { 126 const attendees = await Calendar.getAttendeesForEventAsync(event.id!, { 127 futureEvents: false, 128 instanceStartDate: event.startDate, 129 }); 130 Alert.alert('Attendees found using getAttendeesForEventAsync', JSON.stringify(attendees)); 131 } catch (e) { 132 Alert.alert('Error finding attendees', e.message); 133 } 134 }; 135 136 _updateEvent = async (event: Calendar.Event) => { 137 const { calendar } = this.props.route.params!; 138 if (!calendar.allowsModifications) { 139 Alert.alert('This calendar does not allow modifications'); 140 return; 141 } 142 const newEvent = { 143 title: 'update test', 144 }; 145 try { 146 await Calendar.updateEventAsync(event.id!, newEvent, { 147 futureEvents: false, 148 instanceStartDate: event.startDate, 149 }); 150 Alert.alert('Event saved successfully'); 151 this._findEvents(calendar.id); 152 } catch (e) { 153 Alert.alert('Event not saved successfully', e.message); 154 } 155 }; 156 157 _deleteEvent = async (event: Calendar.Event) => { 158 try { 159 const { calendar } = this.props.route.params!; 160 await Calendar.deleteEventAsync(event.id!, { 161 futureEvents: false, 162 instanceStartDate: event.recurrenceRule ? event.startDate : undefined, 163 }); 164 Alert.alert('Event deleted successfully'); 165 this._findEvents(calendar.id); 166 } catch (e) { 167 Alert.alert('Event not deleted successfully', e.message); 168 } 169 }; 170 171 _openEventInCalendar = (event: Calendar.Event) => { 172 Calendar.openEventInCalendar(event.id!); 173 }; 174 175 _renderActionButtons = () => { 176 return ( 177 <View> 178 <Button 179 onPress={() => this._addEvent(false)} 180 style={{ marginBottom: 10 }} 181 title="Add New Event" 182 /> 183 <Button onPress={() => this._addEvent(true)} title="Add New Recurring Event" /> 184 </View> 185 ); 186 }; 187 188 render() { 189 const events = this.state.events.length ? ( 190 <View> 191 {this.state.events.map((event) => ( 192 <EventRow 193 event={event} 194 key={`${event.id}${event.startDate}`} 195 getEvent={this._getEvent} 196 getAttendees={this._getAttendees} 197 updateEvent={this._updateEvent} 198 deleteEvent={this._deleteEvent} 199 openEventInCalendar={this._openEventInCalendar} 200 /> 201 ))} 202 </View> 203 ) : ( 204 <Text style={{ marginVertical: 12 }}>This calendar has no events.</Text> 205 ); 206 return ( 207 <ScrollView style={styles.container}> 208 {this._renderActionButtons()} 209 {events} 210 </ScrollView> 211 ); 212 } 213} 214 215const styles = StyleSheet.create({ 216 container: { 217 paddingHorizontal: 10, 218 paddingVertical: 16, 219 flex: 1, 220 }, 221 eventRow: { 222 marginBottom: 12, 223 }, 224}); 225