From 2811ba4e29cd27b5893fba676278f29b155068cb Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Wed, 16 Jul 2025 17:11:00 -0400 Subject: non modal updater --- src/api/lsfgApi.ts | 21 ++++ src/components/Content.tsx | 4 + src/components/PluginUpdateChecker.tsx | 187 +++++++++++++++++++++++++++++++++ src/components/index.ts | 1 + 4 files changed, 213 insertions(+) create mode 100644 src/components/PluginUpdateChecker.tsx (limited to 'src') diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts index 2e7964c..f7363c1 100644 --- a/src/api/lsfgApi.ts +++ b/src/api/lsfgApi.ts @@ -49,6 +49,23 @@ export interface ConfigSchemaResult { defaults: ConfigurationData; } +export interface UpdateCheckResult { + success: boolean; + update_available: boolean; + current_version: string; + latest_version: string; + release_notes: string; + release_date: string; + download_url: string; + error?: string; +} + +export interface UpdateDownloadResult { + success: boolean; + download_path?: string; + error?: string; +} + // API functions export const installLsfgVk = callable<[], InstallationResult>("install_lsfg_vk"); export const uninstallLsfgVk = callable<[], InstallationResult>("uninstall_lsfg_vk"); @@ -68,3 +85,7 @@ export const updateLsfgConfigFromObject = async (config: ConfigurationData): Pro const args = ConfigurationManager.createArgsFromConfig(config); return updateLsfgConfig(...args as [boolean, number, number, boolean, boolean, boolean, boolean, number]); }; + +// Self-updater API functions +export const checkForPluginUpdate = callable<[], UpdateCheckResult>("check_for_plugin_update"); +export const downloadPluginUpdate = callable<[string], UpdateDownloadResult>("download_plugin_update"); diff --git a/src/components/Content.tsx b/src/components/Content.tsx index ba651d4..ea3f3c1 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -8,6 +8,7 @@ import { ConfigurationSection } from "./ConfigurationSection"; import { UsageInstructions } from "./UsageInstructions"; import { WikiButton } from "./WikiButton"; import { ClipboardButton } from "./ClipboardButton"; +import { PluginUpdateChecker } from "./PluginUpdateChecker"; import { ConfigurationData } from "../config/configSchema"; export function Content() { @@ -77,6 +78,9 @@ export function Content() { + + {/* Plugin Update Checker */} + ); } diff --git a/src/components/PluginUpdateChecker.tsx b/src/components/PluginUpdateChecker.tsx new file mode 100644 index 0000000..0028a79 --- /dev/null +++ b/src/components/PluginUpdateChecker.tsx @@ -0,0 +1,187 @@ +import React, { useState, useEffect } from 'react'; +import { + ButtonItem, + PanelSection +} from '@decky/ui'; +import { checkForPluginUpdate, downloadPluginUpdate, UpdateCheckResult, UpdateDownloadResult } from '../api/lsfgApi'; + +interface PluginUpdateCheckerProps { + // Add any props if needed +} + +interface UpdateInfo { + updateAvailable: boolean; + currentVersion: string; + latestVersion: string; + releaseNotes: string; + releaseDate: string; + downloadUrl: string; +} + +export const PluginUpdateChecker: React.FC = () => { + const [checkingUpdate, setCheckingUpdate] = useState(false); + const [downloadingUpdate, setDownloadingUpdate] = useState(false); + const [updateInfo, setUpdateInfo] = useState(null); + const [updateError, setUpdateError] = useState(null); + const [downloadResult, setDownloadResult] = useState(null); + + // Auto-hide error messages after 5 seconds + useEffect(() => { + if (updateError) { + const timer = setTimeout(() => { + setUpdateError(null); + }, 5000); + return () => clearTimeout(timer); + } + return undefined; + }, [updateError]); + + const handleCheckForUpdate = async () => { + setCheckingUpdate(true); + setUpdateError(null); + setUpdateInfo(null); + setDownloadResult(null); // Clear previous download result + + try { + const result: UpdateCheckResult = await checkForPluginUpdate(); + + if (result.success) { + setUpdateInfo({ + updateAvailable: result.update_available, + currentVersion: result.current_version, + latestVersion: result.latest_version, + releaseNotes: result.release_notes, + releaseDate: result.release_date, + downloadUrl: result.download_url + }); + + // Simple console log instead of toast since showToast may not be available + if (result.update_available) { + console.log("Update available!", `Version ${result.latest_version} is now available.`); + } else { + console.log("Up to date!", "You have the latest version installed."); + } + } else { + setUpdateError(result.error || "Failed to check for updates"); + } + } catch (error) { + setUpdateError(`Error checking for updates: ${error}`); + } finally { + setCheckingUpdate(false); + } + }; + + const handleDownloadUpdate = async () => { + if (!updateInfo?.downloadUrl) return; + + setDownloadingUpdate(true); + setUpdateError(null); + setDownloadResult(null); + + try { + const result: UpdateDownloadResult = await downloadPluginUpdate(updateInfo.downloadUrl); + + if (result.success) { + setDownloadResult(result); + console.log("✓ Download complete!", `Plugin downloaded to ${result.download_path}`); + } else { + setUpdateError(result.error || "Failed to download update"); + } + } catch (error) { + setUpdateError(`Error downloading update: ${error}`); + } finally { + setDownloadingUpdate(false); + } + }; + + const getStatusMessage = () => { + if (!updateInfo) return null; + + if (updateInfo.updateAvailable) { + if (downloadResult?.success) { + return ( +
+ ✓ v{updateInfo.latestVersion} downloaded - ready to install +
+ ); + } else { + return ( +
+ Update available: v{updateInfo.latestVersion} +
+ ); + } + } else { + return ( +
+ Up to date (v{updateInfo.currentVersion}) +
+ ); + } + }; + + return ( + + + {checkingUpdate ? 'Checking for updates...' : 'Check for Updates'} + + + {updateInfo && updateInfo.updateAvailable && !downloadResult?.success && ( + + {downloadingUpdate ? 'Downloading...' : 'Download Update'} + + )} + + {downloadResult?.success && ( +
+
+ ✓ Download Complete! +
+
+ File saved to: {downloadResult.download_path} +
+
+ Installation Instructions: +
    +
  1. Go to Decky Loader settings
  2. +
  3. Click "Developer" tab
  4. +
  5. Click "Uninstall" next to "Lossless Scaling"
  6. +
  7. Click "Install from ZIP"
  8. +
  9. Select the downloaded file
  10. +
  11. Restart Steam or reload plugins
  12. +
+
+
+ )} + + {updateError && ( +
+ {updateError} +
+ )} +
+ ); +}; diff --git a/src/components/index.ts b/src/components/index.ts index 7304ca9..d26159d 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -5,3 +5,4 @@ export { ConfigurationSection } from "./ConfigurationSection"; export { UsageInstructions } from "./UsageInstructions"; export { WikiButton } from "./WikiButton"; export { ClipboardButton } from "./ClipboardButton"; +export { PluginUpdateChecker } from "./PluginUpdateChecker"; -- cgit v1.2.3