diff --git a/neode-ui/src/views/Dashboard.vue b/neode-ui/src/views/Dashboard.vue index cb1b6d0f..e9822c84 100644 --- a/neode-ui/src/views/Dashboard.vue +++ b/neode-ui/src/views/Dashboard.vue @@ -91,8 +91,23 @@ - - + + +
+ +
+
+ +
+ +
+
@@ -112,7 +127,7 @@ import { useRouter, useRoute } from 'vue-router' import { useAppStore } from '../stores/app' import { useAppLauncherStore } from '../stores/appLauncher' import { appConfirm } from '@/composables/useAppConfirm' -import DashboardAppSessions from '@/views/dashboard/DashboardAppSessions.vue' +import AppSession from '@/views/AppSession.vue' import { useLoginTransitionStore } from '../stores/loginTransition' import { playDashboardLoadOomph } from '@/composables/useLoginSounds' diff --git a/neode-ui/src/views/dashboard/DashboardAppSessions.vue b/neode-ui/src/views/dashboard/DashboardAppSessions.vue deleted file mode 100644 index dbd982cd..00000000 --- a/neode-ui/src/views/dashboard/DashboardAppSessions.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/neode-ui/src/views/dashboard/__tests__/DashboardAppSessions.test.ts b/neode-ui/src/views/dashboard/__tests__/DashboardAppSessions.test.ts deleted file mode 100644 index 710a2da5..00000000 --- a/neode-ui/src/views/dashboard/__tests__/DashboardAppSessions.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { mount } from '@vue/test-utils' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { defineComponent, h, nextTick, onMounted, onUnmounted } from 'vue' -vi.mock('@/stores/appLauncher', async () => { - const {reactive} = await import('vue') - const state = reactive({panelAppId:null as string|null, mediaAppId:null as string|null, panelPath:null as string|null, - closePanel() {state.panelAppId=null;state.panelPath=null}}) - return {useAppLauncherStore:()=>state} -}) -import { useAppLauncherStore } from '@/stores/appLauncher' -import DashboardAppSessions from '../DashboardAppSessions.vue' -describe('stable dashboard app frame ownership', () => { - const mounted=vi.fn(), unmounted=vi.fn() - const Session=defineComponent({props:{appIdProp:String,pathProp:String,suspended:Boolean},setup(props) { - onMounted(()=>mounted(props.appIdProp));onUnmounted(()=>unmounted(props.appIdProp)) - return ()=>h('iframe',{'data-app':props.appIdProp,src:props.pathProp ?? '/browse','data-suspended':String(props.suspended)}) - }}) - beforeEach(()=> {vi.clearAllMocks();const store=useAppLauncherStore();store.panelAppId=null;store.mediaAppId=null;store.panelPath=null}) - it('keeps the exact playing frame and deep link through late promotion, close and reopen',async()=>{ - const store=useAppLauncherStore();store.panelAppId='music';store.panelPath='/browse/album/one' - const wrapper=mount(DashboardAppSessions,{global:{stubs:{AppSession:Session,TransitionGroup:false}}}) - const frame=wrapper.get('iframe').element as HTMLIFrameElement - frame.dataset.playbackPosition='17' - store.mediaAppId='music';await nextTick() - expect(wrapper.get('iframe').element).toBe(frame) - expect(mounted).toHaveBeenCalledTimes(1);expect(unmounted).not.toHaveBeenCalled() - store.closePanel();await nextTick() - expect(wrapper.get('iframe').element).toBe(frame) - expect(frame.getAttribute('src')).toBe('/browse/album/one') - expect(frame.dataset.suspended).toBe('true') - store.panelAppId='music';await nextTick() - expect(wrapper.get('iframe').element).toBe(frame) - expect(frame.dataset.playbackPosition).toBe('17') - expect(frame.getAttribute('src')).toBe('/browse/album/one') - expect(frame.dataset.suspended).toBe('false') - expect(unmounted).not.toHaveBeenCalled() - wrapper.unmount();expect(unmounted).toHaveBeenCalledOnce() - }) - it('keeps retained music while another app opens and promotes that app without recreating it',async()=>{ - const store=useAppLauncherStore();store.panelAppId='music';store.panelPath='/first' - const wrapper=mount(DashboardAppSessions,{global:{stubs:{AppSession:Session,TransitionGroup:false}}}) - const music=wrapper.get('iframe[data-app="music"]').element - store.mediaAppId='music';await nextTick() - store.panelPath='/station/two';store.panelAppId='radio';await nextTick() - const radio=wrapper.get('iframe[data-app="radio"]').element - expect(wrapper.get('iframe[data-app="music"]').element).toBe(music) - expect(music.getAttribute('src')).toBe('/first') - store.mediaAppId='radio';await nextTick() - expect(wrapper.get('iframe[data-app="radio"]').element).toBe(radio) - expect(mounted.mock.calls.map(call=>call[0])).toEqual(['music','radio']) - expect(unmounted).toHaveBeenCalledWith('music') - expect(unmounted).not.toHaveBeenCalledWith('radio') - wrapper.unmount() - }) -})