From 7edcf31ddd2ae773b8ed7b09e1ec4b28e29128bc Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Mon, 29 Jul 2024 14:02:13 +0300 Subject: [PATCH] app_store: widget change and more ui --- .../app_store/app_store/src/http_api.rs | 2 +- kinode/packages/app_store/ui/src/App.tsx | 4 +- .../app_store/ui/src/components/Header.tsx | 3 +- .../app_store/ui/src/constants/path.ts | 1 - kinode/packages/app_store/ui/src/index.css | 141 +++++++++------ .../app_store/ui/src/pages/AppPage.tsx | 170 +++++------------- .../app_store/ui/src/pages/PublishPage.tsx | 2 +- .../app_store/ui/src/pages/StorePage.tsx | 14 +- 8 files changed, 144 insertions(+), 193 deletions(-) diff --git a/kinode/packages/app_store/app_store/src/http_api.rs b/kinode/packages/app_store/app_store/src/http_api.rs index fa0824f2..2e17e15b 100644 --- a/kinode/packages/app_store/app_store/src/http_api.rs +++ b/kinode/packages/app_store/app_store/src/http_api.rs @@ -144,7 +144,7 @@ fn make_widget() -> String { if (app.metadata) { const a = document.createElement('a'); a.className = 'app'; - a.href = `/main:app_store:sys/apps/${app.package}:${app.publisher}` + a.href = `/main:app_store:sys/app/${app.package}:${app.publisher}` a.target = '_blank'; a.rel = 'noopener noreferrer'; const iconLetter = app.metadata_hash.replace('0x', '')[0].toUpperCase(); diff --git a/kinode/packages/app_store/ui/src/App.tsx b/kinode/packages/app_store/ui/src/App.tsx index bdccebfd..aa14c28f 100644 --- a/kinode/packages/app_store/ui/src/App.tsx +++ b/kinode/packages/app_store/ui/src/App.tsx @@ -2,9 +2,8 @@ import React from "react"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import StorePage from "./pages/StorePage"; -import MyAppsPage from "./pages/MyAppsPage"; import AppPage from "./pages/AppPage"; -import { APP_DETAILS_PATH, MY_APPS_PATH, PUBLISH_PATH, STORE_PATH } from "./constants/path"; +import { APP_DETAILS_PATH, PUBLISH_PATH, STORE_PATH } from "./constants/path"; import PublishPage from "./pages/PublishPage"; import Header from "./components/Header"; @@ -19,7 +18,6 @@ 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 fbd66da5..1a956888 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, useLocation } from 'react-router-dom'; -import { STORE_PATH, MY_APPS_PATH, PUBLISH_PATH } from '../constants/path'; +import { STORE_PATH, PUBLISH_PATH } from '../constants/path'; import { ConnectButton } from '@rainbow-me/rainbowkit'; const Header: React.FC = () => { @@ -11,7 +11,6 @@ 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 de3c86e5..b15c60fb 100644 --- a/kinode/packages/app_store/ui/src/constants/path.ts +++ b/kinode/packages/app_store/ui/src/constants/path.ts @@ -1,4 +1,3 @@ -export const MY_APPS_PATH = '/my-apps'; export const STORE_PATH = '/'; export const PUBLISH_PATH = '/publish'; export const APP_DETAILS_PATH = '/app'; diff --git a/kinode/packages/app_store/ui/src/index.css b/kinode/packages/app_store/ui/src/index.css index d2772092..666f3727 100644 --- a/kinode/packages/app_store/ui/src/index.css +++ b/kinode/packages/app_store/ui/src/index.css @@ -106,85 +106,89 @@ color: light-dark(var(--gray), var(--off-white)); } -/* ... (previous styles) ... */ - /* App Page Styles */ .app-page { - padding: 2rem; + max-width: 800px; + margin: 0 auto; } -.app-details-card { - background-color: light-dark(var(--white), var(--off-black)); - border-radius: 8px; - padding: 2rem; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); -} - -.app-divider { - border: none; - border-top: 1px solid var(--gray); - margin: 1rem 0; -} - -.app-info-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 1rem; +.app-description { margin-bottom: 1rem; + color: light-dark(var(--gray), var(--off-white)); } -.app-info-item { - text-align: center; +.app-details { + display: flex; + justify-content: space-between; + gap: 2rem; } -.app-info-top { - font-weight: bold; - margin-bottom: 0.5rem; +.app-info { + flex: 1; } -.app-info-middle { - font-size: 1.5rem; - margin-bottom: 0.5rem; +.app-details-list { + list-style-type: none; + padding: 0; } -.app-info-bottom { +.app-details-list li { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0; + border-bottom: 1px solid light-dark(var(--gray), var(--maroon)); +} + +.app-details-list li:last-child { + border-bottom: none; +} + +.status-icon { + font-size: 1rem; +} + +.status-icon.installed, +.status-icon.mirroring, +.status-icon.auto-update { + color: var(--orange); +} + +.status-icon.not-installed, +.status-icon.not-mirroring, +.status-icon.no-auto-update { + color: var(--ansi-red); +} + +.app-actions { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.app-actions button { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.5rem 1rem; font-size: 0.9rem; - color: var(--gray); } .app-screenshots { display: flex; overflow-x: auto; gap: 1rem; - margin-bottom: 1rem; + padding: 1rem 0; + margin-top: 1rem; } .app-screenshot { max-width: 200px; border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } -.app-actions { - display: flex; - gap: 1rem; - flex-wrap: wrap; -} - -.app-actions button, -.app-actions .button { - flex: 1; - min-width: 120px; -} - -.version-number { - font-family: monospace; - background-color: light-dark(var(--tan), var(--maroon)); - padding: 0.2rem 0.5rem; - border-radius: 4px; -} - -/* ... (previous styles) ... */ - /* My Apps Page Styles */ .my-apps-page { padding: 2rem; @@ -258,8 +262,6 @@ color: light-dark(var(--gray), var(--off-white)); } -/* ... (previous styles) ... */ - /* Publish Page Styles */ .publish-page { padding: 2rem; @@ -361,8 +363,6 @@ background-color: #c62828; } -/* ... (previous styles) ... */ - /* Store Page Styles */ .store-page { padding: 2rem; @@ -371,13 +371,15 @@ .store-header { display: flex; justify-content: space-between; - align-items: center; + align-items: stretch; margin-bottom: 2rem; + gap: 1rem; } .search-bar { flex-grow: 1; - margin-right: 1rem; + display: flex; + align-items: stretch; } .search-bar input { @@ -386,6 +388,27 @@ font-size: 1rem; border: 1px solid var(--gray); border-radius: 4px; + height: 38px; +} + +.filter-button, +.store-header button { + height: 38px; + padding: 0 1rem; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + align-self: stretch; +} + +/* Add these new styles */ +.store-header>* { + margin: 0; +} + +.store-header button { + flex-shrink: 0; } .app-list table { @@ -430,7 +453,7 @@ } .status.installed { - background-color: var(--green); + background-color: var(--off-black); color: var(--white); } diff --git a/kinode/packages/app_store/ui/src/pages/AppPage.tsx b/kinode/packages/app_store/ui/src/pages/AppPage.tsx index 18652fa0..b7d45a81 100644 --- a/kinode/packages/app_store/ui/src/pages/AppPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/AppPage.tsx @@ -1,148 +1,72 @@ -import React, { useState, useEffect } from "react"; -import { useNavigate, useParams } from "react-router-dom"; -import { FaGlobe, FaPeopleGroup, FaCode, FaLink, FaCaretDown } from "react-icons/fa6"; - -import { AppInfo } from "../types/Apps"; +import React from "react"; +import { useParams } from "react-router-dom"; +import { FaDownload, FaSync, FaTrash, FaMagnet, FaCog, FaCheck, FaTimes } from "react-icons/fa"; import useAppsStore from "../store"; -import { PUBLISH_PATH } from "../constants/path"; -import { FaCheckCircle, FaTimesCircle } from "react-icons/fa"; +import { appId } from "../utils/app"; export default function AppPage() { - const { getApp, installApp, updateApp, uninstallApp, setMirroring, setAutoUpdate } = useAppsStore(); - const navigate = useNavigate(); + const { installApp, updateApp, uninstallApp, setMirroring, setAutoUpdate, apps } = useAppsStore(); const { id } = useParams(); - const [app, setApp] = useState(undefined); + const app = apps.find(a => appId(a) === id); - useEffect(() => { - if (id) { - getApp(id).then(setApp).catch(console.error); - } - }, [id, getApp]); + if (!app) { + return

App details not found for {id}

; + } const handleInstall = () => app && installApp(app); const handleUpdate = () => app && updateApp(app); const handleUninstall = () => app && uninstallApp(app); const handleMirror = () => app && setMirroring(app, !app.state?.mirroring); const handleAutoUpdate = () => app && setAutoUpdate(app, !app.state?.auto_update); - const handlePublish = () => navigate(PUBLISH_PATH, { state: { app } }); - - if (!app) { - return

App details not found for {id}

; - } - - const version = app.metadata?.properties?.current_version || "Unknown"; - const versions = Object.entries(app.metadata?.properties?.code_hashes || {}); - const hash = app.state?.our_version || (versions[(versions.length || 1) - 1] || ["", ""])[1]; - const mirrors = app.metadata?.properties?.mirrors || []; - - const tbaLink = `https://optimistic.etherscan.io/address/${app.tba}`; return ( -
-
-

{app.metadata?.name || app.package}

-

{app.metadata?.description || "No description available"}

-
-
-
- - Developer - {app.publisher} -
-
- - Version - {version} - {`${hash.slice(0, 5)}...${hash.slice(-5)}`} -
-
- - Mirrors - {mirrors.length} - -
- -
- Installed - {app.installed ? : } -
- {app.state && ( - <> -
- Mirrored From - {app.state.mirrored_from || "N/A"} -
-
- Capabilities Approved - {app.state.caps_approved ? : } -
-
- Mirroring - {app.state.mirroring ? : } -
-
- Auto Update - {app.state.auto_update ? : } -
-
- Verified - {app.state.verified ? : } -
- - )} +
+

{app.metadata?.name || app.package}

+

{app.metadata?.description || "No description available"}

+
+
+
    +
  • Version: {app.metadata?.properties?.current_version || "Unknown"}
  • +
  • Developer: {app.publisher}
  • +
  • Mirrors: {app.metadata?.properties?.mirrors?.length || 0}
  • +
  • + Installed: + {app.installed ? : } +
  • +
  • + Mirroring: + {app.state?.mirroring ? : } +
  • +
  • + Auto-Update: + {app.state?.auto_update ? : } +
  • +
- {app.metadata?.properties?.screenshots && ( -
- {app.metadata.properties.screenshots.map((screenshot, index) => ( - {`Screenshot - ))} -
- )}
{app.installed ? ( <> - - - - - {app.state?.mirroring && ( - - )} + + ) : ( - + )} + +
-
- ); -} - -const MirrorsDropdown = ({ mirrors }: { mirrors: string[] }) => { - const [isOpen, setIsOpen] = useState(false); - - return ( -
- - {isOpen && ( -
    - {mirrors.map((mirror, index) => ( -
  • {mirror}
  • + {app.metadata?.properties?.screenshots && ( +
    + {app.metadata.properties.screenshots.map((screenshot, index) => ( + {`Screenshot ))} -
+
)} -
+ ); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx index c073705b..a5b7ed67 100644 --- a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx @@ -39,7 +39,7 @@ export default function PublishPage() { useEffect(() => { setMyPublishedApps( - apps.filter((app) => app.owner?.toLowerCase() === address?.toLowerCase()) + apps.filter((app) => app.publisher?.toLowerCase() === window.our.node?.toLowerCase()) ); }, [apps, address]) diff --git a/kinode/packages/app_store/ui/src/pages/StorePage.tsx b/kinode/packages/app_store/ui/src/pages/StorePage.tsx index 24a227d4..933c598f 100644 --- a/kinode/packages/app_store/ui/src/pages/StorePage.tsx +++ b/kinode/packages/app_store/ui/src/pages/StorePage.tsx @@ -3,20 +3,22 @@ import useAppsStore from "../store"; import { AppInfo } from "../types/Apps"; import { appId } from '../utils/app' import { Link } from "react-router-dom"; -import { FaGlobe, FaPeopleGroup, FaCode } from "react-icons/fa6"; +import { FaGlobe, FaPeopleGroup, FaCode, FaFilter } from "react-icons/fa6"; export default function StorePage() { const { apps, getApps, rebuildIndex } = useAppsStore(); const [searchQuery, setSearchQuery] = useState(""); const [isRebuildingIndex, setIsRebuildingIndex] = useState(false); + const [showMyApps, setShowMyApps] = useState(false); useEffect(() => { getApps(); }, [getApps]); const filteredApps = apps.filter((app) => - app.package.toLowerCase().includes(searchQuery.toLowerCase()) || - app.metadata?.description?.toLowerCase().includes(searchQuery.toLowerCase()) + (app.package.toLowerCase().includes(searchQuery.toLowerCase()) || + app.metadata?.description?.toLowerCase().includes(searchQuery.toLowerCase())) && + (!showMyApps || app.installed) ); const handleRebuildIndex = async () => { @@ -44,6 +46,12 @@ export default function StorePage() { onChange={(e) => setSearchQuery(e.target.value)} />
+