Skip to content

Commit

Permalink
Fix calendar issues (#836)
Browse files Browse the repository at this point in the history
- [x] Calendar permission issue
- [x] Calendar event time not accurate
- [x] UI add event not updating



<!-- This is an auto-generated comment: release notes by OSS
Entelligence.AI -->
### Summary by Entelligence.AI

- New Feature: Enhanced calendar access and event handling. Users will
now experience improved permission checks and UI feedback for calendar
access.
- Refactor: Updated the `CalendarPage` class to extend `StatefulWidget`
for better state management.
- New Feature: Introduced a new package (`manage_calendar_events`) for
managing calendar events, providing a more robust and efficient way of
handling calendar operations.
- Refactor: Replaced the previous implementation of displaying events
with a new `EventsListWidget` class in the memory detail page, improving
modularity and readability.
- Bug Fix: Adjusted event start and end times to consider local timezone
and UTC offset, ensuring accurate event timing across different time
zones.
<!-- end of auto-generated comment: release notes by OSS Entelligence.AI
-->
  • Loading branch information
beastoin committed Sep 15, 2024
2 parents 54cbcd0 + 01eecd7 commit 605be2e
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 179 deletions.
6 changes: 6 additions & 0 deletions app/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ post_install do |installer|
'PERMISSION_LOCATION=1',
'PERMISSION_LOCATION_WHENINUSE=1',
'PERMISSION_LOCATION_ALWAYS=1',

## dart: PermissionGroup.calendarFullAccess
'PERMISSION_EVENTS_FULL_ACCESS=1',

## dart: PermissionGroup.calendar
'PERMISSION_EVENTS=1',
]

end
Expand Down
2 changes: 2 additions & 0 deletions app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'package:friend_private/pages/home/page.dart';
import 'package:friend_private/pages/memory_detail/memory_detail_provider.dart';
import 'package:friend_private/pages/onboarding/wrapper.dart';
import 'package:friend_private/providers/auth_provider.dart';
import 'package:friend_private/providers/calendar_provider.dart';
import 'package:friend_private/providers/capture_provider.dart';
import 'package:friend_private/providers/device_provider.dart';
import 'package:friend_private/providers/home_provider.dart';
Expand Down Expand Up @@ -190,6 +191,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
update: (BuildContext context, value, MemoryDetailProvider? previous) =>
(previous?..setPluginProvider(value)) ?? MemoryDetailProvider(),
),
ChangeNotifierProvider(create: (context) => CalenderProvider()),
],
builder: (context, child) {
return WithForegroundTask(
Expand Down
161 changes: 113 additions & 48 deletions app/lib/pages/memory_detail/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,54 +144,55 @@ class GetSummaryWidgets extends StatelessWidget {
],
)
: const SizedBox.shrink(),
...memory.structured.events.mapIndexed<Widget>((idx, event) {
print(event.toJson());
return ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
event.title,
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text(
'${dateTimeFormat('MMM d, yyyy', event.startsAt)} at ${dateTimeFormat('h:mm a', event.startsAt)} ~ ${event.duration} minutes.',
style: const TextStyle(color: Colors.grey, fontSize: 15),
),
),
trailing: IconButton(
onPressed: event.created
? null
: () {
var calEnabled = SharedPreferencesUtil().calendarEnabled;
var calSelected = SharedPreferencesUtil().calendarId.isNotEmpty;
if (!calEnabled || !calSelected) {
routeToPage(context, const CalendarPage());
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(!calEnabled
? 'Enable calendar integration to add events'
: 'Select a calendar to add events to'),
));
return;
}
context.read<MemoryDetailProvider>().updateEventState(true, idx);
setMemoryEventsState(memory.id, [idx], [true]);
CalendarUtil().createEvent(
event.title,
event.startsAt,
event.duration,
description: event.description,
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Event added to calendar'),
),
);
},
icon: Icon(event.created ? Icons.check : Icons.add, color: Colors.white),
),
);
}),
const EventsListWidget(),
// ...memory.structured.events.mapIndexed<Widget>((idx, event) {
// print(event.toJson());
// return ListTile(
// contentPadding: EdgeInsets.zero,
// title: Text(
// event.title,
// style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600),
// ),
// subtitle: Padding(
// padding: const EdgeInsets.only(top: 4.0),
// child: Text(
// '${dateTimeFormat('MMM d, yyyy', event.startsAt)} at ${dateTimeFormat('h:mm a', event.startsAt)} ~ ${event.duration} minutes.',
// style: const TextStyle(color: Colors.grey, fontSize: 15),
// ),
// ),
// trailing: IconButton(
// onPressed: event.created
// ? null
// : () {
// var calEnabled = SharedPreferencesUtil().calendarEnabled;
// var calSelected = SharedPreferencesUtil().calendarId.isNotEmpty;
// if (!calEnabled || !calSelected) {
// routeToPage(context, const CalendarPage());
// ScaffoldMessenger.of(context).showSnackBar(SnackBar(
// content: Text(!calEnabled
// ? 'Enable calendar integration to add events'
// : 'Select a calendar to add events to'),
// ));
// return;
// }
// context.read<MemoryDetailProvider>().updateEventState(true, idx);
// setMemoryEventsState(memory.id, [idx], [true]);
// CalendarUtil().createEvent(
// event.title,
// event.startsAt,
// event.duration,
// description: event.description,
// );
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(
// content: Text('Event added to calendar'),
// ),
// );
// },
// icon: Icon(event.created ? Icons.check : Icons.add, color: Colors.white),
// ),
// );
// }),
memory.structured.events.isNotEmpty ? const SizedBox(height: 40) : const SizedBox.shrink(),
],
);
Expand All @@ -200,6 +201,70 @@ class GetSummaryWidgets extends StatelessWidget {
}
}

class EventsListWidget extends StatelessWidget {
const EventsListWidget({super.key});

@override
Widget build(BuildContext context) {
return Consumer<MemoryDetailProvider>(
builder: (context, provider, child) {
return ListView.builder(
itemCount: provider.memory.structured.events.length,
shrinkWrap: true,
itemBuilder: (context, idx) {
var event = provider.memory.structured.events[idx];
return ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
event.title,
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text(
'${dateTimeFormat('MMM d, yyyy', event.startsAt)} at ${dateTimeFormat('h:mm a', event.startsAt)} ~ ${event.duration} minutes.',
style: const TextStyle(color: Colors.grey, fontSize: 15),
),
),
trailing: IconButton(
onPressed: event.created
? null
: () {
var calEnabled = SharedPreferencesUtil().calendarEnabled;
var calSelected = SharedPreferencesUtil().calendarId.isNotEmpty;
if (!calEnabled || !calSelected) {
routeToPage(context, const CalendarPage());
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(!calEnabled
? 'Enable calendar integration to add events'
: 'Select a calendar to add events to'),
));
return;
}
context.read<MemoryDetailProvider>().updateEventState(true, idx);
setMemoryEventsState(provider.memory.id, [idx], [true]);
CalendarUtil().createEvent(
event.title,
event.startsAt,
event.duration,
description: event.description,
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Event added to calendar'),
),
);
},
icon: Icon(event.created ? Icons.check : Icons.add, color: Colors.white),
),
);
},
);
},
);
}
}

class GetEditTextField extends StatefulWidget {
final bool enabled;
final String overview;
Expand Down
26 changes: 8 additions & 18 deletions app/lib/pages/settings/calendar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,18 @@ import 'package:friend_private/widgets/extensions/functions.dart';
import 'package:gradient_borders/box_borders/gradient_box_border.dart';
import 'package:provider/provider.dart';

class CalendarPage extends StatelessWidget {
class CalendarPage extends StatefulWidget {
const CalendarPage({super.key});

@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CalenderProvider(),
child: const _CalendarPage(),
);
}
}

class _CalendarPage extends StatefulWidget {
const _CalendarPage({super.key});

@override
State<_CalendarPage> createState() => __CalendarPageState();
State<CalendarPage> createState() => _CalendarPageState();
}

class __CalendarPageState extends State<_CalendarPage> {
class _CalendarPageState extends State<CalendarPage> {
@override
void initState() {
() {
Provider.of<CalenderProvider>(context, listen: false).initialize();
() async {
await Provider.of<CalenderProvider>(context, listen: false).initialize();
}.withPostFrameCallback();
super.initState();
}
Expand Down Expand Up @@ -67,7 +55,9 @@ class __CalendarPageState extends State<_CalendarPage> {
),
Switch(
value: provider.calendarEnabled,
onChanged: provider.onCalendarSwitchChanged,
onChanged: (v) async {
await provider.onCalendarSwitchChanged(v);
},
),
],
),
Expand Down
69 changes: 43 additions & 26 deletions app/lib/providers/calendar_provider.dart
Original file line number Diff line number Diff line change
@@ -1,33 +1,51 @@
import 'package:device_calendar/device_calendar.dart';
// import 'package:device_calendar/device_calendar.dart';
import 'package:flutter/material.dart';
import 'package:friend_private/backend/preferences.dart';
import 'package:friend_private/utils/alerts/app_snackbar.dart';
import 'package:friend_private/utils/analytics/mixpanel.dart';
import 'package:friend_private/utils/features/calendar.dart';
import 'package:manage_calendar_events/manage_calendar_events.dart';
import 'package:permission_handler/permission_handler.dart';

class CalenderProvider extends ChangeNotifier {
List<Calendar> calendars = [];
bool calendarEnabled = false;
final CalendarUtil _calendarUtil = CalendarUtil();
final MixpanelManager _mixpanelManager = MixpanelManager();
final SharedPreferencesUtil _sharedPreferencesUtil = SharedPreferencesUtil();
void initialize() {
calendarEnabled = _sharedPreferencesUtil.calendarEnabled;
if (calendarEnabled) _getCalendars();
bool isLoading = false;

void setLoading(bool value) {
isLoading = value;
notifyListeners();
}

Future<void> initialize() async {
calendarEnabled = await hasCalendarAccess();
if (await hasCalendarAccess()) await _getCalendars();
}

Future<void> _getCalendars() async {
calendars = await _calendarUtil.getCalendars();
calendars = await _calendarUtil.fetchCalendars();
notifyListeners();
}

Future<bool> hasCalendarAccess() async {
return await _calendarUtil.checkCalendarPermission();
}

Future<void> onCalendarSwitchChanged(bool s) async {
if (s) {
bool hasAccess = await _calendarUtil.enableCalendarAccess();
print('onCalendarSwitchChanged: hasAccess: $hasAccess');
if (hasAccess) {
var res = await Permission.calendarFullAccess.request();
print('res: $res');
_sharedPreferencesUtil.calendarPermissionAlreadyRequested = true;
bool hasAccess = await hasCalendarAccess();
print('hasAccess: $hasAccess');
if (res.isGranted || hasAccess) {
setLoading(true);

await _getCalendars();
setLoading(false);
if (calendars.isEmpty) {
AppSnackbar.showSnackbar(
'No calendars found. Please check your device settings.',
Expand All @@ -38,23 +56,23 @@ class CalenderProvider extends ChangeNotifier {
calendarEnabled = true;
_mixpanelManager.calendarEnabled();
}
} else if ((await Permission.calendarFullAccess.isDenied ||
await Permission.calendarFullAccess.isPermanentlyDenied) &&
_sharedPreferencesUtil.calendarPermissionAlreadyRequested) {
AppSnackbar.showSnackbar(
'Calendar access was denied. Please enable it in your app settings.',
duration: const Duration(seconds: 5),
// action: SnackBarAction(
// label: 'Open Settings',
// onPressed: () => _calendarUtil.openAppSettings(),
// ),
);
calendarEnabled = false;
} else {
bool wasAsked = await _calendarUtil.calendarPermissionAsked();
if (wasAsked) {
AppSnackbar.showSnackbar(
'Calendar access was denied. Please enable it in your app settings.',
duration: const Duration(seconds: 5),
// action: SnackBarAction(
// label: 'Open Settings',
// onPressed: () => _calendarUtil.openAppSettings(),
// ),
);
} else {
AppSnackbar.showSnackbar(
'Failed to request calendar access. Please try again.',
duration: const Duration(seconds: 5),
);
}
AppSnackbar.showSnackbar(
'Failed to request calendar access. Please try again.',
duration: const Duration(seconds: 5),
);
calendarEnabled = false;
}
} else {
Expand All @@ -63,11 +81,10 @@ class CalenderProvider extends ChangeNotifier {
_mixpanelManager.calendarDisabled();
calendarEnabled = false;
}

_sharedPreferencesUtil.calendarPermissionAlreadyRequested = await _calendarUtil.calendarPermissionAsked();
_sharedPreferencesUtil.calendarEnabled = calendarEnabled;
notifyListeners();
}

void onCalendarTypeChanged(String? v) {
_sharedPreferencesUtil.calendarType = v!;
_mixpanelManager.calendarTypeChanged(v);
Expand Down
Loading

0 comments on commit 605be2e

Please sign in to comment.