Implement real-time event refresh functionality

- Backend: Add GET /api/calendar/events/:uid endpoint for single event refresh
- Backend: Implement fetch_event_by_uid method to retrieve updated events from CalDAV
- Frontend: Add event click callback system to trigger refresh on interaction
- Frontend: Display loading state with orange pulsing animation during refresh
- Frontend: Smart event data updates without full calendar reload
- Frontend: Graceful error handling with fallback to cached data
- CSS: Add refreshing animation for visual feedback during updates

Events now automatically refresh from CalDAV server when clicked, ensuring
users always see the most current event data including changes made in
other calendar applications.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-08-28 17:51:30 -04:00
parent 1c4857ccad
commit d945c46e5a
7 changed files with 178 additions and 5 deletions

View File

@@ -110,6 +110,7 @@ fn CalendarView() -> Html {
let events = use_state(|| HashMap::<NaiveDate, Vec<CalendarEvent>>::new());
let loading = use_state(|| true);
let error = use_state(|| None::<String>);
let refreshing_event = use_state(|| None::<String>);
// Get current auth token
let auth_token: Option<String> = LocalStorage::get("auth_token").ok();
@@ -118,6 +119,57 @@ fn CalendarView() -> Html {
let current_year = today.year();
let current_month = today.month();
// Event refresh callback
let on_event_click = {
let events = events.clone();
let refreshing_event = refreshing_event.clone();
let auth_token = auth_token.clone();
Callback::from(move |event: CalendarEvent| {
if let Some(token) = auth_token.clone() {
let events = events.clone();
let refreshing_event = refreshing_event.clone();
let uid = event.uid.clone();
refreshing_event.set(Some(uid.clone()));
wasm_bindgen_futures::spawn_local(async move {
let calendar_service = CalendarService::new();
match calendar_service.refresh_event(&token, &uid).await {
Ok(Some(refreshed_event)) => {
// Update the event in the existing events map
let mut updated_events = (*events).clone();
for (_, day_events) in updated_events.iter_mut() {
for existing_event in day_events.iter_mut() {
if existing_event.uid == uid {
*existing_event = refreshed_event.clone();
break;
}
}
}
events.set(updated_events);
}
Ok(None) => {
// Event was deleted, remove it from the map
let mut updated_events = (*events).clone();
for (_, day_events) in updated_events.iter_mut() {
day_events.retain(|e| e.uid != uid);
}
events.set(updated_events);
}
Err(_err) => {
// Log error but don't show it to user - keep using cached event
// Silently fall back to cached event data
}
}
refreshing_event.set(None);
});
}
})
};
// Fetch events when component mounts
{
let events = events.clone();
@@ -165,15 +217,16 @@ fn CalendarView() -> Html {
</div>
}
} else if let Some(err) = (*error).clone() {
let dummy_callback = Callback::from(|_: CalendarEvent| {});
html! {
<div class="calendar-error">
<p>{format!("Error: {}", err)}</p>
<Calendar events={HashMap::new()} />
<Calendar events={HashMap::new()} on_event_click={dummy_callback} refreshing_event_uid={(*refreshing_event).clone()} />
</div>
}
} else {
html! {
<Calendar events={(*events).clone()} />
<Calendar events={(*events).clone()} on_event_click={on_event_click} refreshing_event_uid={(*refreshing_event).clone()} />
}
}
}