From c24091abd19e6610582b98f5153cedae619a3832 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Wed, 9 Oct 2024 18:17:50 +0200 Subject: [PATCH] app_store UI: launch button loader --- .../app_store/ui/src/pages/DownloadPage.tsx | 158 ++++++++++++------ .../packages/app_store/ui/src/store/index.ts | 6 - 2 files changed, 108 insertions(+), 56 deletions(-) diff --git a/kinode/packages/app_store/ui/src/pages/DownloadPage.tsx b/kinode/packages/app_store/ui/src/pages/DownloadPage.tsx index cd90c6be..c933d3a7 100644 --- a/kinode/packages/app_store/ui/src/pages/DownloadPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/DownloadPage.tsx @@ -1,11 +1,10 @@ import React, { useState, useEffect, useCallback, useMemo } from "react"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams } from "react-router-dom"; import { FaDownload, FaSpinner, FaChevronDown, FaChevronUp, FaRocket, FaTrash, FaPlay } from "react-icons/fa"; import useAppsStore from "../store"; import { MirrorSelector } from '../components'; export default function DownloadPage() { - const navigate = useNavigate(); const { id } = useParams<{ id: string }>(); const { listings, @@ -28,6 +27,9 @@ export default function DownloadPage() { const [isMirrorOnline, setIsMirrorOnline] = useState(null); const [showCapApproval, setShowCapApproval] = useState(false); const [manifest, setManifest] = useState(null); + const [isInstalling, setIsInstalling] = useState(false); + const [isCheckingLaunch, setIsCheckingLaunch] = useState(false); + const [launchPath, setLaunchPath] = useState(null); const app = useMemo(() => listings[id || ""], [listings, id]); const appDownloads = useMemo(() => downloads[id || ""] || [], [downloads, id]); @@ -101,6 +103,36 @@ export default function DownloadPage() { return versionData ? installedApp.our_version_hash === versionData.hash : false; }, [app, selectedVersion, installedApp, sortedVersions]); + const checkLaunchPath = useCallback(() => { + if (!app) return; + setIsCheckingLaunch(true); + const appId = `${app.package_id.package_name}:${app.package_id.publisher_node}`; + fetchHomepageApps().then(() => { + const path = getLaunchUrl(appId); + setLaunchPath(path); + setIsCheckingLaunch(false); + if (path) { + setIsInstalling(false); + } + }); + }, [app, fetchHomepageApps, getLaunchUrl]); + + useEffect(() => { + if (isInstalling) { + const checkInterval = setInterval(checkLaunchPath, 500); + const timeout = setTimeout(() => { + clearInterval(checkInterval); + setIsInstalling(false); + setIsCheckingLaunch(false); + }, 5000); + + return () => { + clearInterval(checkInterval); + clearTimeout(timeout); + }; + } + }, [isInstalling, checkLaunchPath]); + const handleDownload = useCallback(() => { if (!id || !selectedMirror || !app || !selectedVersion) return; const versionData = sortedVersions.find(v => v.version === selectedVersion); @@ -130,36 +162,87 @@ export default function DownloadPage() { } }, [id, app, appDownloads]); - const canDownload = useMemo(() => { - return selectedMirror && (isMirrorOnline === true || selectedMirror.startsWith('http')) && !isDownloading && !isDownloaded; - }, [selectedMirror, isMirrorOnline, isDownloading, isDownloaded]); - const confirmInstall = useCallback(() => { if (!id || !selectedVersion) return; const versionData = sortedVersions.find(v => v.version === selectedVersion); if (versionData) { + setIsInstalling(true); + setLaunchPath(null); installApp(id, versionData.hash).then(() => { - fetchData(id); setShowCapApproval(false); setManifest(null); + fetchData(id); }); } }, [id, selectedVersion, sortedVersions, installApp, fetchData]); const handleLaunch = useCallback(() => { - if (app) { - const launchUrl = getLaunchUrl(`${app.package_id.package_name}:${app.package_id.publisher_node}`); - if (launchUrl) { - window.location.href = launchUrl; - } + if (launchPath) { + window.location.href = launchPath; } - }, [app, getLaunchUrl]); + }, [launchPath]); const canLaunch = useMemo(() => { if (!app) return false; return !!getLaunchUrl(`${app.package_id.package_name}:${app.package_id.publisher_node}`); }, [app, getLaunchUrl]); + const canDownload = useMemo(() => { + return selectedMirror && (isMirrorOnline === true || selectedMirror.startsWith('http')) && !isDownloading && !isDownloaded; + }, [selectedMirror, isMirrorOnline, isDownloading, isDownloaded]); + + const renderActionButton = () => { + if (isCurrentVersionInstalled || launchPath) { + return ( + + ); + } + + if (isInstalling || isCheckingLaunch) { + return ( + + ); + } + + if (isDownloaded) { + return ( + + ); + } + + return ( + + ); + }; + if (!app) { return

Loading app details...

; } @@ -176,15 +259,22 @@ export default function DownloadPage() {

{`${app.package_id.package_name}.${app.package_id.publisher_node}`}

- {installedApp && ( + {launchPath ? ( - )} + ) : isInstalling || isCheckingLaunch ? ( + + ) : installedApp ? ( + + ) : null}

{app.metadata?.description}

@@ -207,39 +297,7 @@ export default function DownloadPage() { onMirrorSelect={handleMirrorSelect} /> - {isCurrentVersionInstalled ? ( - - ) : isDownloaded ? ( - - ) : ( - - )} + {renderActionButton()}
diff --git a/kinode/packages/app_store/ui/src/store/index.ts b/kinode/packages/app_store/ui/src/store/index.ts index c87e23e8..c6c0deec 100644 --- a/kinode/packages/app_store/ui/src/store/index.ts +++ b/kinode/packages/app_store/ui/src/store/index.ts @@ -218,12 +218,6 @@ const useAppsStore = create()((set, get) => ({ }); if (res.status === HTTP_STATUS.CREATED) { await get().fetchInstalled(); - - // hacky: a small delay (500ms) before fetching homepage apps - // to give the app time to add itself to the homepage - // might make sense to add more state and do retry logic instead. - await new Promise(resolve => setTimeout(resolve, 500)); - await get().fetchHomepageApps(); } } catch (error) {