From 409bc610c82232685ed7fee1ffc0c6241b675f2f Mon Sep 17 00:00:00 2001 From: Bob Lee Date: Fri, 31 Jul 2026 02:28:55 -0700 Subject: [PATCH] feat(web-ui): add an Open action to installed MiniApp listings The market detail modal ended installed apps on a disabled "Installed" button, so there was no way to launch an app from the market and the update state was easy to miss. - show "Installed" as an inert status line and give the primary slot to a new Open action that focuses (or opens) the app's scene tab - keep Update as the primary action when a newer release exists, with Open demoted to secondary - shorten the update label to "Update" and add the "Open" string across en-US, zh-CN, and zh-TW --- .../miniapps/views/MiniAppMarketView.scss | 9 +++ .../miniapps/views/MiniAppMarketView.tsx | 67 +++++++++++++------ .../src/locales/en-US/scenes/miniapp.json | 3 +- .../src/locales/zh-CN/scenes/miniapp.json | 3 +- .../src/locales/zh-TW/scenes/miniapp.json | 3 +- 5 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.scss b/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.scss index dbfc189655..17d90514e7 100644 --- a/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.scss +++ b/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.scss @@ -159,6 +159,15 @@ } } + // Inert "installed" state that sits beside the action buttons in the modal footer. + &__installed-state { + display: inline-flex; + align-items: center; + gap: $size-gap-1; + color: var(--color-success); + font-size: var(--font-size-sm); + } + &__screenshots { display: flex; gap: $size-gap-2; diff --git a/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.tsx b/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.tsx index 24b22a8250..14c630d2ad 100644 --- a/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.tsx +++ b/src/web-ui/src/app/scenes/miniapps/views/MiniAppMarketView.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { AlertTriangle, + ArrowUpRight, Check, Download, ExternalLink, @@ -89,6 +90,18 @@ const MiniAppMarketView: React.FC = () => { const openTabIds = useMemo(() => new Set(openTabs.map((tab) => tab.id)), [openTabs]); + /** Focuses the installed MiniApp's scene tab, opening it when absent. */ + const openInstalledApp = useCallback((appId: string) => { + setDetail(undefined); + setInstalled(null); + const tabId: SceneTabId = `miniapp:${appId}`; + if (openTabIds.has(tabId)) { + activateScene(tabId); + } else { + openScene(tabId); + } + }, [activateScene, openScene, openTabIds]); + const loadCatalog = useCallback(async (append = false) => { if (append) { setLoadingMore(true); @@ -241,17 +254,10 @@ const MiniAppMarketView: React.FC = () => { confirmOverwrite: Boolean(installed?.localOverride), }); upsertApp(result.app); - setDetail(undefined); - setInstalled(null); notification.success( t(result.updated ? 'market.messages.updated' : 'market.messages.installed'), ); - const tabId: SceneTabId = `miniapp:${result.app.id}`; - if (openTabIds.has(tabId)) { - activateScene(tabId); - } else { - openScene(tabId); - } + openInstalledApp(result.app.id); } catch (installError) { notification.error(t('market.messages.installFailed', { error: String(installError) })); } finally { @@ -283,11 +289,8 @@ const MiniAppMarketView: React.FC = () => { && requiresWorkspace(detail.permissions), ); const canUpdate = Boolean(installed && detail && detail.latestRelease > installed.origin.releaseNumber); - const installLabel = installed - ? canUpdate - ? t('market.detail.update') - : t('market.detail.installed') - : t('market.detail.install'); + // The install button only renders for the actionable states: fresh install or update. + const installLabel = canUpdate ? t('market.detail.update') : t('market.detail.install'); const detailName = detail ? pickLocalizedString(detail, currentLanguage, 'name') : ''; @@ -472,15 +475,35 @@ const MiniAppMarketView: React.FC = () => { {formatNumber(detail.favoriteCount)} - + {/* Installed and up to date: nothing left to install, so say so and let "Open" lead. */} + {installed && !canUpdate ? ( + + + {t('market.detail.installed')} + + ) : null} + {installed ? ( + + ) : null} + {!installed || canUpdate ? ( + + ) : null} ) : null} > diff --git a/src/web-ui/src/locales/en-US/scenes/miniapp.json b/src/web-ui/src/locales/en-US/scenes/miniapp.json index c83263d504..2d0adaf5aa 100644 --- a/src/web-ui/src/locales/en-US/scenes/miniapp.json +++ b/src/web-ui/src/locales/en-US/scenes/miniapp.json @@ -72,8 +72,9 @@ "reviewed": "Human reviewed", "by": "By @{{owner}}", "install": "Install", - "update": "Update manually", + "update": "Update", "installed": "Installed", + "open": "Open", "permissions": "Install permissions", "noPermissions": "No additional host permissions", "rating": "Rating", diff --git a/src/web-ui/src/locales/zh-CN/scenes/miniapp.json b/src/web-ui/src/locales/zh-CN/scenes/miniapp.json index 806b533b79..baaa76abf1 100644 --- a/src/web-ui/src/locales/zh-CN/scenes/miniapp.json +++ b/src/web-ui/src/locales/zh-CN/scenes/miniapp.json @@ -72,8 +72,9 @@ "reviewed": "人工审核", "by": "作者 @{{owner}}", "install": "安装", - "update": "手动更新", + "update": "更新", "installed": "已安装", + "open": "打开", "permissions": "安装权限", "noPermissions": "不需要额外宿主权限", "rating": "评分", diff --git a/src/web-ui/src/locales/zh-TW/scenes/miniapp.json b/src/web-ui/src/locales/zh-TW/scenes/miniapp.json index 4056266953..d9038f0d01 100644 --- a/src/web-ui/src/locales/zh-TW/scenes/miniapp.json +++ b/src/web-ui/src/locales/zh-TW/scenes/miniapp.json @@ -72,8 +72,9 @@ "reviewed": "人工審核", "by": "作者 @{{owner}}", "install": "安裝", - "update": "手動更新", + "update": "更新", "installed": "已安裝", + "open": "開啟", "permissions": "安裝權限", "noPermissions": "不需要額外宿主權限", "rating": "評分",