From 752f513dd9c53b8945fcb68ecd7b10409b886bc7 Mon Sep 17 00:00:00 2001 From: Shubham Jitiya Date: Fri, 8 Nov 2024 12:09:04 +0530 Subject: [PATCH] :sparkles: Add delete recurrence event --- example/lib/pages/event_details_page.dart | 56 ++++++----- .../lib/pages/web/delete_event_dialog.dart | 63 +++++++++++++ lib/src/calendar_event_data.dart | 6 ++ lib/src/enumerations.dart | 7 ++ lib/src/event_controller.dart | 93 +++++++++++++++---- 5 files changed, 188 insertions(+), 37 deletions(-) create mode 100644 example/lib/pages/web/delete_event_dialog.dart diff --git a/example/lib/pages/event_details_page.dart b/example/lib/pages/event_details_page.dart index df1f1d30..dada34dc 100644 --- a/example/lib/pages/event_details_page.dart +++ b/example/lib/pages/event_details_page.dart @@ -1,4 +1,5 @@ import 'package:calendar_view/calendar_view.dart'; +import 'package:example/pages/web/delete_event_dialog.dart'; import 'package:flutter/material.dart'; import '../extension.dart'; @@ -16,6 +17,8 @@ class DetailsPage extends StatelessWidget { @override Widget build(BuildContext context) { + debugPrint('date details page: ${date}'); + return Scaffold( appBar: AppBar( backgroundColor: event.color, @@ -96,26 +99,8 @@ class DetailsPage extends StatelessWidget { children: [ Expanded( child: ElevatedButton( - onPressed: () { - // Delete following events - // final updatedRecurrenceSettings = - // event.recurrenceSettings?.copyWith(endDate: date); - // final updatedEvent = event.copyWith( - // recurrenceSettings: updatedRecurrenceSettings); - // CalendarControllerProvider.of(context) - // .controller - // .update(event, updatedEvent); - List excludeDates = - event.recurrenceSettings?.excludeDates ?? []; - excludeDates.add(date); - final updatedRecurrenceSettings = event.recurrenceSettings - ?.copyWith(excludeDates: excludeDates); - final updatedEvent = event.copyWith( - recurrenceSettings: updatedRecurrenceSettings); - CalendarControllerProvider.of(context) - .controller - .update(event, updatedEvent); - // .remove(event); + onPressed: () async { + await _handleDeleteEvent(context); Navigator.of(context).pop(); }, child: Text('Delete Event'), @@ -133,7 +118,7 @@ class DetailsPage extends StatelessWidget { ), ); - if (result) { + if (result != null) { Navigator.of(context).pop(); } }, @@ -146,4 +131,33 @@ class DetailsPage extends StatelessWidget { ), ); } + + /// Handles the deletion of an event, showing a dialog for repeating events. + /// + /// This method checks if the event is a repeating event. If it is, it shows + /// a dialog to the user to choose the deletion type (e.g., delete this + /// event, delete following events, delete all events). + /// If the event is not repeating, it defaults to deleting all occurrences + /// of the event. + Future _handleDeleteEvent(BuildContext context) async { + DeleteEvent? result; + final isRepeatingEvent = event.recurrenceSettings != null && + (event.recurrenceSettings?.frequency != RepeatFrequency.doNotRepeat); + + if (isRepeatingEvent) { + result = await showDialog( + context: context, + builder: (_) => DeleteEventDialog(), + ); + } else { + result = DeleteEvent.all; + } + if (result != null) { + CalendarControllerProvider.of(context).controller.deleteRecurrenceEvent( + date: date, + event: event, + deleteEventType: result, + ); + } + } } diff --git a/example/lib/pages/web/delete_event_dialog.dart b/example/lib/pages/web/delete_event_dialog.dart new file mode 100644 index 00000000..64bfceb6 --- /dev/null +++ b/example/lib/pages/web/delete_event_dialog.dart @@ -0,0 +1,63 @@ +import 'package:calendar_view/calendar_view.dart'; +import 'package:flutter/material.dart'; + +class DeleteEventDialog extends StatefulWidget { + @override + _RadioDialogState createState() => _RadioDialogState(); +} + +class _RadioDialogState extends State { + DeleteEvent _selectedOption = DeleteEvent.current; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text('Delete recurring event '), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + title: Text('This event'), + value: DeleteEvent.current, + groupValue: _selectedOption, + onChanged: (deleteType) { + if (deleteType != null) { + setState(() => _selectedOption = deleteType); + } + }, + ), + RadioListTile( + title: Text('This and following events'), + value: DeleteEvent.following, + groupValue: _selectedOption, + onChanged: (deleteType) { + if (deleteType != null) { + setState(() => _selectedOption = deleteType); + } + }, + ), + RadioListTile( + title: Text('All events'), + value: DeleteEvent.all, + groupValue: _selectedOption, + onChanged: (deleteType) { + if (deleteType != null) { + setState(() => _selectedOption = deleteType); + } + }, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(_selectedOption), + child: Text('Done'), + ), + ], + ); + } +} diff --git a/lib/src/calendar_event_data.dart b/lib/src/calendar_event_data.dart index c61f65f0..9eeb0b7c 100644 --- a/lib/src/calendar_event_data.dart +++ b/lib/src/calendar_event_data.dart @@ -103,6 +103,12 @@ class CalendarEventData { } } + /// Checks if the given date is in the list of excluded dates. + /// Returns true if the date is excluded, otherwise false. + bool isExcluded(DateTime date) { + return recurrenceSettings?.excludeDates?.contains(date) ?? false; + } + /// Returns a boolean that defines whether current event is occurring on /// [currentDate] or not. /// diff --git a/lib/src/enumerations.dart b/lib/src/enumerations.dart index 73cd3690..105b84d2 100644 --- a/lib/src/enumerations.dart +++ b/lib/src/enumerations.dart @@ -67,3 +67,10 @@ enum RecurrenceEnd { on, after, } + +// TODO(Shubham): Add docs +enum DeleteEvent { + all, + current, + following, +} diff --git a/lib/src/event_controller.dart b/lib/src/event_controller.dart index 665bd34b..e077beb1 100644 --- a/lib/src/event_controller.dart +++ b/lib/src/event_controller.dart @@ -170,6 +170,9 @@ class EventController extends ChangeNotifier { required DateTime eventEndDate, required RecurrenceSettings recurrenceSettings, }) { + if (recurrenceSettings.excludeDates?.contains(currentDate) ?? false) { + return false; + } switch (recurrenceSettings.frequency) { case RepeatFrequency.daily: return _isDailyRecurrence( @@ -197,9 +200,69 @@ class EventController extends ChangeNotifier { } return false; } + + void _deleteCurrentEvent(DateTime date, CalendarEventData event) { + List excludeDates = event.recurrenceSettings?.excludeDates ?? []; + excludeDates.add(date); + final updatedRecurrenceSettings = + event.recurrenceSettings?.copyWith(excludeDates: excludeDates); + final updatedEvent = + event.copyWith(recurrenceSettings: updatedRecurrenceSettings); + update(event, updatedEvent); + } + + /// If the selected date to delete the event is the same as the event's start date, delete all recurrences. + /// Otherwise, delete the event on the selected date and all subsequent recurrences. + void _deleteFollowingEvents(DateTime date, CalendarEventData event) { + final updatedRecurrenceSettings = event.recurrenceSettings?.copyWith( + endDate: date.subtract( + Duration(days: 1), + ), + ); + if (date == event.date) { + remove(event); + } else { + debugPrint('Event: delete event ${event}'); + final updatedEvent = + event.copyWith(recurrenceSettings: updatedRecurrenceSettings); + update(event, updatedEvent); + } + } //#endregion //#region Public Methods + /// Deletes a recurring event based on the specified deletion type. + /// + /// This method handles the deletion of recurring events by determining the type of deletion + /// requested (all events, the current event, or following events) and performing the appropriate action. + /// + /// Takes the following parameters: + /// - [date]: The date of the event to be deleted. + /// - [event]: The event data to be deleted. + /// - [deleteEventType]: The `DeleteEventType` of deletion to perform (all events, the current event, or following events). + /// + /// The method performs the following actions based on the [deleteEventType]: + /// - [DeleteEvent.all]: Removes the entire series of events. + /// - [DeleteEvent.current]: Deletes only the current event. + /// - [DeleteEvent.following]: Deletes the current event and all subsequent events. + void deleteRecurrenceEvent({ + required DateTime date, + required CalendarEventData event, + required DeleteEvent deleteEventType, + }) { + switch (deleteEventType) { + case DeleteEvent.all: + remove(event); + break; + case DeleteEvent.current: + _deleteCurrentEvent(date, event); + break; + case DeleteEvent.following: + _deleteFollowingEvents(date, event); + break; + } + } + /// Add all the events in the list /// If there is an event with same date then void addAll(List> events) { @@ -267,19 +330,20 @@ class EventController extends ChangeNotifier { includeFullDayEvents: includeFullDayEvents); } - /// Get all events including repeated events on that day + /// Retrieves all events for a given date, including repeated events that are not excluded on that day. + /// + /// This method combines events that occur on the specified date with repeated events that are not excluded. + /// It filters out any events that are marked as excluded for the given date. + /// + /// Takes a [date] parameter representing the date for which to retrieve events. + /// Returns a list of [CalendarEventData] objects representing all events on the specified date. List> getAllEventsOnDay(DateTime date) { - final events = getEventsOnDay(date); - - debugPrint('Day: ${date.day} Events: ${events}'); - final repeatedEvents = getRepeatedEvents(date); + final events = + getEventsOnDay(date).where((event) => !event.isExcluded(date)).toList(); + final repeatedEvents = + getRepeatedEvents(date).where((event) => !event.isExcluded(date)); + events.addAll(repeatedEvents); - // For range of events having occurrence add only single time - for (final event in repeatedEvents) { - if (!events.contains(event)) { - events.add(event); - } - } return events; } @@ -299,15 +363,12 @@ class EventController extends ChangeNotifier { continue; } final recurrenceSettings = event.recurrenceSettings; - debugPrint('Date: ${date} | Recurrence settings: ${recurrenceSettings}'); + // debugPrint('Date: ${date} | Recurrence settings: ${recurrenceSettings}'); // if event is not repeating or date is in excluded - if (recurrenceSettings == null || - (recurrenceSettings.excludeDates?.contains(date) ?? false)) { + if (recurrenceSettings == null) { continue; } - debugPrint( - 'Date: ${date} | Is excluded dates: ${recurrenceSettings.excludeDates}'); final isRecurrence = _handleRecurrence( currentDate: date, eventStartDate: event.date,