Fix item deletion issue on view panels
This commit is contained in:
parent
857362b558
commit
f10c1ddfff
1 changed files with 40 additions and 18 deletions
58
src/app.rs
58
src/app.rs
|
|
@ -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(());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue