Fix recurring event series modification via drag and drop operations

This commit resolves the "Failed to fetch" errors when updating recurring
event series through drag operations by implementing proper request
sequencing and fixing time parameter handling.

Key fixes:
- Eliminate HTTP request cancellation by sequencing operations properly
- Add global mutex to prevent CalDAV HTTP race conditions
- Implement complete RFC 5545-compliant series splitting for "this_and_future"
- Fix frontend to pass dragged times instead of original times
- Add comprehensive error handling and request timing logs
- Backend now handles both UPDATE (add UNTIL) and CREATE (new series) in single request

Technical changes:
- Frontend: Remove concurrent CREATE request, pass dragged times to backend
- Backend: Implement full this_and_future logic with sequential operations
- CalDAV: Add mutex serialization and detailed error tracking
- Series: Create new series with occurrence date + dragged times

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-08-30 20:17:36 -04:00
parent 1794cf9a59
commit 783e13eb10
6 changed files with 315 additions and 204 deletions

View File

@@ -437,32 +437,47 @@ pub fn App() -> Html {
let recurrence_str = original_event.rrule.unwrap_or_default();
let recurrence_days = vec![false; 7]; // Default - could be enhanced to parse existing recurrence
// Determine if this is a recurring event that needs series endpoint
let has_recurrence = !recurrence_str.is_empty() && recurrence_str.to_uppercase() != "NONE";
let result = if let Some(scope) = update_scope.as_ref() {
// Use series endpoint
calendar_service.update_series(
&token,
&password,
backend_uid,
original_event.summary.unwrap_or_default(),
original_event.description.unwrap_or_default(),
start_date.clone(),
start_time.clone(),
end_date.clone(),
end_time.clone(),
original_event.location.unwrap_or_default(),
original_event.all_day,
status_str,
class_str,
original_event.priority,
original_event.organizer.as_ref().map(|o| o.cal_address.clone()).unwrap_or_default(),
original_event.attendees.iter().map(|a| a.cal_address.clone()).collect::<Vec<_>>().join(","),
original_event.categories.join(","),
reminder_str,
recurrence_str,
original_event.calendar_path,
scope.clone(),
occurrence_date,
).await
// Use series endpoint for recurring event operations
if !has_recurrence {
web_sys::console::log_1(&"⚠️ Warning: update_scope provided for non-recurring event, using regular endpoint instead".into());
// Fall through to regular endpoint
None
} else {
Some(calendar_service.update_series(
&token,
&password,
backend_uid.clone(),
original_event.summary.clone().unwrap_or_default(),
original_event.description.clone().unwrap_or_default(),
start_date.clone(),
start_time.clone(),
end_date.clone(),
end_time.clone(),
original_event.location.clone().unwrap_or_default(),
original_event.all_day,
status_str.clone(),
class_str.clone(),
original_event.priority,
original_event.organizer.as_ref().map(|o| o.cal_address.clone()).unwrap_or_default(),
original_event.attendees.iter().map(|a| a.cal_address.clone()).collect::<Vec<_>>().join(","),
original_event.categories.join(","),
reminder_str.clone(),
recurrence_str.clone(),
original_event.calendar_path.clone(),
scope.clone(),
occurrence_date,
).await)
}
} else {
None
};
let result = if let Some(series_result) = result {
series_result
} else {
// Use regular endpoint
calendar_service.update_event(
@@ -507,19 +522,8 @@ pub fn App() -> Html {
});
}
Err(err) => {
// Check if this is a network error that occurred after success
let err_str = format!("{}", err);
if err_str.contains("Failed to fetch") || err_str.contains("Network request failed") {
web_sys::console::log_1(&"Update may have succeeded despite network error, reloading...".into());
// Still reload as the update likely succeeded
wasm_bindgen_futures::spawn_local(async {
gloo_timers::future::sleep(std::time::Duration::from_millis(200)).await;
web_sys::window().unwrap().location().reload().unwrap();
});
} else {
web_sys::console::error_1(&format!("Failed to update event: {}", err).into());
web_sys::window().unwrap().alert_with_message(&format!("Failed to update event: {}", err)).unwrap();
}
web_sys::console::error_1(&format!("Failed to update event: {}", err).into());
web_sys::window().unwrap().alert_with_message(&format!("Failed to update event: {}", err)).unwrap();
}
}
});