From b444ae710df0402577fba0a9008bcd742f3b5518 Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Thu, 28 Aug 2025 21:40:09 -0400 Subject: [PATCH] Refactor app.rs by extracting components for better organization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split monolithic app.rs into focused, reusable components: - Sidebar component for user info, navigation and calendar management - CalendarListItem component for individual calendar items with color picker - RouteHandler component to eliminate duplicated routing logic - Reduced app.rs from 645 to 338 lines (47% reduction) - Improved separation of concerns and maintainability - Clean props-based component communication 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/app.rs | 564 ++++++--------------------- src/components/calendar_list_item.rs | 75 ++++ src/components/mod.rs | 8 +- src/components/route_handler.rs | 226 +++++++++++ src/components/sidebar.rs | 89 +++++ src/services/mod.rs | 2 +- 6 files changed, 522 insertions(+), 442 deletions(-) create mode 100644 src/components/calendar_list_item.rs create mode 100644 src/components/route_handler.rs create mode 100644 src/components/sidebar.rs diff --git a/src/app.rs b/src/app.rs index db89c98..873eed4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,20 +1,10 @@ use yew::prelude::*; use yew_router::prelude::*; use gloo_storage::{LocalStorage, Storage}; -use crate::components::{Login, Calendar, CreateCalendarModal, ContextMenu}; -use crate::services::{CalendarService, CalendarEvent, UserInfo}; -use std::collections::HashMap; -use chrono::{Local, NaiveDate, Datelike}; +use web_sys::MouseEvent; +use crate::components::{Sidebar, CreateCalendarModal, ContextMenu, RouteHandler}; +use crate::services::{CalendarService, calendar_service::UserInfo}; -#[derive(Clone, Routable, PartialEq)] -enum Route { - #[at("/")] - Home, - #[at("/login")] - Login, - #[at("/calendar")] - Calendar, -} #[function_component] pub fn App() -> Html { @@ -23,13 +13,12 @@ pub fn App() -> Html { }); let user_info = use_state(|| -> Option { None }); - let color_picker_open = use_state(|| -> Option { None }); // Store calendar path of open picker + let color_picker_open = use_state(|| -> Option { None }); let create_modal_open = use_state(|| false); let context_menu_open = use_state(|| false); let context_menu_pos = use_state(|| (0i32, 0i32)); let context_menu_calendar_path = use_state(|| -> Option { None }); - // Available colors for calendar customization let available_colors = [ "#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6", "#06B6D4", "#84CC16", "#F97316", @@ -67,7 +56,6 @@ pub fn App() -> Html { wasm_bindgen_futures::spawn_local(async move { let calendar_service = CalendarService::new(); - // Get password from stored credentials let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() @@ -81,10 +69,8 @@ pub fn App() -> Html { if !password.is_empty() { match calendar_service.fetch_user_info(&token, &password).await { Ok(mut info) => { - // Load saved colors from local storage if let Ok(saved_colors_json) = LocalStorage::get::("calendar_colors") { if let Ok(saved_info) = serde_json::from_str::(&saved_colors_json) { - // Update colors with saved preferences for saved_cal in &saved_info.calendars { for cal in &mut info.calendars { if cal.path == saved_cal.path { @@ -119,13 +105,92 @@ pub fn App() -> Html { }) }; - // Clone variables needed for the modal and context menu outside of the conditional blocks - let auth_token_for_modal = auth_token.clone(); - let user_info_for_modal = user_info.clone(); - let create_modal_open_for_modal = create_modal_open.clone(); - let auth_token_for_context = auth_token.clone(); - let user_info_for_context = user_info.clone(); - let context_menu_calendar_path_for_context = context_menu_calendar_path.clone(); + let on_color_change = { + let user_info = user_info.clone(); + let color_picker_open = color_picker_open.clone(); + Callback::from(move |(calendar_path, color): (String, String)| { + if let Some(mut info) = (*user_info).clone() { + for calendar in &mut info.calendars { + if calendar.path == calendar_path { + calendar.color = color.clone(); + break; + } + } + user_info.set(Some(info.clone())); + + if let Ok(json) = serde_json::to_string(&info) { + let _ = LocalStorage::set("calendar_colors", json); + } + } + color_picker_open.set(None); + }) + }; + + let on_color_picker_toggle = { + let color_picker_open = color_picker_open.clone(); + Callback::from(move |calendar_path: String| { + if color_picker_open.as_ref() == Some(&calendar_path) { + color_picker_open.set(None); + } else { + color_picker_open.set(Some(calendar_path)); + } + }) + }; + + let on_calendar_context_menu = { + let context_menu_open = context_menu_open.clone(); + let context_menu_pos = context_menu_pos.clone(); + let context_menu_calendar_path = context_menu_calendar_path.clone(); + Callback::from(move |(event, calendar_path): (MouseEvent, String)| { + context_menu_open.set(true); + context_menu_pos.set((event.client_x(), event.client_y())); + context_menu_calendar_path.set(Some(calendar_path)); + }) + }; + + let refresh_calendars = { + let auth_token = auth_token.clone(); + let user_info = user_info.clone(); + Callback::from(move |_| { + if let Some(token) = (*auth_token).clone() { + let user_info = user_info.clone(); + + wasm_bindgen_futures::spawn_local(async move { + let calendar_service = CalendarService::new(); + + let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { + if let Ok(credentials) = serde_json::from_str::(&credentials_str) { + credentials["password"].as_str().unwrap_or("").to_string() + } else { + String::new() + } + } else { + String::new() + }; + + match calendar_service.fetch_user_info(&token, &password).await { + Ok(mut info) => { + if let Ok(saved_colors_json) = LocalStorage::get::("calendar_colors") { + if let Ok(saved_info) = serde_json::from_str::(&saved_colors_json) { + for saved_cal in &saved_info.calendars { + for cal in &mut info.calendars { + if cal.path == saved_cal.path { + cal.color = saved_cal.color.clone(); + } + } + } + } + } + user_info.set(Some(info)); + } + Err(err) => { + web_sys::console::log_1(&format!("Failed to refresh calendars: {}", err).into()); + } + } + }); + } + }) + }; html! { @@ -134,200 +199,36 @@ pub fn App() -> Html { if auth_token.is_some() { html! { <> - + >()} + on_calendar_context_menu={on_calendar_context_menu} + />
- render={move |route| { - let auth_token = (*auth_token).clone(); - let on_login = on_login.clone(); - - match route { - Route::Home => { - if auth_token.is_some() { - html! { to={Route::Calendar}/> } - } else { - html! { to={Route::Login}/> } - } - } - Route::Login => { - if auth_token.is_some() { - html! { to={Route::Calendar}/> } - } else { - html! { } - } - } - Route::Calendar => { - if auth_token.is_some() { - html! { } - } else { - html! { to={Route::Login}/> } - } - } - } - }} /> +
} } else { html! { } } @@ -336,22 +237,21 @@ pub fn App() -> Html { , Option)| { if let Some(token) = (*auth_token).clone() { - let user_info = user_info.clone(); + let refresh_calendars = refresh_calendars.clone(); let create_modal_open = create_modal_open.clone(); wasm_bindgen_futures::spawn_local(async move { let calendar_service = CalendarService::new(); - // Get password from stored credentials let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() @@ -365,32 +265,11 @@ pub fn App() -> Html { match calendar_service.create_calendar(&token, &password, name, description, color).await { Ok(_) => { web_sys::console::log_1(&"Calendar created successfully!".into()); - // Refresh user info to show the new calendar - match calendar_service.fetch_user_info(&token, &password).await { - Ok(mut info) => { - // Load saved colors from local storage - if let Ok(saved_colors_json) = LocalStorage::get::("calendar_colors") { - if let Ok(saved_info) = serde_json::from_str::(&saved_colors_json) { - for saved_cal in &saved_info.calendars { - for cal in &mut info.calendars { - if cal.path == saved_cal.path { - cal.color = saved_cal.color.clone(); - } - } - } - } - } - user_info.set(Some(info)); - } - Err(err) => { - web_sys::console::log_1(&format!("Failed to refresh calendars: {}", err).into()); - } - } + refresh_calendars.emit(()); create_modal_open.set(false); } Err(err) => { web_sys::console::log_1(&format!("Failed to create calendar: {}", err).into()); - // TODO: Show error to user create_modal_open.set(false); } } @@ -410,17 +289,16 @@ pub fn App() -> Html { move |_| context_menu_open.set(false) })} on_delete={Callback::from({ - let auth_token = auth_token_for_context.clone(); - let user_info = user_info_for_context.clone(); - let context_menu_calendar_path = context_menu_calendar_path_for_context.clone(); + let auth_token = auth_token.clone(); + let context_menu_calendar_path = context_menu_calendar_path.clone(); + let refresh_calendars = refresh_calendars.clone(); move |_: MouseEvent| { if let (Some(token), Some(calendar_path)) = ((*auth_token).clone(), (*context_menu_calendar_path).clone()) { - let user_info = user_info.clone(); + let refresh_calendars = refresh_calendars.clone(); wasm_bindgen_futures::spawn_local(async move { let calendar_service = CalendarService::new(); - // Get password from stored credentials let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { if let Ok(credentials) = serde_json::from_str::(&credentials_str) { credentials["password"].as_str().unwrap_or("").to_string() @@ -434,31 +312,10 @@ pub fn App() -> Html { match calendar_service.delete_calendar(&token, &password, calendar_path).await { Ok(_) => { web_sys::console::log_1(&"Calendar deleted successfully!".into()); - // Refresh user info to remove the deleted calendar - match calendar_service.fetch_user_info(&token, &password).await { - Ok(mut info) => { - // Load saved colors from local storage - if let Ok(saved_colors_json) = LocalStorage::get::("calendar_colors") { - if let Ok(saved_info) = serde_json::from_str::(&saved_colors_json) { - for saved_cal in &saved_info.calendars { - for cal in &mut info.calendars { - if cal.path == saved_cal.path { - cal.color = saved_cal.color.clone(); - } - } - } - } - } - user_info.set(Some(info)); - } - Err(err) => { - web_sys::console::log_1(&format!("Failed to refresh calendars: {}", err).into()); - } - } + refresh_calendars.emit(()); } Err(err) => { web_sys::console::log_1(&format!("Failed to delete calendar: {}", err).into()); - // TODO: Show error to user } } }); @@ -469,177 +326,4 @@ pub fn App() -> Html {
} -} - -#[derive(Properties, PartialEq)] -pub struct CalendarViewProps { - pub user_info: Option, -} - -#[function_component] -fn CalendarView(props: &CalendarViewProps) -> Html { - let events = use_state(|| HashMap::>::new()); - let loading = use_state(|| true); - let error = use_state(|| None::); - let refreshing_event = use_state(|| None::); - - // Get current auth token - let auth_token: Option = LocalStorage::get("auth_token").ok(); - - let today = Local::now().date_naive(); - 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(); - - // Get password from stored credentials - let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { - if let Ok(credentials) = serde_json::from_str::(&credentials_str) { - credentials["password"].as_str().unwrap_or("").to_string() - } else { - String::new() - } - } else { - String::new() - }; - - match calendar_service.refresh_event(&token, &password, &uid).await { - Ok(Some(refreshed_event)) => { - // If this is a recurring event, we need to regenerate all occurrences - let mut updated_events = (*events).clone(); - - // First, remove all existing occurrences of this event - for (_, day_events) in updated_events.iter_mut() { - day_events.retain(|e| e.uid != uid); - } - - // Then, if it's a recurring event, generate new occurrences - if refreshed_event.recurrence_rule.is_some() { - let new_occurrences = CalendarService::expand_recurring_events(vec![refreshed_event.clone()]); - - // Add all new occurrences to the appropriate dates - for occurrence in new_occurrences { - let date = occurrence.get_date(); - updated_events.entry(date) - .or_insert_with(Vec::new) - .push(occurrence); - } - } else { - // Non-recurring event, just add it to the appropriate date - let date = refreshed_event.get_date(); - updated_events.entry(date) - .or_insert_with(Vec::new) - .push(refreshed_event); - } - - 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(); - let loading = loading.clone(); - let error = error.clone(); - let auth_token = auth_token.clone(); - - use_effect_with((), move |_| { - if let Some(token) = auth_token { - let events = events.clone(); - let loading = loading.clone(); - let error = error.clone(); - - wasm_bindgen_futures::spawn_local(async move { - let calendar_service = CalendarService::new(); - - // Get password from stored credentials - let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { - if let Ok(credentials) = serde_json::from_str::(&credentials_str) { - credentials["password"].as_str().unwrap_or("").to_string() - } else { - String::new() - } - } else { - String::new() - }; - - match calendar_service.fetch_events_for_month(&token, &password, current_year, current_month).await { - Ok(calendar_events) => { - let grouped_events = CalendarService::group_events_by_date(calendar_events); - events.set(grouped_events); - loading.set(false); - } - Err(err) => { - error.set(Some(format!("Failed to load events: {}", err))); - loading.set(false); - } - } - }); - } else { - loading.set(false); - error.set(Some("No authentication token found".to_string())); - } - - || () - }); - } - - html! { -
- { - if *loading { - html! { -
-

{"Loading calendar events..."}

-
- } - } else if let Some(err) = (*error).clone() { - let dummy_callback = Callback::from(|_: CalendarEvent| {}); - html! { -
-

{format!("Error: {}", err)}

- -
- } - } else { - html! { - - } - } - } -
- } } \ No newline at end of file diff --git a/src/components/calendar_list_item.rs b/src/components/calendar_list_item.rs new file mode 100644 index 0000000..d5540b2 --- /dev/null +++ b/src/components/calendar_list_item.rs @@ -0,0 +1,75 @@ +use yew::prelude::*; +use web_sys::MouseEvent; +use crate::services::calendar_service::CalendarInfo; + +#[derive(Properties, PartialEq)] +pub struct CalendarListItemProps { + pub calendar: CalendarInfo, + pub color_picker_open: bool, + pub on_color_change: Callback<(String, String)>, // (calendar_path, color) + pub on_color_picker_toggle: Callback, // calendar_path + pub available_colors: Vec, + pub on_context_menu: Callback<(MouseEvent, String)>, // (event, calendar_path) +} + +#[function_component(CalendarListItem)] +pub fn calendar_list_item(props: &CalendarListItemProps) -> Html { + let on_color_click = { + let cal_path = props.calendar.path.clone(); + let on_color_picker_toggle = props.on_color_picker_toggle.clone(); + Callback::from(move |e: MouseEvent| { + e.stop_propagation(); + on_color_picker_toggle.emit(cal_path.clone()); + }) + }; + + let on_context_menu = { + let cal_path = props.calendar.path.clone(); + let on_context_menu = props.on_context_menu.clone(); + Callback::from(move |e: MouseEvent| { + e.prevent_default(); + on_context_menu.emit((e, cal_path.clone())); + }) + }; + + html! { +
  • + + { + if props.color_picker_open { + html! { +
    + { + props.available_colors.iter().map(|color| { + let color_str = color.clone(); + let cal_path = props.calendar.path.clone(); + let on_color_change = props.on_color_change.clone(); + + let on_color_select = Callback::from(move |_: MouseEvent| { + on_color_change.emit((cal_path.clone(), color_str.clone())); + }); + + let is_selected = props.calendar.color == *color; + let class_name = if is_selected { "color-option selected" } else { "color-option" }; + + html! { +
    +
    + } + }).collect::() + } +
    + } + } else { + html! {} + } + } +
    + {&props.calendar.display_name} +
  • + } +} \ No newline at end of file diff --git a/src/components/mod.rs b/src/components/mod.rs index 4a8f79d..7f97e2c 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -3,9 +3,15 @@ pub mod calendar; pub mod event_modal; pub mod create_calendar_modal; pub mod context_menu; +pub mod sidebar; +pub mod calendar_list_item; +pub mod route_handler; pub use login::Login; pub use calendar::Calendar; pub use event_modal::EventModal; pub use create_calendar_modal::CreateCalendarModal; -pub use context_menu::ContextMenu; \ No newline at end of file +pub use context_menu::ContextMenu; +pub use sidebar::Sidebar; +pub use calendar_list_item::CalendarListItem; +pub use route_handler::RouteHandler; \ No newline at end of file diff --git a/src/components/route_handler.rs b/src/components/route_handler.rs new file mode 100644 index 0000000..aed0edb --- /dev/null +++ b/src/components/route_handler.rs @@ -0,0 +1,226 @@ +use yew::prelude::*; +use yew_router::prelude::*; +use crate::components::Login; +use crate::services::calendar_service::UserInfo; + +#[derive(Clone, Routable, PartialEq)] +pub enum Route { + #[at("/")] + Home, + #[at("/login")] + Login, + #[at("/calendar")] + Calendar, +} + +#[derive(Properties, PartialEq)] +pub struct RouteHandlerProps { + pub auth_token: Option, + pub user_info: Option, + pub on_login: Callback, +} + +#[function_component(RouteHandler)] +pub fn route_handler(props: &RouteHandlerProps) -> Html { + let auth_token = props.auth_token.clone(); + let user_info = props.user_info.clone(); + let on_login = props.on_login.clone(); + + html! { + render={move |route| { + let auth_token = auth_token.clone(); + let user_info = user_info.clone(); + let on_login = on_login.clone(); + + match route { + Route::Home => { + if auth_token.is_some() { + html! { to={Route::Calendar}/> } + } else { + html! { to={Route::Login}/> } + } + } + Route::Login => { + if auth_token.is_some() { + html! { to={Route::Calendar}/> } + } else { + html! { } + } + } + Route::Calendar => { + if auth_token.is_some() { + html! { } + } else { + html! { to={Route::Login}/> } + } + } + } + }} /> + } +} + +#[derive(Properties, PartialEq)] +pub struct CalendarViewProps { + pub user_info: Option, +} + +use gloo_storage::{LocalStorage, Storage}; +use crate::services::{CalendarService, CalendarEvent}; +use crate::components::Calendar; +use std::collections::HashMap; +use chrono::{Local, NaiveDate, Datelike}; + +#[function_component(CalendarView)] +pub fn calendar_view(props: &CalendarViewProps) -> Html { + let events = use_state(|| HashMap::>::new()); + let loading = use_state(|| true); + let error = use_state(|| None::); + let refreshing_event = use_state(|| None::); + + let auth_token: Option = LocalStorage::get("auth_token").ok(); + + let today = Local::now().date_naive(); + let current_year = today.year(); + let current_month = today.month(); + + 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(); + + let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { + if let Ok(credentials) = serde_json::from_str::(&credentials_str) { + credentials["password"].as_str().unwrap_or("").to_string() + } else { + String::new() + } + } else { + String::new() + }; + + match calendar_service.refresh_event(&token, &password, &uid).await { + Ok(Some(refreshed_event)) => { + let mut updated_events = (*events).clone(); + + for (_, day_events) in updated_events.iter_mut() { + day_events.retain(|e| e.uid != uid); + } + + if refreshed_event.recurrence_rule.is_some() { + let new_occurrences = CalendarService::expand_recurring_events(vec![refreshed_event.clone()]); + + for occurrence in new_occurrences { + let date = occurrence.get_date(); + updated_events.entry(date) + .or_insert_with(Vec::new) + .push(occurrence); + } + } else { + let date = refreshed_event.get_date(); + updated_events.entry(date) + .or_insert_with(Vec::new) + .push(refreshed_event); + } + + events.set(updated_events); + } + Ok(None) => { + 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) => { + } + } + + refreshing_event.set(None); + }); + } + }) + }; + + { + let events = events.clone(); + let loading = loading.clone(); + let error = error.clone(); + let auth_token = auth_token.clone(); + + use_effect_with((), move |_| { + if let Some(token) = auth_token { + let events = events.clone(); + let loading = loading.clone(); + let error = error.clone(); + + wasm_bindgen_futures::spawn_local(async move { + let calendar_service = CalendarService::new(); + + let password = if let Ok(credentials_str) = LocalStorage::get::("caldav_credentials") { + if let Ok(credentials) = serde_json::from_str::(&credentials_str) { + credentials["password"].as_str().unwrap_or("").to_string() + } else { + String::new() + } + } else { + String::new() + }; + + match calendar_service.fetch_events_for_month(&token, &password, current_year, current_month).await { + Ok(calendar_events) => { + let grouped_events = CalendarService::group_events_by_date(calendar_events); + events.set(grouped_events); + loading.set(false); + } + Err(err) => { + error.set(Some(format!("Failed to load events: {}", err))); + loading.set(false); + } + } + }); + } else { + loading.set(false); + error.set(Some("No authentication token found".to_string())); + } + + || () + }); + } + + html! { +
    + { + if *loading { + html! { +
    +

    {"Loading calendar events..."}

    +
    + } + } else if let Some(err) = (*error).clone() { + let dummy_callback = Callback::from(|_: CalendarEvent| {}); + html! { +
    +

    {format!("Error: {}", err)}

    + +
    + } + } else { + html! { + + } + } + } +
    + } +} \ No newline at end of file diff --git a/src/components/sidebar.rs b/src/components/sidebar.rs new file mode 100644 index 0000000..8540d84 --- /dev/null +++ b/src/components/sidebar.rs @@ -0,0 +1,89 @@ +use yew::prelude::*; +use yew_router::prelude::*; +use crate::services::calendar_service::UserInfo; +use crate::components::CalendarListItem; + +#[derive(Clone, Routable, PartialEq)] +pub enum Route { + #[at("/")] + Home, + #[at("/login")] + Login, + #[at("/calendar")] + Calendar, +} + +#[derive(Properties, PartialEq)] +pub struct SidebarProps { + pub user_info: Option, + pub on_logout: Callback<()>, + pub on_create_calendar: Callback<()>, + pub color_picker_open: Option, + pub on_color_change: Callback<(String, String)>, + pub on_color_picker_toggle: Callback, + pub available_colors: Vec, + pub on_calendar_context_menu: Callback<(MouseEvent, String)>, +} + +#[function_component(Sidebar)] +pub fn sidebar(props: &SidebarProps) -> Html { + html! { + + } +} \ No newline at end of file diff --git a/src/services/mod.rs b/src/services/mod.rs index 28dc16e..b98a1b3 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1,3 +1,3 @@ pub mod calendar_service; -pub use calendar_service::{CalendarService, CalendarEvent, EventReminder, ReminderAction, UserInfo}; \ No newline at end of file +pub use calendar_service::{CalendarService, CalendarEvent, EventReminder, ReminderAction}; \ No newline at end of file