From f10c1ddfff731a2601e5a155358ee90aa3c71177 Mon Sep 17 00:00:00 2001 From: Ian Keane Date: Sun, 23 Nov 2025 12:34:52 -0500 Subject: [PATCH] Fix item deletion issue on view panels --- src/app.rs | 58 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/app.rs b/src/app.rs index 38ecfd7..b593b53 100644 --- a/src/app.rs +++ b/src/app.rs @@ -58,6 +58,7 @@ pub struct App { pub log_view_selection_level: LogViewSelection, pub log_view_frames: Vec, pub log_view_content: Vec, + pub log_view_frame_indices: Vec, // Maps display order to frame index pub log_view_scroll: usize, pub log_view_selected: usize, pub help_scroll: usize, @@ -96,6 +97,7 @@ impl App { log_view_selection_level: LogViewSelection::Entry, log_view_frames: Vec::new(), log_view_content: Vec::new(), + log_view_frame_indices: Vec::new(), log_view_scroll: 0, log_view_selected: 0, help_scroll: 0, @@ -414,7 +416,7 @@ impl App { match self.log_view_selection_level { LogViewSelection::Entry => { // Move to next entry - if self.log_view_selected < self.log_view_frames.len().saturating_sub(1) { + if self.log_view_selected < self.log_view_frame_indices.len().saturating_sub(1) { self.log_view_selected += 1; } } @@ -501,7 +503,7 @@ impl App { } KeyCode::PageDown => { self.log_view_selected = (self.log_view_selected + 10) - .min(self.log_view_frames.len().saturating_sub(1)); + .min(self.log_view_frame_indices.len().saturating_sub(1)); } KeyCode::PageUp => { self.log_view_selected = self.log_view_selected.saturating_sub(10); @@ -529,23 +531,24 @@ impl App { use chrono::{DateTime, Local, Timelike}; use std::collections::BTreeMap; - // Group frames by date - let mut by_date: BTreeMap> = BTreeMap::new(); + // Group frames by date, tracking their original indices + let mut by_date: BTreeMap> = BTreeMap::new(); - for frame in &self.log_view_frames { + for (idx, frame) in self.log_view_frames.iter().enumerate() { if let Ok(start_dt) = DateTime::parse_from_rfc3339(&frame.start) { let local_dt: DateTime = start_dt.into(); let date_key = local_dt.format("%A %d %B %Y").to_string(); - by_date.entry(date_key).or_insert_with(Vec::new).push(frame); + by_date.entry(date_key).or_insert_with(Vec::new).push((idx, frame)); } } let mut lines = Vec::new(); + let mut frame_indices = Vec::new(); for (date, frames) in by_date.iter().rev() { lines.push(date.clone()); - for frame in frames { + for (idx, frame) in frames { if let (Ok(start_dt), Ok(stop_dt)) = ( DateTime::parse_from_rfc3339(&frame.start), DateTime::parse_from_rfc3339(&frame.stop), @@ -566,22 +569,24 @@ impl App { "\t{} to {} {}{}", start_time, stop_time, frame.project, tags_str )); + frame_indices.push(*idx); } } lines.push(String::new()); // Empty line between dates } self.log_view_content = lines; + self.log_view_frame_indices = frame_indices; } fn format_by_project(&mut self) { use chrono::{DateTime, Local, Timelike}; use std::collections::BTreeMap; - // Group frames by date, then by project within each date - let mut by_date: BTreeMap>> = BTreeMap::new(); + // Group frames by date, then by project within each date, tracking indices + let mut by_date: BTreeMap>> = BTreeMap::new(); - for frame in &self.log_view_frames { + for (idx, frame) in self.log_view_frames.iter().enumerate() { if let Ok(start_dt) = DateTime::parse_from_rfc3339(&frame.start) { let local_dt: DateTime = start_dt.into(); let date_key = local_dt.format("%A %d %B %Y").to_string(); @@ -590,11 +595,12 @@ impl App { .or_insert_with(BTreeMap::new) .entry(frame.project.clone()) .or_insert_with(Vec::new) - .push(frame); + .push((idx, frame)); } } let mut lines = Vec::new(); + let mut frame_indices = Vec::new(); for (date, projects) in by_date.iter().rev() { lines.push(date.clone()); @@ -602,7 +608,7 @@ impl App { for (project, frames) in projects.iter() { lines.push(format!(" {}", project)); // Project header with indent - for frame in frames { + for (idx, frame) in frames { if let (Ok(start_dt), Ok(stop_dt)) = ( DateTime::parse_from_rfc3339(&frame.start), DateTime::parse_from_rfc3339(&frame.stop), @@ -623,6 +629,7 @@ impl App { "\t\t{} to {}{}", start_time, stop_time, tags_str )); + frame_indices.push(*idx); } } } @@ -630,14 +637,22 @@ impl App { } self.log_view_content = lines; + self.log_view_frame_indices = frame_indices; } fn edit_selected_frame(&mut self) -> anyhow::Result<()> { - if self.log_view_frames.is_empty() || self.log_view_selected >= self.log_view_frames.len() { + // Check if selection is valid + if self.log_view_selected >= self.log_view_frame_indices.len() { + return Ok(()); + } + + // Get the actual frame index from the display index + let frame_idx = self.log_view_frame_indices[self.log_view_selected]; + if frame_idx >= self.log_view_frames.len() { return Ok(()); } - let frame_id = &self.log_view_frames[self.log_view_selected].id; + let frame_id = &self.log_view_frames[frame_idx].id; use crossterm::{ execute, @@ -668,11 +683,18 @@ impl App { } fn delete_selected_frame(&mut self) -> anyhow::Result<()> { - if self.log_view_frames.is_empty() || self.log_view_selected >= self.log_view_frames.len() { + // Check if selection is valid + if self.log_view_selected >= self.log_view_frame_indices.len() { + return Ok(()); + } + + // Get the actual frame index from the display index + let frame_idx = self.log_view_frame_indices[self.log_view_selected]; + if frame_idx >= self.log_view_frames.len() { return Ok(()); } - let frame_id = self.log_view_frames[self.log_view_selected].id.clone(); + let frame_id = self.log_view_frames[frame_idx].id.clone(); // Run watson remove with --force flag (no confirmation) let output = Command::new("watson") @@ -686,8 +708,8 @@ impl App { self.load_log_content()?; // Adjust selection if we deleted the last item - if self.log_view_selected >= self.log_view_frames.len() && !self.log_view_frames.is_empty() { - self.log_view_selected = self.log_view_frames.len() - 1; + if self.log_view_selected >= self.log_view_frame_indices.len() && !self.log_view_frame_indices.is_empty() { + self.log_view_selected = self.log_view_frame_indices.len() - 1; } }