mirror of
https://github.com/NotAShelf/stash.git
synced 2026-04-12 14:07:42 +00:00
commands/list: debounce for rapid copy operations
Tracks the entry ID currently being copied in `TuiState` to prevent concurrent `copy_entry()` calls on the same entity. Otherwise we hit a race condition. Fun! Track the entry ID currently being copied in TuiState to prevent concurrent copy_entry() calls on the same entry. Fixes database race conditions when users trigger copy commands in rapid succession. Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: If8e8fe56bf6dc35960e47decf59636116a6a6964
This commit is contained in:
parent
cf5b1e8205
commit
0865a1f139
1 changed files with 49 additions and 36 deletions
|
|
@ -57,6 +57,9 @@ struct TuiState {
|
||||||
|
|
||||||
/// Whether to show entries in reverse order (oldest first).
|
/// Whether to show entries in reverse order (oldest first).
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
|
|
||||||
|
/// ID of entry currently being copied.
|
||||||
|
copying_entry: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TuiState {
|
impl TuiState {
|
||||||
|
|
@ -91,6 +94,7 @@ impl TuiState {
|
||||||
search_query: String::new(),
|
search_query: String::new(),
|
||||||
search_mode: false,
|
search_mode: false,
|
||||||
reverse,
|
reverse,
|
||||||
|
copying_entry: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -678,42 +682,51 @@ impl SqliteClipboardDb {
|
||||||
if actions.copy
|
if actions.copy
|
||||||
&& let Some(&(id, ..)) = tui.selected_entry()
|
&& let Some(&(id, ..)) = tui.selected_entry()
|
||||||
{
|
{
|
||||||
match self.copy_entry(id) {
|
if tui.copying_entry == Some(id) {
|
||||||
Ok((new_id, contents, mime)) => {
|
log::debug!(
|
||||||
if new_id != id {
|
"Skipping duplicate copy for entry {id} (already in \
|
||||||
tui.dirty = true;
|
progress)"
|
||||||
}
|
);
|
||||||
let opts = Options::new();
|
} else {
|
||||||
let mime_type = match mime {
|
tui.copying_entry = Some(id);
|
||||||
Some(ref m) if m == "text/plain" => MimeType::Text,
|
match self.copy_entry(id) {
|
||||||
Some(ref m) => MimeType::Specific(m.clone().to_owned()),
|
Ok((new_id, contents, mime)) => {
|
||||||
None => MimeType::Text,
|
if new_id != id {
|
||||||
};
|
tui.dirty = true;
|
||||||
let copy_result = opts
|
}
|
||||||
.copy(Source::Bytes(contents.clone().into()), mime_type);
|
let opts = Options::new();
|
||||||
match copy_result {
|
let mime_type = match mime {
|
||||||
Ok(()) => {
|
Some(ref m) if m == "text/plain" => MimeType::Text,
|
||||||
let _ = Notification::new()
|
Some(ref m) => MimeType::Specific(m.clone().to_owned()),
|
||||||
.summary("Stash")
|
None => MimeType::Text,
|
||||||
.body("Copied entry to clipboard")
|
};
|
||||||
.show();
|
let copy_result = opts
|
||||||
},
|
.copy(Source::Bytes(contents.clone().into()), mime_type);
|
||||||
Err(e) => {
|
match copy_result {
|
||||||
log::error!("Failed to copy entry to clipboard: {e}");
|
Ok(()) => {
|
||||||
let _ = Notification::new()
|
let _ = Notification::new()
|
||||||
.summary("Stash")
|
.summary("Stash")
|
||||||
.body(&format!("Failed to copy to clipboard: {e}"))
|
.body("Copied entry to clipboard")
|
||||||
.show();
|
.show();
|
||||||
},
|
},
|
||||||
}
|
Err(e) => {
|
||||||
},
|
log::error!("Failed to copy entry to clipboard: {e}");
|
||||||
Err(e) => {
|
let _ = Notification::new()
|
||||||
log::error!("Failed to fetch entry {id}: {e}");
|
.summary("Stash")
|
||||||
let _ = Notification::new()
|
.body(&format!("Failed to copy to clipboard: {e}"))
|
||||||
.summary("Stash")
|
.show();
|
||||||
.body(&format!("Failed to fetch entry: {e}"))
|
},
|
||||||
.show();
|
}
|
||||||
},
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to fetch entry {id}: {e}");
|
||||||
|
let _ = Notification::new()
|
||||||
|
.summary("Stash")
|
||||||
|
.body(&format!("Failed to fetch entry: {e}"))
|
||||||
|
.show();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tui.copying_entry = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue