Skip to content

Commit 1e145d6

Browse files
committed
fix: make 'Copy Magnet' button work in non-secure (HTTP) contexts.
navigator.clipboard is only defined in secure contexts, so the button silently failed when the UI was served over plain HTTP. Feature-detect the modern API and fall back to a hidden textarea + execCommand('copy'), which works everywhere.
1 parent 33e0cb6 commit 1e145d6

2 files changed

Lines changed: 31 additions & 4 deletions

File tree

frontend/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ serde_json = "1.0"
1111
wasm-bindgen = "0.2"
1212
js-sys = "0.3"
1313
wasm-bindgen-futures = "0.4"
14-
web-sys = { version = "0.3", features = ["HtmlSelectElement", "HtmlTextAreaElement", "HtmlInputElement", "DataTransfer", "DataTransferItem", "DataTransferItemList", "File", "FileList", "WebSocket", "MessageEvent", "Window", "Storage", "Navigator", "Clipboard"] }
14+
web-sys = { version = "0.3", features = ["HtmlSelectElement", "HtmlTextAreaElement", "HtmlInputElement", "HtmlDocument", "CssStyleDeclaration", "DataTransfer", "DataTransferItem", "DataTransferItemList", "File", "FileList", "WebSocket", "MessageEvent", "Window", "Storage", "Navigator", "Clipboard"] }
1515
gloo-net = { version = "0.6", features = ["websocket", "http"] }
1616
gloo-storage = "0.3"
1717
gloo-file = "0.3"

frontend/src/main.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,35 @@ use wasm_bindgen_futures::spawn_local;
1010
use web_sys::{Event as JsEvent, FileReader};
1111
use yew::prelude::*;
1212

13+
fn copy_text_to_clipboard(text: &str) {
14+
let Some(window) = web_sys::window() else { return };
15+
16+
let clipboard = window.navigator().clipboard();
17+
if !wasm_bindgen::JsValue::from(&clipboard).is_undefined() {
18+
let _ = clipboard.write_text(text);
19+
return;
20+
}
21+
22+
let Some(document) = window.document() else { return };
23+
let Some(body) = document.body() else { return };
24+
let Ok(el) = document.create_element("textarea") else { return };
25+
let Ok(textarea) = el.dyn_into::<web_sys::HtmlTextAreaElement>() else { return };
26+
textarea.set_value(text);
27+
textarea.set_read_only(true);
28+
let style = textarea.style();
29+
let _ = style.set_property("position", "fixed");
30+
let _ = style.set_property("left", "-9999px");
31+
let _ = style.set_property("top", "0");
32+
if body.append_child(&textarea).is_err() {
33+
return;
34+
}
35+
textarea.select();
36+
if let Ok(html_doc) = document.dyn_into::<web_sys::HtmlDocument>() {
37+
let _ = html_doc.exec_command("copy");
38+
}
39+
let _ = body.remove_child(&textarea);
40+
}
41+
1342
#[derive(Properties, PartialEq)]
1443
pub struct FileNodeProps {
1544
pub node: Value,
@@ -1439,9 +1468,7 @@ fn app() -> Html {
14391468
<button class="ui teal right labeled icon button" onclick={
14401469
let magnet = t.magnet.clone();
14411470
Callback::from(move |_| {
1442-
if let Some(clipboard) = web_sys::window().map(|w| w.navigator().clipboard()) {
1443-
let _ = clipboard.write_text(&magnet);
1444-
}
1471+
copy_text_to_clipboard(&magnet);
14451472
})
14461473
}>
14471474
<i class="copy icon"></i>

0 commit comments

Comments
 (0)