use yew::prelude::*; use yew_router::prelude::*; use crate::components::{Login, ViewMode}; use crate::services::calendar_service::{UserInfo, CalendarEvent}; #[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, #[prop_or_default] pub on_event_context_menu: Option>, #[prop_or_default] pub on_calendar_context_menu: Option>, #[prop_or_default] pub view: ViewMode, #[prop_or_default] pub on_create_event_request: Option>, #[prop_or_default] pub on_event_update_request: Option>, #[prop_or_default] pub context_menus_open: bool, } #[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(); let on_event_context_menu = props.on_event_context_menu.clone(); let on_calendar_context_menu = props.on_calendar_context_menu.clone(); let view = props.view.clone(); let on_create_event_request = props.on_create_event_request.clone(); let on_event_update_request = props.on_event_update_request.clone(); let context_menus_open = props.context_menus_open; html! { render={move |route| { let auth_token = auth_token.clone(); let user_info = user_info.clone(); let on_login = on_login.clone(); let on_event_context_menu = on_event_context_menu.clone(); let on_calendar_context_menu = on_calendar_context_menu.clone(); let view = view.clone(); let on_create_event_request = on_create_event_request.clone(); let on_event_update_request = on_event_update_request.clone(); let context_menus_open = context_menus_open; 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, #[prop_or_default] pub on_event_context_menu: Option>, #[prop_or_default] pub on_calendar_context_menu: Option>, #[prop_or_default] pub view: ViewMode, #[prop_or_default] pub on_create_event_request: Option>, #[prop_or_default] pub on_event_update_request: Option>, #[prop_or_default] pub context_menus_open: bool, } use gloo_storage::{LocalStorage, Storage}; use crate::services::CalendarService; 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! { } } }
} }