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/FGModInstallerSection.tsx | 29 ++++++++++++--- src/components/SmartClipboardButton.tsx | 62 +++++++++++++++++--------------- src/index.tsx | 6 ++-- src/utils/constants.ts | 6 ++-- 4 files changed, 64 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/components/FGModInstallerSection.tsx b/src/components/FGModInstallerSection.tsx index 6777301..b82e749 100644 --- a/src/components/FGModInstallerSection.tsx +++ b/src/components/FGModInstallerSection.tsx @@ -5,6 +5,7 @@ import { OperationResult } from "./ResultDisplay"; import { SmartClipboardButton } from "./SmartClipboardButton"; import { createAutoCleanupTimer } from "../utils"; import { TIMEOUTS, MESSAGES, STYLES } from "../utils/constants"; +import optiScalerImage from "../../assets/optiscaler.png"; interface FGModInstallerSectionProps { pathExists: boolean | null; @@ -63,10 +64,10 @@ export function FGModInstallerSection({ pathExists, setPathExists }: FGModInstal return ( - {pathExists !== null ? ( + {pathExists === false ? ( -
- {pathExists ? MESSAGES.modInstalled : MESSAGES.modNotInstalled} +
+ {MESSAGES.modNotInstalled}
) : null} @@ -87,6 +88,26 @@ export function FGModInstallerSection({ pathExists, setPathExists }: FGModInstal ) : null} + {pathExists === true ? ( + +
+ OptiScaler +
+
+ ) : null} + {pathExists === true ? (
@@ -104,7 +125,6 @@ export function FGModInstallerSection({ pathExists, setPathExists }: FGModInstal ) : null} @@ -112,7 +132,6 @@ export function FGModInstallerSection({ pathExists, setPathExists }: FGModInstal ) : null} 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} +