Retain media sessions admitted after installed app state arrives

This commit is contained in:
archipelago
2026-10-07 02:20:49 -04:00
parent fdc596854e
commit 49232fd547
6 changed files with 80 additions and 7 deletions
@@ -141,6 +141,30 @@ describe('useAppLauncherStore', () => {
})
})
it.each(['active', 'closed', 'superseded', 'reopened'])('binds late media retention to the original launch: %s', state => {
const id = 'late-media-fixture'
__setSignedCatalogForTests({apps:{[id]:{version:'1',manifest:{app:{id,metadata:{launch:{media_controls:'archipelago-v1'}}}}}}} as never)
const app = useAppStore()
app.data = {'package-data':{}} as never
const launcher = useAppLauncherStore()
launcher.openSession(id, {skipCredentialPrompt:true})
expect(launcher.panelAppId).toBe(id)
const generation = launcher.mediaSessionGeneration(id)
expect(launcher.mediaAppId).toBeNull()
app.data = {'package-data':{[id]:{state:'running','ui-ready':true,manifest:{id,version:'1'}}}} as never
if (state === 'closed' || state === 'reopened') launcher.closePanel()
if (state === 'superseded') launcher.openSession('mempool', {skipCredentialPrompt:true})
if (state === 'reopened') { launcher.openSession(id, {skipCredentialPrompt:true}); launcher.mediaAppId=null }
expect(launcher.retainMediaSession(id,'1',generation)).toBe(state === 'active')
expect(launcher.mediaAppId).toBe(state === 'active' ? id : null)
if (state === 'active') {
launcher.closePanel()
expect(launcher.mediaAppId).toBe(id)
launcher.openSession(id)
expect(launcher.panelAppId).toBe(id)
}
})
it.each(['close', 'another-app', 'none'])('waits for node demo launch policy, cancellation: %s', async cancel => {
const id = `node-demo-delayed-${cancel}`
const previousFetch = globalThis.fetch
+16
View File
@@ -227,6 +227,20 @@ export const useAppLauncherStore = defineStore('appLauncher', () => {
let launchGeneration = 0
let mediaVersion: string | null = null
/** Capture the active launch, so a late frame callback cannot retain a
* closed session or a newer launch of the same app. */
function mediaSessionGeneration(appId: string): number | null {
return panelAppId.value === appId ? launchGeneration : null
}
function retainMediaSession(appId: string, version: string, generation: number | null): boolean {
const installed = useAppStore().data?.['package-data']?.[appId]
if (generation === null || generation !== launchGeneration || panelAppId.value !== appId
|| installed?.manifest.version !== version || !appHasMediaBridge(appId, version)) return false
mediaAppId.value = appId
mediaVersion = version
return true
}
function openSessionNow(appId: string, opts: LaunchOptions = {}) {
const pkg = useAppStore().data?.['package-data']?.[appId]
if (pkg?.['ui-ready'] === false) {
@@ -632,6 +646,8 @@ export const useAppLauncherStore = defineStore('appLauncher', () => {
closePanel,
panelAppId,
mediaAppId,
mediaSessionGeneration,
retainMediaSession,
panelPath,
credentialPrompt,
cancelCredentialLaunch,