Fix item deletion issue on view panels

This commit is contained in:
Ian Keane 2025-11-23 12:34:52 -05:00
parent 857362b558
commit f10c1ddfff

View file

@ -58,6 +58,7 @@ pub struct App {
pub log_view_selection_level: LogViewSelection,
pub log_view_frames: Vec<WatsonFrame>,
pub log_view_content: Vec<String>,
pub log_view_frame_indices: Vec<usize>, // 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<String, Vec<&WatsonFrame>> = BTreeMap::new();
// Group frames by date, tracking their original indices
let mut by_date: BTreeMap<String, Vec<(usize, &WatsonFrame)>> = 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<Local> = 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<String, BTreeMap<String, Vec<&WatsonFrame>>> = BTreeMap::new();
// Group frames by date, then by project within each date, tracking indices
let mut by_date: BTreeMap<String, BTreeMap<String, Vec<(usize, &WatsonFrame)>>> = 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<Local> = 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(());
}
let frame_id = &self.log_view_frames[self.log_view_selected].id;
// 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[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(());
}
let frame_id = self.log_view_frames[self.log_view_selected].id.clone();
// 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[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;
}
}