builttoroam / device_calendar

A cross platform plugin for modifying calendars on the user's device
https://pub.dev/packages/device_calendar
BSD 3-Clause "New" or "Revised" License
259 stars 258 forks source link

Delay fetching events when opening release ios build #525

Open crobertson247 opened 4 months ago

crobertson247 commented 4 months ago

I have developed an app that reads information from the users calendar, on android when I first open the app the events load in, however, when I do the same on iOS, I get a message saying "no events found", but if i close and reopen the app the events load in. The "no events found" is custom code I implemented if the event array returned is empty. I've noticed the events only are loaded on the second time launching the app.

I want the events to be loaded in on the first time the user opens the app without having to close and reopen it for the events to load.

Iphone 12 pro max (using test flight)

Code:

import 'dart:developer';
import 'package:presentpal/services/preferences_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:device_calendar/device_calendar.dart';
import 'package:timezone/timezone.dart' as tz;
import 'dart:async';

class CalendarService {
  Future<bool> checkAndRequestCalendarPermission() async {
    PreferencesService prefs = PreferencesService();
    bool hasPermission = await prefs.getCalendarPrefferences();
    bool isGranted = false;

    if (!hasPermission) {
      bool status = await Permission.calendarFullAccess.request().isGranted;
      if (status) {
        // Permission granted, proceed with calendar operations
        prefs.setCalendarPrefferences(true);
        isGranted = true;
      } else {
        // Permission denied, handle accordingly
        prefs.setCalendarPrefferences(false);
        isGranted = false;
      }
    } else if (hasPermission) {
      isGranted = true;
    }
    return isGranted;
  }

  //Methods to get events from calendar
  Future<List<Event>> getEventsFromCalendar() async {
    try {
      final DeviceCalendarPlugin deviceCalendarPlugin = DeviceCalendarPlugin();
      var now = DateTime.now();
      var start = DateTime(now.year, now.month, now.day, 0, 0);
      var end = now
          .add(const Duration(days: 90)); // Fetch events for the next 60 days

      final calendarsResult = await deviceCalendarPlugin.retrieveCalendars();
      final calendars = calendarsResult.data;

      List<Event> allEvents = [];

      if (calendars != null && calendars.isNotEmpty) {
        for (final calendar in calendars) {
          final eventsResult = await deviceCalendarPlugin.retrieveEvents(
            calendar.id,
            RetrieveEventsParams(startDate: start, endDate: end),
          );

          final events = eventsResult.data?.cast<Event>() ?? [];
          allEvents.addAll(events);
        }
      }

      return allEvents;
    } catch (e) {
      print(e);
      return [];
    }
  }

  Future<void> addEventToCalendar(String title, String description,
      DateTime startDate, DateTime endDate) async {
    try {
      final DeviceCalendarPlugin deviceCalendarPlugin = DeviceCalendarPlugin();
      final calendar = await deviceCalendarPlugin.retrieveCalendars().then(
        (value) {
          final defaultCalendars =
              value.data?.where((cal) => cal.isDefault ?? false) ?? [];
          return defaultCalendars.isNotEmpty ? defaultCalendars.first : null;
        },
      );
      Duration offsetTime = DateTime.now().timeZoneOffset;
      startDate = startDate.add(offsetTime);
      final tz.TZDateTime startTZDateTime =
          tz.TZDateTime.from(startDate, tz.local);
      endDate = endDate.add(offsetTime);
      final tz.TZDateTime endTZDateTime = tz.TZDateTime.from(endDate, tz.local);
      final event = Event(
        calendar!.id,
        title: title,
        description: description,
        start: startTZDateTime,
        end: endTZDateTime,
      );

      await deviceCalendarPlugin.createOrUpdateEvent(event);
    } catch (e) {
      log(e.toString());
    }
  }

  DateTime getDateWithoutTimeZone(DateTime dateTime) {
    return DateTime.utc(dateTime.year, dateTime.month, dateTime.day,
        dateTime.hour, dateTime.minute);
  }

  Future<DateTime> convertToTimeZone(
      DateTime localDateTime, tz.Location desiredTimeZone) async {
    DateTime dateInTimeZone =
        tz.TZDateTime.from(localDateTime.toUtc(), desiredTimeZone);

    Duration timeDifferenceFromDesiredTimezone =
        getDateWithoutTimeZone(localDateTime)
            .difference(getDateWithoutTimeZone(dateInTimeZone));

    DateTime utcTimeInDesiredTimeZone =
        dateInTimeZone.add(timeDifferenceFromDesiredTimezone).toUtc();

    return utcTimeInDesiredTimeZone;
  }
}

I've also noticed on iOS using this code, all day events are turned into multiday events, I think it might be because iOS already gives a localized time and me localizing it again is causing the unexpected result but I have yet to test whether this is the issue:

//2012/1/1 - represents a null value for DateTime
  Appointment _convertToAppointment(Event event) {
    return Appointment(
      startTime: event.start?.toLocal() ?? DateTime(2012, 1, 1),
      endTime: event.end?.toLocal() ?? DateTime(2012, 1, 1),
      subject:
          '${event.title}' ??
              'No title',
      notes: event.description,
      color: const Color.fromARGB(255, 3, 168, 244).withOpacity(0.5),
    );
  }
crobertson247 commented 4 months ago

I get this error when i first run the app (i have already promoted to enable full calendar access and granted it) and i still get :

Device calendar plugin ran into an issue. Platform specific exception [401], with message :"The user has not allowed this application to modify their calendar(s)", has been thrown.

After restarting the app it works fine, how can i fix it so that the calendar is shown after opening the app?

Peng1104 commented 4 months ago

I get this error when i first run the app (i have already promoted to enable full calendar access and granted it) and i still get :

Device calendar plugin ran into an issue. Platform specific exception [401], with message :"The user has not allowed this application to modify their calendar(s)", has been thrown.

After restarting the app it works fine, how can i fix it so that the calendar is shown after opening the app?

527