Replace page reloads with dynamic calendar refresh functionality
All checks were successful
Build and Push Docker Image / docker (push) Successful in 29s
All checks were successful
Build and Push Docker Image / docker (push) Successful in 29s
- Add refresh_calendar_data function to replace window.location.reload() - Implement dynamic event re-fetching without full page refresh - Add last_updated timestamp to UserInfo to force component re-renders - Fix WASM compatibility by using js_sys::Date::now() instead of SystemTime - Remove debug logging from refresh operations - Maintain same user experience with improved performance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ web-sys = { version = "0.3", features = [
|
|||||||
"CssStyleDeclaration",
|
"CssStyleDeclaration",
|
||||||
] }
|
] }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
|
js-sys = "0.3"
|
||||||
|
|
||||||
# HTTP client for CalDAV requests
|
# HTTP client for CalDAV requests
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
|
|||||||
@@ -155,6 +155,108 @@ pub fn App() -> Html {
|
|||||||
|
|
||||||
let available_colors = use_state(|| get_theme_event_colors());
|
let available_colors = use_state(|| get_theme_event_colors());
|
||||||
|
|
||||||
|
// Function to refresh calendar data without full page reload
|
||||||
|
let refresh_calendar_data = {
|
||||||
|
let user_info = user_info.clone();
|
||||||
|
let auth_token = auth_token.clone();
|
||||||
|
let external_calendars = external_calendars.clone();
|
||||||
|
let external_calendar_events = external_calendar_events.clone();
|
||||||
|
|
||||||
|
Callback::from(move |_| {
|
||||||
|
let user_info = user_info.clone();
|
||||||
|
let auth_token = auth_token.clone();
|
||||||
|
let external_calendars = external_calendars.clone();
|
||||||
|
let external_calendar_events = external_calendar_events.clone();
|
||||||
|
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
// Refresh main calendar data if authenticated
|
||||||
|
if let Some(token) = (*auth_token).clone() {
|
||||||
|
let calendar_service = CalendarService::new();
|
||||||
|
|
||||||
|
let password = if let Ok(credentials_str) =
|
||||||
|
LocalStorage::get::<String>("caldav_credentials")
|
||||||
|
{
|
||||||
|
if let Ok(credentials) =
|
||||||
|
serde_json::from_str::<serde_json::Value>(&credentials_str)
|
||||||
|
{
|
||||||
|
credentials["password"].as_str().unwrap_or("").to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
if !password.is_empty() {
|
||||||
|
match calendar_service.fetch_user_info(&token, &password).await {
|
||||||
|
Ok(mut info) => {
|
||||||
|
// Apply saved colors
|
||||||
|
if let Ok(saved_colors_json) =
|
||||||
|
LocalStorage::get::<String>("calendar_colors")
|
||||||
|
{
|
||||||
|
if let Ok(saved_info) =
|
||||||
|
serde_json::from_str::<UserInfo>(&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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add timestamp to force re-render
|
||||||
|
info.last_updated = (js_sys::Date::now() / 1000.0) as u64;
|
||||||
|
user_info.set(Some(info));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
web_sys::console::log_1(
|
||||||
|
&format!("Failed to refresh main calendar data: {}", err).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh external calendars data
|
||||||
|
match CalendarService::get_external_calendars().await {
|
||||||
|
Ok(calendars) => {
|
||||||
|
external_calendars.set(calendars.clone());
|
||||||
|
|
||||||
|
// Load events for visible external calendars
|
||||||
|
let mut all_external_events = Vec::new();
|
||||||
|
for calendar in calendars {
|
||||||
|
if calendar.is_visible {
|
||||||
|
match CalendarService::fetch_external_calendar_events(calendar.id).await {
|
||||||
|
Ok(mut events) => {
|
||||||
|
// Set calendar_path for color matching
|
||||||
|
for event in &mut events {
|
||||||
|
event.calendar_path = Some(format!("external_{}", calendar.id));
|
||||||
|
}
|
||||||
|
all_external_events.extend(events);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
web_sys::console::log_1(
|
||||||
|
&format!("Failed to fetch events for external calendar {}: {}", calendar.id, e).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
external_calendar_events.set(all_external_events);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
web_sys::console::log_1(
|
||||||
|
&format!("Failed to refresh external calendars: {}", e).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
let on_login = {
|
let on_login = {
|
||||||
let auth_token = auth_token.clone();
|
let auth_token = auth_token.clone();
|
||||||
Callback::from(move |token: String| {
|
Callback::from(move |token: String| {
|
||||||
@@ -531,6 +633,7 @@ pub fn App() -> Html {
|
|||||||
let on_event_create = {
|
let on_event_create = {
|
||||||
let create_event_modal_open = create_event_modal_open.clone();
|
let create_event_modal_open = create_event_modal_open.clone();
|
||||||
let auth_token = auth_token.clone();
|
let auth_token = auth_token.clone();
|
||||||
|
let refresh_calendar_data = refresh_calendar_data.clone();
|
||||||
Callback::from(move |event_data: EventCreationData| {
|
Callback::from(move |event_data: EventCreationData| {
|
||||||
// Check if this is an update operation (has original_uid) or a create operation
|
// Check if this is an update operation (has original_uid) or a create operation
|
||||||
if let Some(original_uid) = event_data.original_uid.clone() {
|
if let Some(original_uid) = event_data.original_uid.clone() {
|
||||||
@@ -541,6 +644,7 @@ pub fn App() -> Html {
|
|||||||
// Handle the update operation using the existing backend update logic
|
// Handle the update operation using the existing backend update logic
|
||||||
if let Some(token) = (*auth_token).clone() {
|
if let Some(token) = (*auth_token).clone() {
|
||||||
let event_data_for_update = event_data.clone();
|
let event_data_for_update = event_data.clone();
|
||||||
|
let refresh_callback = refresh_calendar_data.clone();
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
let calendar_service = CalendarService::new();
|
let calendar_service = CalendarService::new();
|
||||||
|
|
||||||
@@ -641,10 +745,8 @@ pub fn App() -> Html {
|
|||||||
match update_result {
|
match update_result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
web_sys::console::log_1(&"Event updated successfully via modal".into());
|
web_sys::console::log_1(&"Event updated successfully via modal".into());
|
||||||
// Trigger a page reload to refresh events from all calendars
|
// Refresh calendar data without page reload
|
||||||
if let Some(window) = web_sys::window() {
|
refresh_callback.emit(());
|
||||||
let _ = window.location().reload();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
web_sys::console::error_1(
|
web_sys::console::error_1(
|
||||||
@@ -680,6 +782,7 @@ pub fn App() -> Html {
|
|||||||
create_event_modal_open.set(false);
|
create_event_modal_open.set(false);
|
||||||
|
|
||||||
if let Some(_token) = (*auth_token).clone() {
|
if let Some(_token) = (*auth_token).clone() {
|
||||||
|
let refresh_callback = refresh_calendar_data.clone();
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
let _calendar_service = CalendarService::new();
|
let _calendar_service = CalendarService::new();
|
||||||
|
|
||||||
@@ -726,9 +829,8 @@ pub fn App() -> Html {
|
|||||||
match create_result {
|
match create_result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
web_sys::console::log_1(&"Event created successfully".into());
|
web_sys::console::log_1(&"Event created successfully".into());
|
||||||
// Trigger a page reload to refresh events from all calendars
|
// Refresh calendar data without page reload
|
||||||
// TODO: This could be improved to do a more targeted refresh
|
refresh_callback.emit(());
|
||||||
web_sys::window().unwrap().location().reload().unwrap();
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
web_sys::console::error_1(
|
web_sys::console::error_1(
|
||||||
@@ -747,6 +849,7 @@ pub fn App() -> Html {
|
|||||||
|
|
||||||
let on_event_update = {
|
let on_event_update = {
|
||||||
let auth_token = auth_token.clone();
|
let auth_token = auth_token.clone();
|
||||||
|
let refresh_calendar_data = refresh_calendar_data.clone();
|
||||||
Callback::from(
|
Callback::from(
|
||||||
move |(
|
move |(
|
||||||
original_event,
|
original_event,
|
||||||
@@ -781,6 +884,7 @@ pub fn App() -> Html {
|
|||||||
if let Some(token) = (*auth_token).clone() {
|
if let Some(token) = (*auth_token).clone() {
|
||||||
let original_event = original_event.clone();
|
let original_event = original_event.clone();
|
||||||
let backend_uid = backend_uid.clone();
|
let backend_uid = backend_uid.clone();
|
||||||
|
let refresh_callback = refresh_calendar_data.clone();
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
let calendar_service = CalendarService::new();
|
let calendar_service = CalendarService::new();
|
||||||
|
|
||||||
@@ -965,14 +1069,8 @@ pub fn App() -> Html {
|
|||||||
match result {
|
match result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
web_sys::console::log_1(&"Event updated successfully".into());
|
web_sys::console::log_1(&"Event updated successfully".into());
|
||||||
// Add small delay before reload to let any pending requests complete
|
// Refresh calendar data without page reload
|
||||||
wasm_bindgen_futures::spawn_local(async {
|
refresh_callback.emit(());
|
||||||
gloo_timers::future::sleep(std::time::Duration::from_millis(
|
|
||||||
100,
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
web_sys::window().unwrap().location().reload().unwrap();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
web_sys::console::error_1(
|
web_sys::console::error_1(
|
||||||
@@ -1392,10 +1490,10 @@ pub fn App() -> Html {
|
|||||||
let auth_token = auth_token.clone();
|
let auth_token = auth_token.clone();
|
||||||
let event_context_menu_event = event_context_menu_event.clone();
|
let event_context_menu_event = event_context_menu_event.clone();
|
||||||
let event_context_menu_open = event_context_menu_open.clone();
|
let event_context_menu_open = event_context_menu_open.clone();
|
||||||
let refresh_calendars = refresh_calendars.clone();
|
let refresh_calendar_data = refresh_calendar_data.clone();
|
||||||
move |delete_action: DeleteAction| {
|
move |delete_action: DeleteAction| {
|
||||||
if let (Some(token), Some(event)) = ((*auth_token).clone(), (*event_context_menu_event).clone()) {
|
if let (Some(token), Some(event)) = ((*auth_token).clone(), (*event_context_menu_event).clone()) {
|
||||||
let _refresh_calendars = refresh_calendars.clone();
|
let refresh_calendar_data = refresh_calendar_data.clone();
|
||||||
let event_context_menu_open = event_context_menu_open.clone();
|
let event_context_menu_open = event_context_menu_open.clone();
|
||||||
|
|
||||||
// Log the delete action for now - we'll implement different behaviors later
|
// Log the delete action for now - we'll implement different behaviors later
|
||||||
@@ -1405,6 +1503,7 @@ pub fn App() -> Html {
|
|||||||
DeleteAction::DeleteSeries => web_sys::console::log_1(&"Delete entire series".into()),
|
DeleteAction::DeleteSeries => web_sys::console::log_1(&"Delete entire series".into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let refresh_callback = refresh_calendar_data.clone();
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
let calendar_service = CalendarService::new();
|
let calendar_service = CalendarService::new();
|
||||||
|
|
||||||
@@ -1452,8 +1551,8 @@ pub fn App() -> Html {
|
|||||||
|
|
||||||
// Close the context menu
|
// Close the context menu
|
||||||
event_context_menu_open.set(false);
|
event_context_menu_open.set(false);
|
||||||
// Force a page reload to refresh the calendar events
|
// Refresh calendar data without page reload
|
||||||
web_sys::window().unwrap().location().reload().unwrap();
|
refresh_callback.emit(());
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
web_sys::console::log_1(&format!("Failed to delete event: {}", err).into());
|
web_sys::console::log_1(&format!("Failed to delete event: {}", err).into());
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ pub struct UserInfo {
|
|||||||
pub username: String,
|
pub username: String,
|
||||||
pub server_url: String,
|
pub server_url: String,
|
||||||
pub calendars: Vec<CalendarInfo>,
|
pub calendars: Vec<CalendarInfo>,
|
||||||
|
#[serde(default = "default_timestamp")]
|
||||||
|
pub last_updated: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_timestamp() -> u64 {
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
|||||||
Reference in New Issue
Block a user