├── .gitignore ├── package.json ├── LICENSE ├── README.md └── index.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@atiladev/usecalendar", 3 | "version": "0.3.4", 4 | "description": "An easy hook to use with expo-calendar library!", 5 | "main": "index.ts", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/AtilaDev-team/useCalendar" 9 | }, 10 | "keywords": [ 11 | "expo", 12 | "calendar", 13 | "react", 14 | "native", 15 | "hook", 16 | "android", 17 | "ios", 18 | "events", 19 | "alarm", 20 | "settings" 21 | ], 22 | "homepage": "https://github.com/AtilaDev-team", 23 | "author": "Leandro Favre (AtilaDev)", 24 | "devDependencies": { 25 | "expo": "*", 26 | "expo-calendar": "*", 27 | "expo-localization": "*", 28 | "@react-native-async-storage/async-storage": "*" 29 | }, 30 | "license": "MIT" 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Leandro Favre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # useCalendar Hook 🚀 useCalendar version 2 | 3 | [![made with expo](https://img.shields.io/badge/MADE%20WITH%20EXPO-000.svg?style=for-the-badge&logo=expo&labelColor=4630eb&logoWidth=20)](https://github.com/expo/expo) 4 | 5 | > Updated to Expo SDK53 6 | 7 | ## Blog post & example 8 | 9 | To learn more about using this hook and see it in action, [**read the post**](https://medium.com/@FavreLeandro/how-to-use-usecalendar-hook-for-expo-and-react-native-a1d2bcc3aa1c)! 10 | 11 | To see a working example, check out [my-expo-agenda](https://github.com/AtilaDev-team/my-expo-agenda) which is a small example app developed to showcase how to use `useCalendar` hook. 12 | 13 | ## What is it for 14 | 15 | This is an easy hook to use expo-calendar. It allows you: 16 | 17 | - Get access permission to calendar 18 | - Open settings to give access permission to calendar 19 | - Create a new calendar and store it on your device 20 | - Add events to the calendar 21 | - Get all events 22 | - Get calendarId 23 | - Check if an event exists inside of the calendar created 24 | - Delete the calendar and all events inside of it 25 | 26 | ## Install 27 | 28 | ```sh 29 | npm install @atiladev/usecalendar 30 | 31 | or 32 | 33 | yarn add @atiladev/usecalendar 34 | ``` 35 | 36 | After installing the hook, you'll have to install the following libraries 37 | 38 | ```sh 39 | $ npx expo install expo-calendar expo-localization @react-native-async-storage/async-storage 40 | ``` 41 | 42 | ## How to use it 43 | 44 | Start by importing the `useCalendar` hook and then import the packages that you need to interact with the Calendar: 45 | 46 | ```js 47 | import useCalendar from "@atiladev/usecalendar"; 48 | 49 | const { 50 | addEventsToCalendar, 51 | createCalendar, 52 | deleteCalendar, 53 | getCalendarId, 54 | getEvents, 55 | getPermission, 56 | isThereEvents, 57 | openSettings, 58 | } = useCalendar("App_Name", "#BADA55", "Calendar_Name"); 59 | 60 | const createCalAndEvent = async () => { 61 | const granted = await getPermission(); 62 | 63 | if (granted) { 64 | await createCalendar(); 65 | let eventExists = await isThereEvents(); 66 | 67 | if (!eventExists) { 68 | try { 69 | addEventsToCalendar("Event title", new Date(), new Date()); 70 | } catch (e) { 71 | // Something went wrong 72 | } 73 | } 74 | } else { 75 | openSettings(); 76 | } 77 | }; 78 | 79 | const removeCalendar = () => deleteCalendar(); 80 | ``` 81 | 82 | ### useCalendar 83 | 84 | ```js 85 | useCalendar(title: string, color: string, storeName: string); 86 | ``` 87 | 88 | useCalendar returns: 89 | 90 | ```js 91 | getPermission: () => Promise 92 | 93 | createCalendar: () => Promise 94 | 95 | addEventsToCalendar: (eventTitle: string, eventStartDate: Date, eventEndDate: Date) => Promise 96 | 97 | deleteCalendar: () => Promise 98 | 99 | openSettings: () => Promise 100 | 101 | isThereEvents: () => Promise 102 | 103 | getEvents: () => Promise 104 | 105 | getCalendarId: () => Promise 106 | ``` 107 | 108 | --- 109 | 110 | ⭐️ If this hook is useful for you, please consider giving it a star. This motivates us to continue working on this and adding new features. Thanks! 111 | 112 | **Developed with ❤️ by [Leandro Favre (AtilaDev)](https://github.com/AtilaDev)** 113 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import * as Calendar from 'expo-calendar'; 2 | import { Linking, Platform } from 'react-native'; 3 | import * as Localization from 'expo-localization'; 4 | import AsyncStorage from '@react-native-async-storage/async-storage'; 5 | 6 | const useCalendar = (title: string, color: string, storeName: string) => { 7 | const _getDefaultCalendarSource = async () => { 8 | const defaultCalendar = await Calendar.getDefaultCalendarAsync(); 9 | return defaultCalendar.source; 10 | }; 11 | 12 | const _storeCalendar = async (storeName: string, calendarId: string) => { 13 | try { 14 | await AsyncStorage.setItem(storeName, calendarId); 15 | } catch (e) {} 16 | }; 17 | 18 | const _getCalendarStored = async (storeName: string) => { 19 | try { 20 | const calendarStored = await AsyncStorage.getItem(storeName); 21 | if (calendarStored !== null) return calendarStored; 22 | } catch (e) {} 23 | }; 24 | 25 | const openSettings = () => Linking.openSettings(); 26 | 27 | const getPermission = async () => { 28 | const { status } = await Calendar.requestCalendarPermissionsAsync(); 29 | if (status === 'granted') { 30 | const calendars = await Calendar.getCalendarsAsync( 31 | Calendar.EntityTypes.EVENT 32 | ); 33 | return calendars; 34 | } 35 | }; 36 | 37 | const createCalendar = async () => { 38 | const defaultCalendarSource = 39 | Platform.OS === 'ios' 40 | ? await _getDefaultCalendarSource() 41 | : { isLocalAccount: true, name: title }; 42 | 43 | const thereIsACalendar = await _getCalendarStored(storeName); 44 | 45 | if (!thereIsACalendar) { 46 | const newCalendarID = await Calendar.createCalendarAsync({ 47 | title, 48 | color, 49 | entityType: Calendar.EntityTypes.EVENT, 50 | // @ts-ignore 51 | sourceId: defaultCalendarSource.id, 52 | source: defaultCalendarSource as any, 53 | name: storeName, 54 | ownerAccount: 'personal', 55 | accessLevel: Calendar.CalendarAccessLevel.OWNER, 56 | }); 57 | _storeCalendar(storeName, newCalendarID); 58 | } 59 | }; 60 | 61 | const deleteCalendar = async () => { 62 | try { 63 | const calendarId = await _getCalendarStored(storeName); 64 | if (calendarId) { 65 | await Calendar.deleteCalendarAsync(calendarId); 66 | await AsyncStorage.removeItem(storeName); 67 | } 68 | } catch (e) {} 69 | }; 70 | 71 | const addEventsToCalendar = async ( 72 | eventTitle: string, 73 | eventStartDate: Date, 74 | eventEndDate: Date 75 | ) => { 76 | const thereIsACalendar = await _getCalendarStored(storeName); 77 | 78 | if (thereIsACalendar) { 79 | const event = { 80 | title: eventTitle, 81 | startDate: eventStartDate, 82 | endDate: eventEndDate, 83 | timeZone: Localization.timezone, 84 | alarms: [ 85 | { 86 | relativeOffset: 0, 87 | method: Calendar.AlarmMethod.ALERT, 88 | }, 89 | ], 90 | }; 91 | 92 | try { 93 | await Calendar.createEventAsync(thereIsACalendar, event); 94 | } catch (e) {} 95 | } 96 | }; 97 | 98 | const isThereEvents = async () => { 99 | const thereIsACalendar = await _getCalendarStored(storeName); 100 | let thereIs: Calendar.Event[] = []; 101 | if (thereIsACalendar) { 102 | thereIs = await Calendar.getEventsAsync( 103 | [thereIsACalendar], 104 | new Date(2021, 0), 105 | new Date(new Date().getFullYear() + 1, 0) 106 | ); 107 | } 108 | return thereIs.length !== 0; 109 | }; 110 | 111 | const getEvents = async () => { 112 | const thereIsACalendar = await _getCalendarStored(storeName); 113 | let thereIs: Calendar.Event[] = []; 114 | if (thereIsACalendar) { 115 | thereIs = await Calendar.getEventsAsync( 116 | [thereIsACalendar], 117 | new Date(2021, 0), 118 | new Date(new Date().getFullYear() + 1, 0) 119 | ); 120 | } 121 | return thereIs; 122 | }; 123 | 124 | const getCalendarId = async () => { 125 | return await _getCalendarStored(storeName); 126 | }; 127 | 128 | return { 129 | addEventsToCalendar, 130 | createCalendar, 131 | deleteCalendar, 132 | getCalendarId, 133 | getEvents, 134 | getPermission, 135 | isThereEvents, 136 | openSettings, 137 | }; 138 | }; 139 | 140 | export default useCalendar; 141 | --------------------------------------------------------------------------------