From 56eac1ac74e9f32bbf2541929e80b79cdb6cbcb1 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Mon, 28 Jul 2025 07:21:34 -0700 Subject: refined copy to clipboard ui feedback (#122) * copy feedback * add opti logo and update wording * branding updates * hide check mark when installed --- src/components/SmartClipboardButton.tsx | 62 ++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 28 deletions(-) (limited to 'src/components/SmartClipboardButton.tsx') diff --git a/src/components/SmartClipboardButton.tsx b/src/components/SmartClipboardButton.tsx index 7d250f5..095da15 100644 --- a/src/components/SmartClipboardButton.tsx +++ b/src/components/SmartClipboardButton.tsx @@ -1,31 +1,37 @@ -import { useState } from "react"; +import { useState, useEffect } from "react"; import { PanelSectionRow, ButtonItem } from "@decky/ui"; -import { FaClipboard } from "react-icons/fa"; +import { FaClipboard, FaCheck } from "react-icons/fa"; import { toaster } from "@decky/api"; interface SmartClipboardButtonProps { command?: string; buttonText?: string; - successMessage?: string; } export function SmartClipboardButton({ command = "~/fgmod/fgmod %command%", - buttonText = "Copy Launch Command", - successMessage = "Launch option ready to paste" + buttonText = "Copy Launch Command" }: SmartClipboardButtonProps) { const [isLoading, setIsLoading] = useState(false); + const [showSuccess, setShowSuccess] = useState(false); - const getLaunchOptionText = (): string => { - return command; - }; + // Reset success state after 3 seconds + useEffect(() => { + if (showSuccess) { + const timer = setTimeout(() => { + setShowSuccess(false); + }, 3000); + return () => clearTimeout(timer); + } + return undefined; + }, [showSuccess]); const copyToClipboard = async () => { - if (isLoading) return; + if (isLoading || showSuccess) return; setIsLoading(true); try { - const text = getLaunchOptionText(); + const text = command; // Use the proven input simulation method const tempInput = document.createElement('input'); @@ -58,27 +64,18 @@ export function SmartClipboardButton({ document.body.removeChild(tempInput); if (copySuccess) { + // Show success feedback in the button instead of toast + setShowSuccess(true); // Verify the copy worked by reading back try { const readBack = await navigator.clipboard.readText(); - if (readBack === text) { - toaster.toast({ - title: "Copied to Clipboard!", - body: successMessage - }); - } else { - // Copy worked but verification failed - still consider it success - toaster.toast({ - title: "Copied to Clipboard!", - body: "Launch option copied (verification unavailable)" - }); + if (readBack !== text) { + // Copy worked but verification failed - still show success + console.log('Copy verification failed but copy likely worked'); } } catch (e) { // Verification failed but copy likely worked - toaster.toast({ - title: "Copied to Clipboard!", - body: "Launch option copied successfully" - }); + console.log('Copy verification unavailable but copy likely worked'); } } else { toaster.toast({ @@ -102,10 +99,14 @@ export function SmartClipboardButton({ - {isLoading ? ( + {showSuccess ? ( + + ) : isLoading ? ( )} - {isLoading ? "Copying..." : buttonText} + + {showSuccess ? "Copied to clipboard" : isLoading ? "Copying..." : buttonText} +