QR dialog: Give "Copied!" feedback

Clicking anywhere on the URI row copies to clipboard (not just the icon).
Shows "Copied!" text for 2 seconds replacing the copy icon.
This commit is contained in:
Dagur Valberg Johannsson 2026-03-22 22:18:26 +01:00
parent 9593332525
commit 676512ea8d
No known key found for this signature in database
GPG key ID: FD701804AEE88107

View file

@ -2,7 +2,7 @@
// This software is licensed under the GNU Lesser General Public License (LGPL), version 3.0 or later. // This software is licensed under the GNU Lesser General Public License (LGPL), version 3.0 or later.
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/lgpl-3.0.html // A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/lgpl-3.0.html
import React, { useCallback } from "react"; import React, { useCallback, useState, useRef } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { AlphanumericQRCode } from "./AlphanumericQRCode.js"; import { AlphanumericQRCode } from "./AlphanumericQRCode.js";
import { WIZARDCONNECT_LOGO } from "./logo.js"; import { WIZARDCONNECT_LOGO } from "./logo.js";
@ -95,12 +95,18 @@ export function WizardConnectQRDialog({
}: WizardConnectQRDialogProps) { }: WizardConnectQRDialogProps) {
const t = { ...defaults, ...theme }; const t = { ...defaults, ...theme };
const [copied, setCopied] = useState(false);
const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const handleCopy = useCallback(() => { const handleCopy = useCallback(() => {
if (onCopy) { if (onCopy) {
onCopy(uri); onCopy(uri);
} else if (typeof navigator !== "undefined" && navigator.clipboard) { } else if (typeof navigator !== "undefined" && navigator.clipboard) {
navigator.clipboard.writeText(uri).catch(() => {}); navigator.clipboard.writeText(uri).catch(() => {});
} }
setCopied(true);
if (copiedTimerRef.current) clearTimeout(copiedTimerRef.current);
copiedTimerRef.current = setTimeout(() => setCopied(false), 2000);
}, [uri, onCopy]); }, [uri, onCopy]);
const handleBackdropClick = useCallback( const handleBackdropClick = useCallback(
@ -210,8 +216,9 @@ export function WizardConnectQRDialog({
/> />
</div> </div>
{/* Copy URI row */} {/* Copy URI row — entire row is clickable */}
<div <button
onClick={handleCopy}
style={{ style={{
width: "100%", width: "100%",
display: "flex", display: "flex",
@ -221,7 +228,12 @@ export function WizardConnectQRDialog({
backgroundColor: t.uriRowBackground, backgroundColor: t.uriRowBackground,
borderRadius: "8px", borderRadius: "8px",
border: `1px solid ${t.borderColor}`, border: `1px solid ${t.borderColor}`,
cursor: "pointer",
textAlign: "left",
font: "inherit",
transition: "border-color 0.15s",
}} }}
aria-label="Copy URI"
> >
<p <p
style={{ style={{
@ -238,22 +250,26 @@ export function WizardConnectQRDialog({
> >
{uri} {uri}
</p> </p>
<button {copied ? (
onClick={handleCopy} <span
style={{ style={{
background: "none", color: "#0307fe",
border: "none", fontSize: "12px",
cursor: "pointer", fontWeight: 600,
padding: "4px", flexShrink: 0,
flexShrink: 0, whiteSpace: "nowrap",
display: "flex", }}
alignItems: "center", >
}} Copied!
aria-label="Copy URI" </span>
> ) : (
<CopyIcon color={t.copyButtonColor} /> <span
</button> style={{ flexShrink: 0, display: "flex", alignItems: "center" }}
</div> >
<CopyIcon color={t.copyButtonColor} />
</span>
)}
</button>
</div> </div>
</div> </div>
</div>, </div>,