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