Skip to content

Commit

Permalink
feat(interactive): Add possibility to change snapshot description (#1137
Browse files Browse the repository at this point in the history
)

closes #1134
  • Loading branch information
aawsome authored Apr 25, 2024
1 parent 5952682 commit a34000f
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 52 deletions.
19 changes: 12 additions & 7 deletions src/commands/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod snapshots;
mod tree;
mod widgets;

use crossterm::event::{KeyEvent, KeyModifiers};
use progress::TuiProgressBars;
use snapshots::Snapshots;

Expand Down Expand Up @@ -74,14 +75,18 @@ fn run_app<B: Backend, P: ProgressBars, S: IndexedFull>(
let event = event::read()?;
use KeyCode::*;

match event {
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
Char('q') | Esc => return Ok(()),
_ => {}
},
_ => {}
if let Event::Key(KeyEvent {
code: Char('c'),
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
..
}) = event
{
return Ok(());
}
if app.snapshots.input(event)? {
return Ok(());
}
app.snapshots.input(event)?;
}
}

Expand Down
15 changes: 12 additions & 3 deletions src/commands/tui/ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ pub(crate) struct Snapshot<'a, P, S> {
tree: Tree,
}

pub enum SnapshotResult {
Exit,
Return,
None,
}

impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
pub fn new(repo: &'a Repository<P, S>, snapshot: SnapshotFile) -> Result<Self> {
let header = ["Name", "Size", "Mode", "User", "Group", "Time"]
Expand Down Expand Up @@ -172,17 +178,20 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
self.update_table();
}

pub fn input(&mut self, event: Event) -> Result<bool> {
pub fn input(&mut self, event: Event) -> Result<SnapshotResult> {
use KeyCode::*;
match &mut self.current_screen {
CurrentScreen::Snapshot => match event {
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
Enter | Right => self.enter()?,
Backspace | Left => {
if self.goback() {
return Ok(true);
return Ok(SnapshotResult::Return);
}
}
Esc | Char('q') => {
return Ok(SnapshotResult::Exit);
}
Char('?') => {
self.current_screen =
CurrentScreen::ShowHelp(popup_text("help", HELP_TEXT.into()));
Expand Down Expand Up @@ -217,7 +226,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
}
}
}
Ok(false)
Ok(SnapshotResult::None)
}

pub fn draw(&mut self, area: Rect, f: &mut Frame<'_>) {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/tui/restore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Restore<'a, P, S> {
pub fn new(repo: &'a Repository<P, S>, node: Node, source: String) -> Self {
let opts = RestoreOptions::default();
let title = format!("restore {} to:", source);
let popup = popup_input(title, "enter restore destination", "");
let popup = popup_input(title, "enter restore destination", "", 1);
Self {
current_screen: CurrentScreen::GetDestination(popup),
node,
Expand Down
88 changes: 57 additions & 31 deletions src/commands/tui/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
commands::{
snapshots::{fill_table, snap_to_table},
tui::{
ls::Snapshot,
ls::{Snapshot, SnapshotResult},
tree::{Tree, TreeIterItem, TreeNode},
widgets::{
popup_input, popup_prompt, popup_table, popup_text, Draw, PopUpInput, PopUpPrompt,
Expand All @@ -32,6 +32,7 @@ enum CurrentScreen<'a, P, S> {
ShowHelp(PopUpText),
SnapshotDetails(PopUpTable),
EnterLabel(PopUpInput),
EnterDescription(PopUpInput),
EnterAddTags(PopUpInput),
EnterSetTags(PopUpInput),
EnterRemoveTags(PopUpInput),
Expand Down Expand Up @@ -90,6 +91,8 @@ Commands applied to marked snapshot(s) (selected if none marked):
l : set label for snapshot(s)
Ctrl-l : remove label for snapshot(s)
d : set description for snapshot(s)
Ctrl-d : remove description for snapshot(s)
t : add tag(s) for snapshot(s)
Ctrl-t : remove all tags for snapshot(s)
s : set tag(s) for snapshot(s)
Expand Down Expand Up @@ -504,46 +507,37 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
self.update_table();
}

pub fn get_label(&mut self) -> String {
pub fn get_snap_entity(&mut self, f: impl Fn(&SnapshotFile) -> String) -> String {
let has_mark = self.has_mark();

if !has_mark {
self.toggle_mark();
}

let label = self
let entity = self
.snapshots
.iter()
.zip(self.snaps_status.iter())
.filter_map(|(snap, status)| status.marked.then_some(snap.label.clone()))
.reduce(|label, l| if label == l { l } else { String::new() })
.filter_map(|(snap, status)| status.marked.then_some(f(snap)))
.reduce(|entity, e| if entity == e { e } else { String::new() })
.unwrap_or_default();

if !has_mark {
self.toggle_mark();
}
label
entity
}

pub fn get_tags(&mut self) -> String {
let has_mark = self.has_mark();

if !has_mark {
self.toggle_mark();
}
pub fn get_label(&mut self) -> String {
self.get_snap_entity(|snap| snap.label.clone())
}

let label = self
.snapshots
.iter()
.zip(self.snaps_status.iter())
.filter_map(|(snap, status)| status.marked.then_some(snap.tags.formatln()))
.reduce(|tags, t| if tags == t { t } else { String::new() })
.unwrap_or_default();
pub fn get_tags(&mut self) -> String {
self.get_snap_entity(|snap| snap.tags.formatln())
}

if !has_mark {
self.toggle_mark();
}
label
pub fn get_description(&mut self) -> String {
self.get_snap_entity(|snap| snap.description.clone().unwrap_or_default())
}

pub fn set_label(&mut self, label: String) {
Expand All @@ -560,6 +554,21 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
self.set_label(String::new());
}

pub fn set_description(&mut self, desc: String) {
let desc = if desc.is_empty() { None } else { Some(desc) };
self.process_marked_snaps(|snap| {
if snap.description == desc {
return false;
}
snap.description = desc.clone();
true
});
}

pub fn clear_description(&mut self) {
self.set_description(String::new());
}

pub fn add_tags(&mut self, tags: String) {
let tags = vec![StringList::from_str(&tags).unwrap()];
self.process_marked_snaps(|snap| snap.add_tags(tags.clone()));
Expand Down Expand Up @@ -593,6 +602,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
pub fn apply_input(&mut self, input: String) {
match self.current_screen {
CurrentScreen::EnterLabel(_) => self.set_label(input),
CurrentScreen::EnterDescription(_) => self.set_description(input),
CurrentScreen::EnterAddTags(_) => self.add_tags(input),
CurrentScreen::EnterSetTags(_) => self.set_tags(input),
CurrentScreen::EnterRemoveTags(_) => self.remove_tags(input),
Expand Down Expand Up @@ -639,7 +649,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
Ok(())
}

pub fn input(&mut self, event: Event) -> Result<()> {
pub fn input(&mut self, event: Event) -> Result<bool> {
use KeyCode::*;
match &mut self.current_screen {
CurrentScreen::Snapshots => {
Expand All @@ -650,12 +660,14 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
Char('x') => self.clear_marks(),
Char('f') => self.clear_filter(),
Char('l') => self.clear_label(),
Char('d') => self.clear_description(),
Char('t') => self.clear_tags(),
Char('p') => self.clear_delete_protection(),
_ => {}
}
} else {
match key.code {
Esc | Char('q') => return Ok(true),
F(5) => self.reread()?,
Enter => {
if let Some(dir) = self.dir()? {
Expand Down Expand Up @@ -692,25 +704,37 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
"set label",
"enter label",
&self.get_label(),
1,
));
}
Char('d') => {
self.current_screen =
CurrentScreen::EnterDescription(popup_input(
"set description (Ctrl-s to confirm)",
"enter description",
&self.get_description(),
5,
));
}
Char('t') => {
self.current_screen = CurrentScreen::EnterAddTags(popup_input(
"add tags",
"enter tags",
"",
1,
));
}
Char('s') => {
self.current_screen = CurrentScreen::EnterSetTags(popup_input(
"set tags",
"enter tags",
&self.get_tags(),
1,
));
}
Char('r') => {
self.current_screen = CurrentScreen::EnterRemoveTags(
popup_input("remove tags", "enter tags", ""),
popup_input("remove tags", "enter tags", "", 1),
);
}
// TODO: Allow to enter delete protection option
Expand Down Expand Up @@ -744,6 +768,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
_ => {}
},
CurrentScreen::EnterLabel(prompt)
| CurrentScreen::EnterDescription(prompt)
| CurrentScreen::EnterAddTags(prompt)
| CurrentScreen::EnterSetTags(prompt)
| CurrentScreen::EnterRemoveTags(prompt) => match prompt.input(event) {
Expand All @@ -762,13 +787,13 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
PromptResult::Cancel => self.current_screen = CurrentScreen::Snapshots,
PromptResult::None => {}
},
CurrentScreen::Dir(dir) => {
if dir.input(event)? {
self.current_screen = CurrentScreen::Snapshots;
}
}
CurrentScreen::Dir(dir) => match dir.input(event)? {
SnapshotResult::Exit => return Ok(true),
SnapshotResult::Return => self.current_screen = CurrentScreen::Snapshots,
SnapshotResult::None => {}
},
}
Ok(())
Ok(false)
}

pub fn draw(&mut self, area: Rect, f: &mut Frame<'_>) {
Expand All @@ -795,6 +820,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
CurrentScreen::SnapshotDetails(popup) => popup.draw(area, f),
CurrentScreen::ShowHelp(popup) => popup.draw(area, f),
CurrentScreen::EnterLabel(popup)
| CurrentScreen::EnterDescription(popup)
| CurrentScreen::EnterAddTags(popup)
| CurrentScreen::EnterSetTags(popup)
| CurrentScreen::EnterRemoveTags(popup) => popup.draw(area, f),
Expand Down
9 changes: 7 additions & 2 deletions src/commands/tui/widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,14 @@ pub trait Draw {

// the widgets we are using and convenience builders
pub type PopUpInput = PopUp<WithBlock<TextInput>>;
pub fn popup_input(title: impl Into<Title<'static>>, text: &str, initial: &str) -> PopUpInput {
pub fn popup_input(
title: impl Into<Title<'static>>,
text: &str,
initial: &str,
lines: u16,
) -> PopUpInput {
PopUp(WithBlock::new(
TextInput::new(text, initial),
TextInput::new(text, initial, lines),
Block::bordered().title(title),
))
}
Expand Down
22 changes: 14 additions & 8 deletions src/commands/tui/widgets/text_input.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use super::*;

use crossterm::event::KeyModifiers;
use tui_textarea::TextArea;

pub struct TextInput {
textarea: TextArea<'static>,
lines: u16,
}

pub enum TextInputResult {
Expand All @@ -13,18 +15,18 @@ pub enum TextInputResult {
}

impl TextInput {
pub fn new(text: &str, initial: &str) -> Self {
pub fn new(text: &str, initial: &str, lines: u16) -> Self {
let mut textarea = TextArea::default();
textarea.set_style(Style::default());
textarea.set_placeholder_text(text);
_ = textarea.insert_str(initial);
Self { textarea }
Self { textarea, lines }
}
}

impl SizedWidget for TextInput {
fn height(&self) -> Option<u16> {
Some(1)
Some(self.lines)
}
}

Expand All @@ -38,14 +40,18 @@ impl ProcessEvent for TextInput {
type Result = TextInputResult;
fn input(&mut self, event: Event) -> TextInputResult {
if let Event::Key(key) = event {
if key.kind != KeyEventKind::Press {
return TextInputResult::None;
}
use KeyCode::*;
match key {
KeyEvent { code: Esc, .. } => return TextInputResult::Cancel,
KeyEvent { code: Enter, .. } => {
return TextInputResult::Input(self.textarea.lines()[0].clone());
KeyEvent { code: Enter, .. } if self.lines == 1 => {
return TextInputResult::Input(self.textarea.lines().join("\n"));
}
KeyEvent {
code: Char('s'),
modifiers: KeyModifiers::CONTROL,
..
} => {
return TextInputResult::Input(self.textarea.lines().join("\n"));
}
key => {
_ = self.textarea.input(key);
Expand Down

0 comments on commit a34000f

Please sign in to comment.