From 7461e8b1236f4cba2c8bb81dac420048cb716fe3 Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Wed, 3 Sep 2025 19:24:02 -0400 Subject: [PATCH] Add right-click context menu to external calendars for deletion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users can now right-click on external calendar items in the sidebar to access a context menu with a "Delete Calendar" option. The delete action removes the calendar from both the server and local state, including all associated events from the calendar display. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/src/app.rs | 33 ++++++++++++++ frontend/src/components/sidebar.rs | 69 +++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/frontend/src/app.rs b/frontend/src/app.rs index e5a1cba..b667966 100644 --- a/frontend/src/app.rs +++ b/frontend/src/app.rs @@ -1029,6 +1029,39 @@ pub fn App() -> Html { }); } })} + on_external_calendar_delete={Callback::from({ + let external_calendars = external_calendars.clone(); + let external_calendar_events = external_calendar_events.clone(); + move |id: i32| { + let external_calendars = external_calendars.clone(); + let external_calendar_events = external_calendar_events.clone(); + wasm_bindgen_futures::spawn_local(async move { + // Delete the external calendar from the server + if let Err(err) = CalendarService::delete_external_calendar(id).await { + web_sys::console::log_1( + &format!("Failed to delete external calendar: {}", err).into(), + ); + return; + } + + // Remove calendar from local state + let mut calendars = (*external_calendars).clone(); + calendars.retain(|c| c.id != id); + external_calendars.set(calendars.clone()); + + // Remove events from this calendar + let mut events = (*external_calendar_events).clone(); + events.retain(|e| { + if let Some(ref calendar_path) = e.calendar_path { + calendar_path != &format!("external_{}", id) + } else { + true + } + }); + external_calendar_events.set(events); + }); + } + })} color_picker_open={(*color_picker_open).clone()} on_color_change={on_color_change} on_color_picker_toggle={on_color_picker_toggle} diff --git a/frontend/src/components/sidebar.rs b/frontend/src/components/sidebar.rs index 2623b99..afb367c 100644 --- a/frontend/src/components/sidebar.rs +++ b/frontend/src/components/sidebar.rs @@ -104,6 +104,7 @@ pub struct SidebarProps { pub on_create_external_calendar: Callback<()>, pub external_calendars: Vec, pub on_external_calendar_toggle: Callback, + pub on_external_calendar_delete: Callback, pub color_picker_open: Option, pub on_color_change: Callback<(String, String)>, pub on_color_picker_toggle: Callback, @@ -119,6 +120,7 @@ pub struct SidebarProps { #[function_component(Sidebar)] pub fn sidebar(props: &SidebarProps) -> Html { + let external_context_menu_open = use_state(|| None::); let on_view_change = { let on_view_change = props.on_view_change.clone(); Callback::from(move |e: Event| { @@ -158,6 +160,30 @@ pub fn sidebar(props: &SidebarProps) -> Html { }) }; + let on_external_calendar_context_menu = { + let external_context_menu_open = external_context_menu_open.clone(); + Callback::from(move |(e, cal_id): (MouseEvent, i32)| { + e.prevent_default(); + external_context_menu_open.set(Some(cal_id)); + }) + }; + + let on_external_calendar_delete = { + let on_external_calendar_delete = props.on_external_calendar_delete.clone(); + let external_context_menu_open = external_context_menu_open.clone(); + Callback::from(move |cal_id: i32| { + on_external_calendar_delete.emit(cal_id); + external_context_menu_open.set(None); + }) + }; + + let close_external_context_menu = { + let external_context_menu_open = external_context_menu_open.clone(); + Callback::from(move |_| { + external_context_menu_open.set(None); + }) + }; + html! {