react-native-calendar-events icon indicating copy to clipboard operation
react-native-calendar-events copied to clipboard

Ar you having write issues on Android, read this?

Open ricbermo opened this issue 8 months ago • 2 comments

So, you HAVE TO USE the provided "permission" functions in order to get this working. I was using react-native-permissions to get the WRITE permission granted but I was just getting "write" errors. Enjoy the snipped

import {useCallback, useEffect, useState} from 'react';
import {Platform} from 'react-native';
import RNCalendarEvents, {
  CalendarEventReadable,
  CalendarEventWritable,
  Options,
} from 'react-native-calendar-events';
import {formatRFC3339, addMonths} from 'date-fns';

const NOW = new Date();
const IN_ONE_MONTH = addMonths(NOW, 1);

type UseCalendarEventsProps = {
  phoneCalendars: string[];
  loadEvents: (
    startDate: Date,
    endDate: Date,
  ) => Promise<CalendarEventReadable[]>;
  saveEventToCalendar: (
    title: string,
    details: CalendarEventWritable,
    options?: Options,
  ) => Promise<void>;
  transformDate: (date: Date) => string | undefined;
  loadError?: Error;
  saveError?: Error;
};

// results is undefined on the iOS sim.
// use a device to test calendar access
async function requestCalendarAccess(): Promise<boolean> {
  try {
    await RNCalendarEvents.checkPermissions(false);
    const res = await RNCalendarEvents.requestPermissions(false);
    return res === 'authorized';
  } catch (error) {
    return false;
  }
}

/*
 * This function will return ALL of the calendars saved in your phone
 * The idea is to return only YOUR calendar, exclusing birthdays, holidays
 * and subscribed calendars.
 * */

export const loadCalendars = async () => {
  try {
    await requestCalendarAccess();
    const calendars = await RNCalendarEvents.findCalendars();
    return calendars?.filter(c => c?.isPrimary)?.map(c => c?.id);
  } catch (e) {
    return [];
  }
};

export function useCalendarEvents(): UseCalendarEventsProps {
  const [saveError, setSaveError] = useState<Error>();
  const [loadError, setLoadError] = useState<Error>();
  const [permissionGranted, setGranted] = useState<boolean>(false);
  const [phoneCalendars, setCalendars] = useState<string[]>([]);

  useEffect(() => {
    requestCalendarAccess().then(granted => setGranted(granted));
  }, []);

  useEffect(() => {
    loadCalendars()
      .then((results: string[]) => {
        setCalendars(results);
      })
      .catch(e => setLoadError(e));
  }, []);

  const transformDate = useCallback((date: Date) => {
    return Platform.select({
      ios: formatRFC3339(date, {fractionDigits: 3}),
      android: date.toISOString(),
    });
  }, []);

  const loadEvents = useCallback(
    async (startDate: Date = NOW, endDate: Date = IN_ONE_MONTH) => {
      const pg = await requestCalendarAccess();
      if (!pg) {
        return [];
      }

      const sd = transformDate(startDate);
      const ed = transformDate(endDate);

      if (sd && ed) {
        return RNCalendarEvents.fetchAllEvents(sd, ed, phoneCalendars);
      } else {
        return [];
      }
    },
    [transformDate, phoneCalendars],
  );

  const saveEventToCalendar = useCallback(
    async (
      title: string,
      details: CalendarEventWritable,
      options?: Options,
    ) => {
      try {
        if (permissionGranted) {
          // you need the calendar ID so the event is actually saved+displayed.
          const calendarId = phoneCalendars[0];
          await RNCalendarEvents.saveEvent(
            title,
            {...details, calendarId},
            {
              ...options,
              sync: true,
            },
          );
        }
      } catch (error) {
        setSaveError(error as Error);
      }
    },
    [permissionGranted, phoneCalendars],
  );

  return {
    phoneCalendars,
    loadEvents,
    saveEventToCalendar,
    transformDate,
    loadError,
    saveError,
  };
}

ricbermo avatar Nov 03 '23 19:11 ricbermo