From 7572d6db98587b1c15453cf65e1d8f4456f1ddab Mon Sep 17 00:00:00 2001 From: Bob Lee Date: Thu, 30 Jul 2026 22:13:51 -0700 Subject: [PATCH] fix(miniapp): default market submissions to client version --- .../implementations/miniapp_publish_tool.rs | 32 +++++++++------ .../miniapps/views/MiniAppSubmissionsView.tsx | 40 ++++++++++++------- .../views/miniAppSubmissionDraft.test.ts | 25 ++++++++++++ .../miniapps/views/miniAppSubmissionDraft.ts | 29 ++++++++++++++ 4 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.test.ts create mode 100644 src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.ts diff --git a/src/crates/assembly/core/src/agentic/tools/implementations/miniapp_publish_tool.rs b/src/crates/assembly/core/src/agentic/tools/implementations/miniapp_publish_tool.rs index a8ff936233..e4fe626306 100644 --- a/src/crates/assembly/core/src/agentic/tools/implementations/miniapp_publish_tool.rs +++ b/src/crates/assembly/core/src/agentic/tools/implementations/miniapp_publish_tool.rs @@ -17,9 +17,12 @@ use bitfun_services_integrations::miniapp_market::{ use serde_json::{json, Value}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -const DEFAULT_MIN_BITFUN_VERSION: &str = "0.1.0"; const DEFAULT_LICENSE: &str = "MIT"; +fn default_min_bitfun_version() -> &'static str { + crate::VERSION +} + pub struct PublishMiniAppTool; impl PublishMiniAppTool { @@ -161,10 +164,9 @@ Publishing is an outward-facing action: only call this when the user explicitly } } } else if let Some(app_name) = app_name { - let apps = manager - .list() - .await - .map_err(|e| BitFunError::tool(format!("Could not list installed MiniApps: {e}")))?; + let apps = manager.list().await.map_err(|e| { + BitFunError::tool(format!("Could not list installed MiniApps: {e}")) + })?; let matches = find_apps_by_name(&apps, app_name); match matches.as_slice() { [only] => manager @@ -365,7 +367,7 @@ Publishing is an outward-facing action: only call this when the user explicitly icon: app.icon.clone(), category, tags, - min_bitfun_version: DEFAULT_MIN_BITFUN_VERSION.to_string(), + min_bitfun_version: default_min_bitfun_version().to_string(), changelog, license: MarketLicense { spdx_expression: Some(DEFAULT_LICENSE.to_string()), @@ -462,7 +464,11 @@ fn find_apps_by_name<'a>(apps: &'a [MiniAppMeta], needle: &str) -> Vec<&'a MiniA return exact; } apps.iter() - .filter(|meta| display_names(meta).iter().any(|name| name.contains(&needle))) + .filter(|meta| { + display_names(meta) + .iter() + .any(|name| name.contains(&needle)) + }) .collect() } @@ -488,7 +494,7 @@ fn unix_now() -> i64 { #[cfg(test)] mod tests { - use super::{find_apps_by_name, PublishMiniAppTool}; + use super::{default_min_bitfun_version, find_apps_by_name, PublishMiniAppTool}; use crate::agentic::tools::framework::{Tool, ToolExposure, ToolUseContext}; use bitfun_product_domains::miniapp::types::MiniAppMeta; use serde_json::json; @@ -499,6 +505,11 @@ mod tests { assert_eq!(tool.default_exposure(), ToolExposure::Direct); } + #[test] + fn publish_miniapp_defaults_to_current_client_version() { + assert_eq!(default_min_bitfun_version(), crate::VERSION); + } + #[test] fn publish_miniapp_emits_stable_permission_identity() { let tool = PublishMiniAppTool::new(); @@ -584,10 +595,7 @@ mod tests { #[test] fn find_apps_by_name_falls_back_to_substring_and_reports_ambiguity() { - let apps = vec![ - meta("a1", "循天问命", None), - meta("a2", "问命笺", None), - ]; + let apps = vec![meta("a1", "循天问命", None), meta("a2", "问命笺", None)]; let partial = find_apps_by_name(&apps, "问命"); assert_eq!(partial.len(), 2); assert!(find_apps_by_name(&apps, "不存在").is_empty()); diff --git a/src/web-ui/src/app/scenes/miniapps/views/MiniAppSubmissionsView.tsx b/src/web-ui/src/app/scenes/miniapps/views/MiniAppSubmissionsView.tsx index 27df562418..ab5827822d 100644 --- a/src/web-ui/src/app/scenes/miniapps/views/MiniAppSubmissionsView.tsx +++ b/src/web-ui/src/app/scenes/miniapps/views/MiniAppSubmissionsView.tsx @@ -34,6 +34,10 @@ import { createLogger } from '@/shared/utils/logger'; import { useSceneManager } from '@/app/hooks/useSceneManager'; import type { SceneTabId } from '@/app/components/SceneBar/types'; import { renderMiniAppIcon } from '../utils/miniAppIcons'; +import { + applyCurrentClientVersionDefault, + createEmptyMarketSubmissionDraft, +} from './miniAppSubmissionDraft'; import './MiniAppSubmissionsView.scss'; const log = createLogger('MiniAppSubmissionsView'); @@ -49,18 +53,14 @@ const MARKET_CATEGORIES = [ 'other', ] as const; -const emptyDraft: MarketSubmissionDraftRequest = { - slug: '', - releaseNumber: 1, - name: '', - description: '', - icon: 'box', - category: 'other', - tags: [], - minBitfunVersion: '0.1.0', - changelog: '', - license: { spdxExpression: 'MIT' }, -}; +async function loadCurrentClientVersion(): Promise { + try { + return await systemAPI.getAppVersion(); + } catch (error) { + log.warn('Failed to load current BitFun version for MiniApp submission defaults', error); + return undefined; + } +} const MiniAppSubmissionsView: React.FC = () => { const { t } = useI18n('scenes/miniapp'); @@ -73,7 +73,9 @@ const MiniAppSubmissionsView: React.FC = () => { const [apps, setApps] = useState([]); const [submissions, setSubmissions] = useState([]); const [selectedAppId, setSelectedAppId] = useState(''); - const [draft, setDraft] = useState(emptyDraft); + const [draft, setDraft] = useState( + createEmptyMarketSubmissionDraft, + ); const [screenshotPaths, setScreenshotPaths] = useState([]); const [showAdvanced, setShowAdvanced] = useState(false); const [busy, setBusy] = useState(false); @@ -89,10 +91,18 @@ const MiniAppSubmissionsView: React.FC = () => { const refresh = async () => { setLoading(true); try { - const profile = await miniAppMarketAPI.me(); + const [profile, installed, currentClientVersion] = await Promise.all([ + miniAppMarketAPI.me(), + miniAppAPI.listMiniApps(), + draft.minBitfunVersion ? Promise.resolve(undefined) : loadCurrentClientVersion(), + ]); setMe(profile); - const installed = await miniAppAPI.listMiniApps(); setApps(installed); + if (currentClientVersion) { + setDraft((current) => + applyCurrentClientVersionDefault(current, currentClientVersion), + ); + } if (!selectedAppId && installed[0]) { selectApp(installed[0]); } diff --git a/src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.test.ts b/src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.test.ts new file mode 100644 index 0000000000..294d86700c --- /dev/null +++ b/src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from 'vitest'; +import { + applyCurrentClientVersionDefault, + createEmptyMarketSubmissionDraft, +} from './miniAppSubmissionDraft'; + +describe('MiniApp market submission defaults', () => { + it('uses the current client version for a new draft', () => { + const draft = applyCurrentClientVersionDefault( + createEmptyMarketSubmissionDraft(), + '0.2.15', + ); + + expect(draft.minBitfunVersion).toBe('0.2.15'); + }); + + it('preserves a minimum version chosen by the user', () => { + const draft = { + ...createEmptyMarketSubmissionDraft(), + minBitfunVersion: '0.2.10', + }; + + expect(applyCurrentClientVersionDefault(draft, '0.2.15')).toBe(draft); + }); +}); diff --git a/src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.ts b/src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.ts new file mode 100644 index 0000000000..41f63ba248 --- /dev/null +++ b/src/web-ui/src/app/scenes/miniapps/views/miniAppSubmissionDraft.ts @@ -0,0 +1,29 @@ +import type { MarketSubmissionDraftRequest } from '@/infrastructure/api/service-api/MiniAppMarketAPI'; + +export function createEmptyMarketSubmissionDraft(): MarketSubmissionDraftRequest { + return { + slug: '', + releaseNumber: 1, + name: '', + description: '', + icon: 'box', + category: 'other', + tags: [], + minBitfunVersion: '', + changelog: '', + license: { spdxExpression: 'MIT' }, + }; +} + +export function applyCurrentClientVersionDefault( + draft: MarketSubmissionDraftRequest, + currentClientVersion: string, +): MarketSubmissionDraftRequest { + if (draft.minBitfunVersion.trim() || !currentClientVersion.trim()) { + return draft; + } + return { + ...draft, + minBitfunVersion: currentClientVersion.trim(), + }; +}