From 048e72a4ae865acb886b8c2ca4bb445ff105ad7d Mon Sep 17 00:00:00 2001 From: "smx.pusha" <139338836+smxpusha@users.noreply.github.com> Date: Fri, 14 Aug 2026 19:50:50 +0200 Subject: [PATCH] FIX - preload App Store module Preload and cache the App Store route module when the phone shell starts so FiveM CEF does not fetch it only after the user opens the app. Cover the shared loader contract with a focused registry test. --- frontend/src/App.vue | 5 ++++- frontend/src/config/apps.test.ts | 10 +++++++++- frontend/src/config/apps.ts | 17 ++++++++++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 8fb380c..10e7e15 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -47,7 +47,7 @@ import { useMarketplaceStore } from '@/stores/marketplace' import { useAppCatalogStore } from '@/stores/app-catalog' import { useAppStoreStore } from '@/stores/app-store' import { useWidgetsStore } from '@/stores/widgets' -import { isPhoneAppId } from '@/config/apps' +import { isPhoneAppId, loadAppStoreComponent } from '@/config/apps' import { useNotesStore } from '@/stores/notes' import { useMemosStore } from '@/stores/memos' import { useWeatherStore } from '@/stores/weather' @@ -1223,6 +1223,9 @@ onMounted(() => { window.addEventListener('resize', updateViewportScale) systemColorScheme.addEventListener('change', onSystemColorSchemeChange) phone.setSystemDarkMode(systemColorScheme.matches) + void loadAppStoreComponent().catch((error: unknown) => { + console.error('[App Store] Could not preload the phone app.', error) + }) void nuiCall('ui:ready', { protocolVersion: 1 }) clockTicker = setInterval(() => { const now = Date.now() diff --git a/frontend/src/config/apps.test.ts b/frontend/src/config/apps.test.ts index 09fd166..6feca84 100644 --- a/frontend/src/config/apps.test.ts +++ b/frontend/src/config/apps.test.ts @@ -1,8 +1,16 @@ import { describe, expect, it } from 'vitest' import { Newspaper } from 'lucide-vue-next' -import { isPhoneAppId, PHONE_APPS } from './apps' +import { isPhoneAppId, loadAppStoreComponent, PHONE_APPS } from './apps' describe('app registry', () => { + it('reuses the App Store module loaded by the phone shell', async () => { + const firstLoad = loadAppStoreComponent() + const secondLoad = loadAppStoreComponent() + + expect(secondLoad).toBe(firstLoad) + expect((await firstLoad).default).toBeTruthy() + }) + it('has unique ids and routes with the reference dock order', () => { expect(new Set(PHONE_APPS.map((app) => app.id)).size).toBe( PHONE_APPS.length, diff --git a/frontend/src/config/apps.ts b/frontend/src/config/apps.ts index dd63476..f3a211d 100644 --- a/frontend/src/config/apps.ts +++ b/frontend/src/config/apps.ts @@ -86,6 +86,19 @@ import type { PhoneAppDefinition, } from '@/types/apps' +let appStoreComponentPromise: + | ReturnType + | null = null + +function importAppStoreComponent() { + return import('@/views/apps/AppStoreApp.vue') +} + +export function loadAppStoreComponent() { + appStoreComponentPromise ??= importAppStoreComponent() + return appStoreComponentPromise +} + export const PHONE_APPS = shallowReactive([ { category: 'social', @@ -481,9 +494,7 @@ export const PHONE_APPS = shallowReactive([ }, { category: 'utilities', - component: markRaw( - defineAsyncComponent(() => import('@/views/apps/AppStoreApp.vue')), - ), + component: markRaw(defineAsyncComponent(loadAppStoreComponent)), dockOrder: null, gridOrder: 9, icon: markRaw(ShoppingBag),