SMP join button: robust clipboard copy with fallback for insecure contexts
This commit is contained in:
parent
10135b3d36
commit
9695452974
1 changed files with 33 additions and 8 deletions
|
|
@ -360,7 +360,7 @@ function SmpApp() {
|
||||||
motd: null,
|
motd: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
});
|
});
|
||||||
const [copied, setCopied] = useState(false);
|
const [copyState, setCopyState] = useState<"idle" | "ok" | "fail">("idle");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let alive = true;
|
let alive = true;
|
||||||
|
|
@ -389,13 +389,36 @@ function SmpApp() {
|
||||||
const pending = info.status === "pending";
|
const pending = info.status === "pending";
|
||||||
|
|
||||||
const copyAddress = async () => {
|
const copyAddress = async () => {
|
||||||
|
let ok = false;
|
||||||
|
// The async Clipboard API only exists in secure contexts (https / localhost)
|
||||||
|
// and can still reject (no focus, denied permission).
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(address);
|
if (navigator.clipboard?.writeText) {
|
||||||
setCopied(true);
|
await navigator.clipboard.writeText(address);
|
||||||
window.setTimeout(() => setCopied(false), 1600);
|
ok = true;
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* clipboard unavailable - the address is shown below anyway */
|
ok = false;
|
||||||
}
|
}
|
||||||
|
// Fallback that works in insecure contexts (e.g. LAN over http).
|
||||||
|
if (!ok) {
|
||||||
|
try {
|
||||||
|
const ta = document.createElement("textarea");
|
||||||
|
ta.value = address;
|
||||||
|
ta.setAttribute("readonly", "");
|
||||||
|
ta.style.position = "fixed";
|
||||||
|
ta.style.top = "-1000px";
|
||||||
|
ta.style.opacity = "0";
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.select();
|
||||||
|
ok = document.execCommand("copy");
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
} catch {
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCopyState(ok ? "ok" : "fail");
|
||||||
|
window.setTimeout(() => setCopyState("idle"), 1800);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -445,7 +468,7 @@ function SmpApp() {
|
||||||
|
|
||||||
<div className="smp-actions">
|
<div className="smp-actions">
|
||||||
<button type="button" className="smp-btn smp-btn-green" onClick={copyAddress}>
|
<button type="button" className="smp-btn smp-btn-green" onClick={copyAddress}>
|
||||||
{copied ? "Address Copied!" : "Join Server"}
|
{copyState === "ok" ? "Address Copied!" : copyState === "fail" ? "Copy failed" : "Join Server"}
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
className="smp-btn"
|
className="smp-btn"
|
||||||
|
|
@ -458,9 +481,11 @@ function SmpApp() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="smp-hint">
|
<p className="smp-hint">
|
||||||
{copied
|
{copyState === "ok"
|
||||||
? "Paste it in Minecraft → Multiplayer → Add Server."
|
? "Paste it in Minecraft → Multiplayer → Add Server."
|
||||||
: "Join Server copies the address. Add it in Minecraft → Multiplayer → Add Server."}
|
: copyState === "fail"
|
||||||
|
? `Copy manually: ${address}`
|
||||||
|
: "Join Server copies the address. Add it in Minecraft → Multiplayer → Add Server."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue