1import React from 'react';
2import { Alert, Platform, ScrollView, StyleSheet, View } from 'react-native';
3import { NavigationScreenComponent, NavigationScreenProps } from 'react-navigation';
4import * as Permissions from 'expo-permissions';
5import * as Calendar from 'expo-calendar';
6import Button from '../components/Button';
7import Colors from '../constants/Colors';
8import HeadingText from '../components/HeadingText';
9import ListButton from '../components/ListButton';
10import MonoText from '../components/MonoText';
11
12const CalendarRow: NavigationScreenComponent<
13  {},
14  {},
15  {
16    calendar: any;
17    updateCalendar: (calendarId: string) => void;
18    deleteCalendar: (calendar: any) => void;
19  }
20> = props => {
21  const { calendar } = props;
22  const calendarTypeName =
23    calendar.entityType === Calendar.EntityTypes.REMINDER ? 'Reminders' : 'Events';
24  return (
25    <View style={styles.calendarRow}>
26      <HeadingText>{calendar.title}</HeadingText>
27      <MonoText>{JSON.stringify(calendar, null, 2)}</MonoText>
28      <ListButton
29        onPress={() => props.navigation.navigate(calendarTypeName, { calendar })}
30        title={`View ${calendarTypeName}`}
31      />
32      <ListButton
33        onPress={() => props.updateCalendar(calendar.id)}
34        title="Update Calendar"
35        disabled={!calendar.allowsModifications}
36      />
37      <ListButton
38        onPress={() => props.deleteCalendar(calendar)}
39        title="Delete Calendar"
40        disabled={!calendar.allowsModifications}
41      />
42    </View>
43  );
44};
45CalendarRow.navigationOptions = {
46  title: 'Calendars',
47};
48
49interface State {
50  haveCalendarPermissions: boolean;
51  haveReminderPermissions: boolean;
52  calendars: any[];
53  activeCalendarId?: string;
54  activeCalendarEvents: any[];
55  showAddNewEventForm: boolean;
56  editingEvent?: any;
57}
58
59export default class CalendarsScreen extends React.Component<NavigationScreenProps, State> {
60  static navigationOptions = {
61    title: 'Calendars',
62  };
63
64  readonly state: State = {
65    haveCalendarPermissions: false,
66    haveReminderPermissions: false,
67    calendars: [],
68    activeCalendarEvents: [],
69    showAddNewEventForm: false,
70  };
71
72  _askForCalendarPermissions = async () => {
73    const response = await Permissions.askAsync(Permissions.CALENDAR);
74    const granted = response.status === 'granted';
75    this.setState({
76      haveCalendarPermissions: granted,
77    });
78    return granted;
79  }
80
81  _askForReminderPermissions = async () => {
82    if (Platform.OS === 'android') return true;
83    const response = await Permissions.askAsync(Permissions.REMINDERS);
84    const granted = response.status === 'granted';
85    this.setState({
86      haveReminderPermissions: granted,
87    });
88    return granted;
89  }
90
91  _findCalendars = async () => {
92    const calendarGranted = await this._askForCalendarPermissions();
93    const reminderGranted = await this._askForReminderPermissions();
94    if (calendarGranted && reminderGranted) {
95      const eventCalendars = (await Calendar.getCalendarsAsync('event')) as unknown as any[];
96      const reminderCalendars =
97        (Platform.OS === 'ios' ? await Calendar.getCalendarsAsync('reminder') : []) as any[];
98      this.setState({ calendars: [...eventCalendars, ...reminderCalendars] });
99    }
100  }
101
102  _addCalendar = async () => {
103    const newCalendar = {
104      title: 'cool new calendar',
105      entityType: Calendar.EntityTypes.EVENT,
106      color: '#c0ff33',
107      sourceId:
108        Platform.OS === 'ios'
109          ? this.state.calendars.find(cal => cal.source && cal.source.name === 'Default').source.id
110          : undefined,
111      source:
112        Platform.OS === 'android'
113          ? {
114            name: this.state.calendars.find(
115                cal => cal.accessLevel === Calendar.CalendarAccessLevel.OWNER
116              ).source.name,
117            isLocalAccount: true,
118          }
119          : undefined,
120      name: 'coolNewCalendar',
121      accessLevel: Calendar.CalendarAccessLevel.OWNER,
122      ownerAccount:
123        Platform.OS === 'android'
124          ? this.state.calendars.find(cal => cal.accessLevel === Calendar.CalendarAccessLevel.OWNER)
125              .ownerAccount
126          : undefined,
127    };
128    try {
129      await Calendar.createCalendarAsync(newCalendar);
130      Alert.alert('Calendar saved successfully');
131      this._findCalendars();
132    } catch (e) {
133      Alert.alert('Calendar not saved successfully', e.message);
134    }
135  }
136
137  _updateCalendar = async (calendarId: string) => {
138    const newCalendar = {
139      title: 'cool updated calendar',
140    };
141    try {
142      await Calendar.updateCalendarAsync(calendarId, newCalendar);
143      Alert.alert('Calendar saved successfully');
144      this._findCalendars();
145    } catch (e) {
146      Alert.alert('Calendar not saved successfully', e.message);
147    }
148  }
149
150  _deleteCalendar = async (calendar: any) => {
151    Alert.alert(`Are you sure you want to delete ${calendar.title}?`, 'This cannot be undone.', [
152      {
153        text: 'Cancel',
154        onPress: () => {},
155      },
156      {
157        text: 'OK',
158        onPress: async () => {
159          try {
160            await Calendar.deleteCalendarAsync(calendar.id);
161            Alert.alert('Calendar deleted successfully');
162            this._findCalendars();
163          } catch (e) {
164            Alert.alert('Calendar not deleted successfully', e.message);
165          }
166        },
167      },
168    ]);
169  }
170
171  render() {
172    if (this.state.calendars.length) {
173      return (
174        <ScrollView style={styles.container}>
175          <Button onPress={this._addCalendar} title="Add New Calendar" />
176          {this.state.calendars.map(calendar => (
177            <CalendarRow
178              calendar={calendar}
179              key={calendar.id}
180              navigation={this.props.navigation}
181              updateCalendar={this._updateCalendar}
182              deleteCalendar={this._deleteCalendar}
183            />
184          ))}
185        </ScrollView>
186      );
187    }
188
189    return (
190      <View style={styles.container}>
191        <Button onPress={this._findCalendars} title="Find my Calendars" />
192      </View>
193    );
194  }
195}
196
197const styles = StyleSheet.create({
198  container: {
199    backgroundColor: Colors.greyBackground,
200    paddingHorizontal: 10,
201    paddingVertical: 16,
202    flex: 1,
203  },
204  calendarRow: {
205    marginBottom: 12,
206  },
207});
208