Add desktop search and entry mutation workflows
This commit is contained in:
@@ -21,12 +21,16 @@ pub enum UiAction {
|
||||
CopyEditedField,
|
||||
Paste,
|
||||
Find,
|
||||
SearchContents,
|
||||
CommandPalette,
|
||||
TogglePaneFocus,
|
||||
Refresh,
|
||||
ReloadEntry,
|
||||
EditEntry,
|
||||
GeneratePassword,
|
||||
MoveEntry,
|
||||
CopyEntry,
|
||||
DeleteEntry,
|
||||
ToggleReveal,
|
||||
Lock,
|
||||
Minimize,
|
||||
@@ -53,12 +57,16 @@ impl UiAction {
|
||||
Self::CopyEditedField => "copy-edited-field",
|
||||
Self::Paste => "paste",
|
||||
Self::Find => "find",
|
||||
Self::SearchContents => "search-contents",
|
||||
Self::CommandPalette => "command-palette",
|
||||
Self::TogglePaneFocus => "toggle-pane-focus",
|
||||
Self::Refresh => "refresh",
|
||||
Self::ReloadEntry => "reload-entry",
|
||||
Self::EditEntry => "edit-entry",
|
||||
Self::GeneratePassword => "generate-password",
|
||||
Self::MoveEntry => "move-entry",
|
||||
Self::CopyEntry => "copy-entry",
|
||||
Self::DeleteEntry => "delete-entry",
|
||||
Self::ToggleReveal => "toggle-reveal",
|
||||
Self::Lock => "lock",
|
||||
Self::Minimize => "minimize",
|
||||
@@ -120,6 +128,7 @@ pub struct ActionContext {
|
||||
pub focused_sensitive: bool,
|
||||
pub focused_generatable: bool,
|
||||
pub entry_path: bool,
|
||||
pub selected_object: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -173,6 +182,12 @@ pub const ACTIONS: &[ActionSpec] = &[
|
||||
),
|
||||
spec(UiAction::Paste, MenuGroup::Edit, "Paste", Some("⌘V")),
|
||||
spec(UiAction::Find, MenuGroup::Edit, "Find", Some("⌘F")),
|
||||
spec(
|
||||
UiAction::SearchContents,
|
||||
MenuGroup::Edit,
|
||||
"Search Decrypted Contents…",
|
||||
Some("⇧⌘F"),
|
||||
),
|
||||
spec(
|
||||
UiAction::CommandPalette,
|
||||
MenuGroup::View,
|
||||
@@ -199,6 +214,14 @@ pub const ACTIONS: &[ActionSpec] = &[
|
||||
"Generate Password…",
|
||||
None,
|
||||
),
|
||||
spec(
|
||||
UiAction::MoveEntry,
|
||||
MenuGroup::Entry,
|
||||
"Move or Rename…",
|
||||
None,
|
||||
),
|
||||
spec(UiAction::CopyEntry, MenuGroup::Entry, "Copy Entry…", None),
|
||||
spec(UiAction::DeleteEntry, MenuGroup::Entry, "Delete…", None),
|
||||
spec(
|
||||
UiAction::ToggleReveal,
|
||||
MenuGroup::Entry,
|
||||
@@ -289,7 +312,13 @@ pub fn enabled(action: UiAction, context: ActionContext) -> bool {
|
||||
}
|
||||
UiAction::CloseWindow | UiAction::Quit => !context.switching_vault,
|
||||
UiAction::Minimize => true,
|
||||
UiAction::Undo | UiAction::Redo | UiAction::Cut | UiAction::Paste | UiAction::Find => false,
|
||||
UiAction::Undo | UiAction::Redo | UiAction::Cut | UiAction::Paste => false,
|
||||
UiAction::Find | UiAction::SearchContents => {
|
||||
context.storage_ready
|
||||
&& !context.saving
|
||||
&& !context.switching_vault
|
||||
&& !context.modal_open
|
||||
}
|
||||
UiAction::CopyField => {
|
||||
context.unlocked
|
||||
&& context.document_open
|
||||
@@ -320,6 +349,13 @@ pub fn enabled(action: UiAction, context: ActionContext) -> bool {
|
||||
&& !context.switching_vault
|
||||
&& !context.modal_open
|
||||
}
|
||||
UiAction::MoveEntry | UiAction::CopyEntry | UiAction::DeleteEntry => {
|
||||
context.storage_ready
|
||||
&& context.selected_object
|
||||
&& !context.saving
|
||||
&& !context.switching_vault
|
||||
&& !context.modal_open
|
||||
}
|
||||
UiAction::ToggleReveal => {
|
||||
context.unlocked
|
||||
&& context.document_open
|
||||
@@ -367,9 +403,17 @@ pub fn disabled_reason(action: UiAction, context: ActionContext) -> Option<&'sta
|
||||
UiAction::Save if context.saving => "A save is already running",
|
||||
UiAction::Save => "Wait for vault validation",
|
||||
UiAction::CloseWindow | UiAction::Quit => "Wait for vault validation",
|
||||
UiAction::Undo | UiAction::Redo | UiAction::Cut | UiAction::Paste | UiAction::Find => {
|
||||
UiAction::Undo | UiAction::Redo | UiAction::Cut | UiAction::Paste => {
|
||||
"Use the focused native text field"
|
||||
}
|
||||
UiAction::Find | UiAction::SearchContents if !context.storage_ready => {
|
||||
"Shared configuration is unavailable"
|
||||
}
|
||||
UiAction::Find | UiAction::SearchContents if context.saving => "Wait for the active save",
|
||||
UiAction::Find | UiAction::SearchContents if context.switching_vault => {
|
||||
"Wait for vault validation"
|
||||
}
|
||||
UiAction::Find | UiAction::SearchContents => "Close the current screen first",
|
||||
UiAction::CopyField | UiAction::CopyEditedField if !context.unlocked => {
|
||||
"Unlock an entry first"
|
||||
}
|
||||
@@ -402,6 +446,27 @@ pub fn disabled_reason(action: UiAction, context: ActionContext) -> Option<&'sta
|
||||
}
|
||||
UiAction::GeneratePassword if context.switching_vault => "Wait for vault validation",
|
||||
UiAction::GeneratePassword => "Close the current screen first",
|
||||
UiAction::MoveEntry | UiAction::CopyEntry | UiAction::DeleteEntry
|
||||
if !context.storage_ready =>
|
||||
{
|
||||
"Shared configuration is unavailable"
|
||||
}
|
||||
UiAction::MoveEntry | UiAction::CopyEntry | UiAction::DeleteEntry
|
||||
if !context.selected_object =>
|
||||
{
|
||||
"Select an entry or folder first"
|
||||
}
|
||||
UiAction::MoveEntry | UiAction::CopyEntry | UiAction::DeleteEntry if context.saving => {
|
||||
"Wait for the active save"
|
||||
}
|
||||
UiAction::MoveEntry | UiAction::CopyEntry | UiAction::DeleteEntry
|
||||
if context.switching_vault =>
|
||||
{
|
||||
"Wait for vault validation"
|
||||
}
|
||||
UiAction::MoveEntry | UiAction::CopyEntry | UiAction::DeleteEntry => {
|
||||
"Close the current screen first"
|
||||
}
|
||||
UiAction::ToggleReveal if !context.unlocked => "Unlock an entry first",
|
||||
UiAction::ToggleReveal if !context.document_open => "Open an entry first",
|
||||
UiAction::ToggleReveal if !context.focused_sensitive => "Select a sensitive field first",
|
||||
@@ -436,12 +501,16 @@ pub const fn aliases(action: UiAction) -> &'static [&'static str] {
|
||||
UiAction::CopyField | UiAction::CopyEditedField => &["copy value", "clipboard"],
|
||||
UiAction::Paste => &["insert clipboard"],
|
||||
UiAction::Find => &["search text"],
|
||||
UiAction::SearchContents => &["grep", "decrypted search", "search passwords"],
|
||||
UiAction::CommandPalette => &["commands", "actions", "search commands"],
|
||||
UiAction::TogglePaneFocus => &["next pane", "switch pane", "focus"],
|
||||
UiAction::Refresh => &["reload vault", "refresh tree"],
|
||||
UiAction::ReloadEntry => &["revert entry", "refresh entry"],
|
||||
UiAction::EditEntry => &["modify entry"],
|
||||
UiAction::GeneratePassword => &["random password", "replace password", "generate"],
|
||||
UiAction::MoveEntry => &["rename", "mv", "move folder"],
|
||||
UiAction::CopyEntry => &["duplicate entry", "copy folder", "pass cp"],
|
||||
UiAction::DeleteEntry => &["remove", "rm", "delete folder"],
|
||||
UiAction::ToggleReveal => &["show password", "hide password", "reveal field"],
|
||||
UiAction::Lock => &["secure", "log out", "relock"],
|
||||
UiAction::Minimize => &["hide window"],
|
||||
@@ -471,6 +540,7 @@ pub fn shortcut_action(key: &keyboard::Key, modifiers: keyboard::Modifiers) -> O
|
||||
keyboard::Key::Character("x" | "X") => Some(UiAction::Cut),
|
||||
keyboard::Key::Character("c" | "C") => Some(UiAction::CopyField),
|
||||
keyboard::Key::Character("v" | "V") => Some(UiAction::Paste),
|
||||
keyboard::Key::Character("f" | "F") if modifiers.shift() => Some(UiAction::SearchContents),
|
||||
keyboard::Key::Character("f" | "F") => Some(UiAction::Find),
|
||||
keyboard::Key::Character("k" | "K") => Some(UiAction::CommandPalette),
|
||||
keyboard::Key::Character("r" | "R") => Some(UiAction::Refresh),
|
||||
@@ -501,6 +571,7 @@ mod tests {
|
||||
focused_sensitive: true,
|
||||
focused_generatable: true,
|
||||
entry_path: true,
|
||||
selected_object: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,6 +625,18 @@ mod tests {
|
||||
] {
|
||||
assert!(!enabled(action, locked));
|
||||
}
|
||||
for action in [
|
||||
UiAction::Find,
|
||||
UiAction::SearchContents,
|
||||
UiAction::MoveEntry,
|
||||
UiAction::CopyEntry,
|
||||
UiAction::DeleteEntry,
|
||||
] {
|
||||
assert!(
|
||||
enabled(action, locked),
|
||||
"{action:?} can initiate authentication"
|
||||
);
|
||||
}
|
||||
assert!(!enabled(
|
||||
UiAction::Refresh,
|
||||
ActionContext {
|
||||
@@ -591,6 +674,11 @@ mod tests {
|
||||
UiAction::EditEntry,
|
||||
UiAction::GeneratePassword,
|
||||
UiAction::ToggleReveal,
|
||||
UiAction::Find,
|
||||
UiAction::SearchContents,
|
||||
UiAction::MoveEntry,
|
||||
UiAction::CopyEntry,
|
||||
UiAction::DeleteEntry,
|
||||
] {
|
||||
assert!(!enabled(action, switching), "{action:?}");
|
||||
}
|
||||
@@ -662,6 +750,13 @@ mod tests {
|
||||
Some(expected)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
shortcut_action(
|
||||
&keyboard::Key::Character("f".into()),
|
||||
primary | keyboard::Modifiers::SHIFT,
|
||||
),
|
||||
Some(UiAction::SearchContents)
|
||||
);
|
||||
assert_eq!(
|
||||
shortcut_action(&keyboard::Key::Named(Named::F1), keyboard::Modifiers::NONE),
|
||||
Some(UiAction::Help)
|
||||
|
||||
Reference in New Issue
Block a user