fix(mobile): improve app store search and launches

This commit is contained in:
archipelago
2026-05-19 18:29:04 -04:00
parent 3e01e57c8d
commit 1836b035b4
12 changed files with 138 additions and 27 deletions
+56 -1
View File
@@ -106,10 +106,34 @@
</div>
<!-- No Results -->
<div v-if="filteredPackageEntries.length === 0 && searchQuery" class="text-center py-12">
<div v-if="filteredPackageEntries.length === 0 && marketplaceMatches.length === 0 && searchQuery" class="text-center py-12">
<p class="text-white/70">{{ t('apps.noResults', { query: searchQuery }) }}</p>
</div>
<div v-if="marketplaceMatches.length > 0" class="mb-5">
<div class="flex items-center gap-3 mb-3">
<span class="discover-terminal-tag">app store</span>
<h2 class="text-lg font-bold text-white">Available in Discover</h2>
<div class="flex-1 h-px bg-white/10"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<button
v-for="app in marketplaceMatches"
:key="app.id"
type="button"
class="glass-card p-4 text-left flex items-center gap-3 hover:bg-orange-500/5 hover:border-orange-500/15 transition-colors"
@click="openMarketplaceResult(app)"
>
<img v-if="app.icon" :src="app.icon" :alt="app.title" class="w-12 h-12 rounded-xl object-cover bg-white/10" />
<div v-else class="w-12 h-12 rounded-xl bg-white/10 flex-shrink-0"></div>
<div class="min-w-0 flex-1">
<p class="font-semibold text-white truncate">{{ app.title }}</p>
<p class="text-xs text-white/50 truncate">Available in App Store</p>
</div>
</button>
</div>
</div>
<!-- Mobile: iPhone-style icon grid -->
<div class="md:hidden">
<AppIconGrid
@@ -236,10 +260,12 @@ import AppCard from './apps/AppCard.vue'
import AppIconGrid from './apps/AppIconGrid.vue'
import AppsUninstallModal from './apps/AppsUninstallModal.vue'
import { useAppsActions } from './apps/useAppsActions'
import { useMarketplaceApp } from '@/composables/useMarketplaceApp'
import {
type AppsTab, filterEntriesForTab, isWebOnlyApp, isWebsitePackage, opensInTab, resolveRuntimeLaunchUrl,
WEB_ONLY_APPS, WEB_ONLY_APP_URLS, buildAllCategories, useCategoriesWithApps,
} from './apps/appsConfig'
import { getCuratedAppList, INSTALLED_ALIASES, type MarketplaceApp } from './marketplace/marketplaceData'
const { t } = useI18n()
const router = useRouter()
@@ -247,6 +273,7 @@ const route = useRoute()
const store = useAppStore()
const serverStore = useServerStore()
const actions = useAppsActions()
const { setCurrentApp } = useMarketplaceApp()
const showSideload = ref(false)
const sideloading = ref(false)
const sideloadError = ref('')
@@ -266,6 +293,10 @@ const activeTab = ref<AppsTab>(
route.query.tab === 'websites' || route.query.tab === 'services' ? 'websites' : 'apps'
)
watch(() => route.query.tab, (tab) => {
activeTab.value = tab === 'websites' || tab === 'services' ? 'websites' : 'apps'
})
// Search (debounced)
const searchQuery = ref('')
const debouncedSearchQuery = ref('')
@@ -309,6 +340,19 @@ const packages = computed(() => {
const categoriesWithApps = useCategoriesWithApps(packages, ALL_CATEGORIES)
const curatedApps = getCuratedAppList()
const marketplaceMatches = computed(() => {
const q = debouncedSearchQuery.value.trim().toLowerCase()
if (!q || activeTab.value !== 'apps') return [] as MarketplaceApp[]
return curatedApps.filter(app => {
if (isInstalledInMyApps(app.id)) return false
return app.title?.toLowerCase().includes(q) ||
app.id.toLowerCase().includes(q) ||
app.author?.toLowerCase().includes(q) ||
(typeof app.description === 'string' && app.description.toLowerCase().includes(q))
}).slice(0, 6)
})
const isLoadingApps = computed(() => !store.hasLoadedInitialData && !connectionError.value)
// Connection error state
@@ -352,6 +396,17 @@ const filteredPackageEntries = computed(() => {
)
})
function isInstalledInMyApps(appId: string): boolean {
if (appId in packages.value) return true
const aliases = INSTALLED_ALIASES[appId]
return aliases ? aliases.some(alias => alias in packages.value) : false
}
function openMarketplaceResult(app: MarketplaceApp) {
setCurrentApp(app)
router.push({ name: 'marketplace-app-detail', params: { id: app.id }, query: { from: 'apps' } }).catch(() => {})
}
// Uninstall modal
const uninstallModal = ref({ show: false, appId: '', appTitle: '' })