From 9f7c945d96d53168d8df2e8e35de316603c5ad6d Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Mon, 28 Oct 2024 17:54:46 +0100 Subject: [PATCH] app_store UI: my-downloads -> my-apps page, enable uninstalling unlisted packages --- kinode/packages/app_store/ui/src/App.tsx | 6 +- .../app_store/ui/src/components/Header.tsx | 6 +- .../app_store/ui/src/constants/path.ts | 2 +- .../{MyDownloadsPage.tsx => MyAppsPage.tsx} | 144 +++++++++++++++++- kinode/packages/app_store/ui/vite.config.ts | 2 +- 5 files changed, 147 insertions(+), 13 deletions(-) rename kinode/packages/app_store/ui/src/pages/{MyDownloadsPage.tsx => MyAppsPage.tsx} (61%) diff --git a/kinode/packages/app_store/ui/src/App.tsx b/kinode/packages/app_store/ui/src/App.tsx index aa112f28..c2f3d14e 100644 --- a/kinode/packages/app_store/ui/src/App.tsx +++ b/kinode/packages/app_store/ui/src/App.tsx @@ -2,13 +2,13 @@ import React from "react"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import Header from "./components/Header"; -import { APP_DETAILS_PATH, DOWNLOAD_PATH, MY_DOWNLOADS_PATH, PUBLISH_PATH, STORE_PATH } from "./constants/path"; +import { APP_DETAILS_PATH, DOWNLOAD_PATH, MY_APPS_PATH, PUBLISH_PATH, STORE_PATH } from "./constants/path"; import StorePage from "./pages/StorePage"; import AppPage from "./pages/AppPage"; import DownloadPage from "./pages/DownloadPage"; import PublishPage from "./pages/PublishPage"; -import MyDownloadsPage from "./pages/MyDownloadsPage"; +import MyAppsPage from "./pages/MyAppsPage"; const BASE_URL = import.meta.env.BASE_URL; @@ -22,7 +22,7 @@ function App() {
} /> - } /> + } /> } /> } /> } /> diff --git a/kinode/packages/app_store/ui/src/components/Header.tsx b/kinode/packages/app_store/ui/src/components/Header.tsx index b28695b0..14cc9d6d 100644 --- a/kinode/packages/app_store/ui/src/components/Header.tsx +++ b/kinode/packages/app_store/ui/src/components/Header.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { STORE_PATH, PUBLISH_PATH, MY_DOWNLOADS_PATH } from '../constants/path'; +import { STORE_PATH, PUBLISH_PATH, MY_APPS_PATH } from '../constants/path'; import { ConnectButton } from '@rainbow-me/rainbowkit'; import { FaHome } from "react-icons/fa"; @@ -9,12 +9,12 @@ const Header: React.FC = () => {
diff --git a/kinode/packages/app_store/ui/src/constants/path.ts b/kinode/packages/app_store/ui/src/constants/path.ts index 0f422617..08f6c817 100644 --- a/kinode/packages/app_store/ui/src/constants/path.ts +++ b/kinode/packages/app_store/ui/src/constants/path.ts @@ -2,4 +2,4 @@ export const STORE_PATH = '/'; export const PUBLISH_PATH = '/publish'; export const APP_DETAILS_PATH = '/app'; export const DOWNLOAD_PATH = '/download'; -export const MY_DOWNLOADS_PATH = '/my-downloads'; +export const MY_APPS_PATH = '/my-apps'; diff --git a/kinode/packages/app_store/ui/src/pages/MyDownloadsPage.tsx b/kinode/packages/app_store/ui/src/pages/MyAppsPage.tsx similarity index 61% rename from kinode/packages/app_store/ui/src/pages/MyDownloadsPage.tsx rename to kinode/packages/app_store/ui/src/pages/MyAppsPage.tsx index edafaf4c..fa2af6f7 100644 --- a/kinode/packages/app_store/ui/src/pages/MyDownloadsPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/MyAppsPage.tsx @@ -3,15 +3,41 @@ import { FaFolder, FaFile, FaChevronLeft, FaSync, FaRocket, FaSpinner, FaCheck, import useAppsStore from "../store"; import { DownloadItem, PackageManifest, PackageState } from "../types/Apps"; -export default function MyDownloadsPage() { - const { fetchDownloads, fetchDownloadsForApp, startMirroring, stopMirroring, installApp, removeDownload, fetchInstalled, installed } = useAppsStore(); +// Core packages that cannot be uninstalled +const CORE_PACKAGES = [ + "app_store:sys", + "contacts:sys", + "kino_updates:sys", + "terminal:sys", + "chess:sys", + "kns_indexer:sys", + "settings:sys", + "homepage:sys" +]; + +export default function MyAppsPage() { + const { + fetchDownloads, + fetchDownloadsForApp, + startMirroring, + stopMirroring, + installApp, + removeDownload, + fetchInstalled, + installed, + uninstallApp + } = useAppsStore(); + const [currentPath, setCurrentPath] = useState([]); const [items, setItems] = useState([]); const [isInstalling, setIsInstalling] = useState(false); + const [isUninstalling, setIsUninstalling] = useState(false); const [error, setError] = useState(null); const [showCapApproval, setShowCapApproval] = useState(false); const [manifest, setManifest] = useState(null); const [selectedItem, setSelectedItem] = useState(null); + const [showUninstallConfirm, setShowUninstallConfirm] = useState(false); + const [appToUninstall, setAppToUninstall] = useState(null); useEffect(() => { loadItems(); @@ -33,6 +59,35 @@ export default function MyDownloadsPage() { } }; + const initiateUninstall = (app: any) => { + const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`; + if (CORE_PACKAGES.includes(packageId)) { + setError("Cannot uninstall core system packages"); + return; + } + setAppToUninstall(app); + setShowUninstallConfirm(true); + }; + + const handleUninstall = async () => { + if (!appToUninstall) return; + setIsUninstalling(true); + const packageId = `${appToUninstall.package_id.package_name}:${appToUninstall.package_id.publisher_node}`; + try { + await uninstallApp(packageId); + await fetchInstalled(); + await loadItems(); + setShowUninstallConfirm(false); + setAppToUninstall(null); + } catch (error) { + console.error('Uninstallation failed:', error); + setError(`Uninstallation failed: ${error instanceof Error ? error.message : String(error)}`); + } finally { + setIsUninstalling(false); + } + }; + + const navigateToItem = (item: DownloadItem) => { if (item.Dir) { setCurrentPath([...currentPath, item.Dir.name]); @@ -85,7 +140,6 @@ export default function MyDownloadsPage() { if (!versionHash) throw new Error('Invalid file name format'); - // Construct packageId by combining currentPath and remaining parts of the filename const packageId = [...currentPath, ...parts].join(':'); await installApp(packageId, versionHash); @@ -121,8 +175,48 @@ export default function MyDownloadsPage() { return (
-

Downloads

+

My Apps

+ + {/* Installed Apps Section */}
+

Installed Apps

+ + + + + + + + + {Object.values(installed).map((app) => { + const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`; + const isCore = CORE_PACKAGES.includes(packageId); + return ( + + + + + ); + })} + +
Package IDActions
{packageId} + {isCore ? ( + Core Package + ) : ( + + )} +
+
+ + {/* Downloads Section */} +
+

Downloads

{currentPath.length > 0 && (
)} + {/* Uninstall Confirmation Modal */} + {showUninstallConfirm && appToUninstall && ( +
+
+

Confirm Uninstall

+
+ Are you sure you want to uninstall this app? +
+
+ Package ID: {`${appToUninstall.package_id.package_name}:${appToUninstall.package_id.publisher_node}`} +
+ {appToUninstall.metadata?.name && ( +
+ Name: {appToUninstall.metadata.name} +
+ )} +
+ + +
+
+
+ )} + + + {showCapApproval && manifest && (
diff --git a/kinode/packages/app_store/ui/vite.config.ts b/kinode/packages/app_store/ui/vite.config.ts index 625a5e05..8a556bed 100644 --- a/kinode/packages/app_store/ui/vite.config.ts +++ b/kinode/packages/app_store/ui/vite.config.ts @@ -17,7 +17,7 @@ The format is "/" + "process_name:package_name:publisher_node" const BASE_URL = `/main:app_store:sys`; // This is the proxy URL, it must match the node you are developing against -const PROXY_URL = (process.env.VITE_NODE_URL || 'http://127.0.0.1:8080').replace('localhost', '127.0.0.1'); +const PROXY_URL = (process.env.VITE_NODE_URL || 'http://127.0.0.1:8080'); console.log('process.env.VITE_NODE_URL', process.env.VITE_NODE_URL, PROXY_URL);