From 4b9a1c4d5cf3d60c19a057d4b1140063f99a4aed Mon Sep 17 00:00:00 2001 From: Abeautifulsnow Date: Thu, 24 Sep 2026 19:16:02 +0800 Subject: [PATCH] feat(plugins): add marketplace sort modes with release dates --- .../plugins/PluginContributionsPanel.vue | 33 +++- .../PluginContributionsPanelBatch.spec.ts | 71 ++++++- .../i18n/__tests__/featureMessages.spec.ts | 1 + apps/desktop/src/i18n/locales/en.ts | 5 + apps/desktop/src/i18n/locales/es.ts | 5 + apps/desktop/src/i18n/locales/it.ts | 5 + apps/desktop/src/i18n/locales/ja.ts | 5 + apps/desktop/src/i18n/locales/ko.ts | 5 + apps/desktop/src/i18n/locales/pt-BR.ts | 5 + apps/desktop/src/i18n/locales/ru.ts | 5 + apps/desktop/src/i18n/locales/zh-CN.ts | 5 + apps/desktop/src/i18n/locales/zh-TW.ts | 5 + .../src/lib/plugins/pluginMarketplace.spec.ts | 176 +++++++++++++++++- .../src/lib/plugins/pluginMarketplace.ts | 53 ++++++ 14 files changed, 373 insertions(+), 6 deletions(-) diff --git a/apps/desktop/src/components/plugins/PluginContributionsPanel.vue b/apps/desktop/src/components/plugins/PluginContributionsPanel.vue index e9a74ec61c..38807097af 100644 --- a/apps/desktop/src/components/plugins/PluginContributionsPanel.vue +++ b/apps/desktop/src/components/plugins/PluginContributionsPanel.vue @@ -26,11 +26,14 @@ import { buildInstalledUpdateIndex, buildMarketplacePluginListings, filterMarketplacePluginListings, + formatMarketplaceReleasedDate, listingRepositoryCanVerify, marketplaceHomepageUrl, pluginSourceChange, + sortMarketplacePluginListings, type InstalledPluginUpdateEntry, type MarketplacePluginListing, + type MarketplacePluginSortMode, type PluginSourceChange, } from "@/lib/plugins/pluginMarketplace"; import { isBatchSelectableListing, runBatch } from "@/lib/plugins/pluginBatch"; @@ -70,6 +73,7 @@ const GithubIcon = defineComponent({ const PLUGIN_ALLOW_UNSIGNED_STORAGE_KEY = "dbx-plugin-allow-unsigned"; const MARKETPLACE_VIEW_MODE_STORAGE_KEY = "dbx-plugin-marketplace-view-mode"; +const MARKETPLACE_SORT_MODE_STORAGE_KEY = "dbx-plugin-marketplace-sort-mode"; type TauriFileDropPayload = { type: "enter"; paths: string[]; position: { x: number; y: number } } | { type: "over"; position: { x: number; y: number } } | { type: "drop"; paths: string[]; position: { x: number; y: number } } | { type: "leave" }; @@ -113,6 +117,15 @@ const repositoryCatalogUrl = ref(""); const marketplaceQuery = ref(""); const marketplaceRepositoryId = ref("all"); const marketplaceViewMode = ref<"grid" | "list">(safeLocalStorageGet(MARKETPLACE_VIEW_MODE_STORAGE_KEY) === "list" ? "list" : "grid"); +const MARKETPLACE_SORT_MODES: MarketplacePluginSortMode[] = ["name", "recently-updated", "recently-listed", "updates-first"]; +// "name" stays the default: the batch specs select listings positionally, so a persisted +// value must be re-validated against the known modes before it may change the order. +const marketplaceSortMode = ref( + ((): MarketplacePluginSortMode => { + const stored = safeLocalStorageGet(MARKETPLACE_SORT_MODE_STORAGE_KEY) as MarketplacePluginSortMode; + return MARKETPLACE_SORT_MODES.includes(stored) ? stored : "name"; + })(), +); const webFileInput = ref(null); const panelRootRef = ref(null); const draggingPackage = ref(false); @@ -148,6 +161,8 @@ const providerConnections = computed(() => { const selectedConnection = computed(() => providerConnections.value.find((connection) => connection.id === selectedConnectionId.value)); const marketplaceListings = computed(() => buildMarketplacePluginListings(catalogResults.value, installedPlugins.value, appLocale.value)); const filteredMarketplaceListings = computed(() => filterMarketplacePluginListings(marketplaceListings.value, marketplaceQuery.value, marketplaceRepositoryId.value)); +// Render order only: batch selection and update execution keep the builder's name order. +const sortedMarketplaceListings = computed(() => sortMarketplacePluginListings(filteredMarketplaceListings.value, marketplaceSortMode.value)); const batchUpdatableListings = computed(() => filteredMarketplaceListings.value.filter((listing) => listing.status === "update")); const batchSelectedListings = computed(() => filteredMarketplaceListings.value.filter((listing) => isBatchSelectableListing(listing.status) && selectedListingKeys.value.has(listing.key))); const batchSelectedInstalled = computed(() => definitions.value.filter((definition) => selectedInstalledIds.value.has(definition.plugin.manifest.id))); @@ -898,6 +913,7 @@ onMounted(() => { if (isTauriRuntime()) document.addEventListener("dbx:tauri-file-drop", onTauriPluginDrop); }); watch(marketplaceViewMode, (mode) => safeLocalStorageSet(MARKETPLACE_VIEW_MODE_STORAGE_KEY, mode)); +watch(marketplaceSortMode, (mode) => safeLocalStorageSet(MARKETPLACE_SORT_MODE_STORAGE_KEY, mode)); onBeforeUnmount(() => { window.removeEventListener(COMPONENT_PLUGINS_UPDATED_EVENT, handleComponentPluginsUpdated); if (isTauriRuntime()) document.removeEventListener("dbx:tauri-file-drop", onTauriPluginDrop); @@ -934,6 +950,15 @@ onBeforeUnmount(() => {
+