Implement complete recurring event deletion with EXDATE and RRULE UNTIL support
Frontend Changes: - Add DeleteAction enum with DeleteThis, DeleteFollowing, DeleteSeries options - Update EventContextMenu to show different delete options for recurring events - Add exception_dates field to CalendarEvent struct - Fix occurrence generation to respect EXDATE exclusions - Add comprehensive RRULE parsing with UNTIL date support - Fix UNTIL date parsing to handle backend format (YYYYMMDDTHHMMSSZ) - Enhanced debugging for RRULE processing and occurrence generation Backend Changes: - Add exception_dates field to CalendarEvent struct with EXDATE parsing/generation - Implement update_event method for CalDAV client - Add fetch_event_by_href helper function - Update DeleteEventRequest model with delete_action and occurrence_date fields - Implement proper delete_this logic with EXDATE addition - Implement delete_following logic with RRULE UNTIL modification - Add comprehensive logging for delete operations CalDAV Integration: - Proper EXDATE generation in iCal format for excluded occurrences - RRULE modification with UNTIL clause for partial series deletion - Event updating via CalDAV PUT operations - Full iCal RFC 5545 compliance for recurring event modifications 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
47
src/app.rs
47
src/app.rs
@@ -2,7 +2,7 @@ use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
use gloo_storage::{LocalStorage, Storage};
|
||||
use web_sys::MouseEvent;
|
||||
use crate::components::{Sidebar, CreateCalendarModal, ContextMenu, EventContextMenu, CalendarContextMenu, CreateEventModal, EventCreationData, RouteHandler, EventStatus, EventClass, ReminderType, RecurrenceType};
|
||||
use crate::components::{Sidebar, CreateCalendarModal, ContextMenu, EventContextMenu, CalendarContextMenu, CreateEventModal, EventCreationData, RouteHandler, EventStatus, EventClass, ReminderType, RecurrenceType, DeleteAction};
|
||||
use crate::services::{CalendarService, calendar_service::{UserInfo, CalendarEvent}};
|
||||
use chrono::NaiveDate;
|
||||
|
||||
@@ -475,6 +475,7 @@ pub fn App() -> Html {
|
||||
is_open={*event_context_menu_open}
|
||||
x={event_context_menu_pos.0}
|
||||
y={event_context_menu_pos.1}
|
||||
event={(*event_context_menu_event).clone()}
|
||||
on_close={Callback::from({
|
||||
let event_context_menu_open = event_context_menu_open.clone();
|
||||
move |_| event_context_menu_open.set(false)
|
||||
@@ -484,11 +485,18 @@ pub fn App() -> Html {
|
||||
let event_context_menu_event = event_context_menu_event.clone();
|
||||
let event_context_menu_open = event_context_menu_open.clone();
|
||||
let refresh_calendars = refresh_calendars.clone();
|
||||
move |_: MouseEvent| {
|
||||
move |delete_action: DeleteAction| {
|
||||
if let (Some(token), Some(event)) = ((*auth_token).clone(), (*event_context_menu_event).clone()) {
|
||||
let _refresh_calendars = refresh_calendars.clone();
|
||||
let event_context_menu_open = event_context_menu_open.clone();
|
||||
|
||||
// Log the delete action for now - we'll implement different behaviors later
|
||||
match delete_action {
|
||||
DeleteAction::DeleteThis => web_sys::console::log_1(&"Delete this event".into()),
|
||||
DeleteAction::DeleteFollowing => web_sys::console::log_1(&"Delete following events".into()),
|
||||
DeleteAction::DeleteSeries => web_sys::console::log_1(&"Delete entire series".into()),
|
||||
}
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let calendar_service = CalendarService::new();
|
||||
|
||||
@@ -503,9 +511,37 @@ pub fn App() -> Html {
|
||||
};
|
||||
|
||||
if let (Some(calendar_path), Some(event_href)) = (&event.calendar_path, &event.href) {
|
||||
match calendar_service.delete_event(&token, &password, calendar_path.clone(), event_href.clone()).await {
|
||||
Ok(_) => {
|
||||
web_sys::console::log_1(&"Event deleted successfully!".into());
|
||||
// Convert delete action to string and get occurrence date
|
||||
let action_str = match delete_action {
|
||||
DeleteAction::DeleteThis => "delete_this".to_string(),
|
||||
DeleteAction::DeleteFollowing => "delete_following".to_string(),
|
||||
DeleteAction::DeleteSeries => "delete_series".to_string(),
|
||||
};
|
||||
|
||||
// Get the occurrence date from the clicked event
|
||||
let occurrence_date = Some(event.start.date_naive().format("%Y-%m-%d").to_string());
|
||||
|
||||
web_sys::console::log_1(&format!("🔄 Delete action: {}", action_str).into());
|
||||
web_sys::console::log_1(&format!("🔄 Event UID: {}", event.uid).into());
|
||||
web_sys::console::log_1(&format!("🔄 Event start: {}", event.start).into());
|
||||
web_sys::console::log_1(&format!("🔄 Occurrence date: {:?}", occurrence_date).into());
|
||||
|
||||
match calendar_service.delete_event(
|
||||
&token,
|
||||
&password,
|
||||
calendar_path.clone(),
|
||||
event_href.clone(),
|
||||
action_str,
|
||||
occurrence_date
|
||||
).await {
|
||||
Ok(message) => {
|
||||
web_sys::console::log_1(&format!("Delete response: {}", message).into());
|
||||
|
||||
// Show the message to the user to explain what actually happened
|
||||
if message.contains("Warning") {
|
||||
web_sys::window().unwrap().alert_with_message(&message).unwrap();
|
||||
}
|
||||
|
||||
// Close the context menu
|
||||
event_context_menu_open.set(false);
|
||||
// Force a page reload to refresh the calendar events
|
||||
@@ -513,6 +549,7 @@ pub fn App() -> Html {
|
||||
}
|
||||
Err(err) => {
|
||||
web_sys::console::log_1(&format!("Failed to delete event: {}", err).into());
|
||||
web_sys::window().unwrap().alert_with_message(&format!("Failed to delete event: {}", err)).unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user