From b9bf5dd7ef31c25eb4af9785a34e024a792edfe7 Mon Sep 17 00:00:00 2001 From: DerEchteAlec Date: Thu, 13 Aug 2026 19:45:02 +0200 Subject: [PATCH] FIX - migrate Music navigation to full width --- .../apps/MusicApp.navigation.contract.test.ts | 73 ++++++++++ frontend/src/views/apps/MusicApp.vue | 130 ++++++++++-------- 2 files changed, 146 insertions(+), 57 deletions(-) create mode 100644 frontend/src/views/apps/MusicApp.navigation.contract.test.ts diff --git a/frontend/src/views/apps/MusicApp.navigation.contract.test.ts b/frontend/src/views/apps/MusicApp.navigation.contract.test.ts new file mode 100644 index 0000000..98016b5 --- /dev/null +++ b/frontend/src/views/apps/MusicApp.navigation.contract.test.ts @@ -0,0 +1,73 @@ +import { readFileSync } from 'node:fs' + +import { describe, expect, it } from 'vitest' + +const source = readFileSync(new URL('./MusicApp.vue', import.meta.url), 'utf8') +const navigationSource = source.slice( + source.indexOf('') + ''.length, +) + +describe('MusicApp Sky pill navigation contract', () => { + it('uses the full-width sliding Glass navigation outside playlists', () => { + expect(source).not.toContain('kTabbar') + expect(source).not.toContain('kTabbarLink') + expect(source).not.toContain('kToolbarPane') + expect(source).not.toContain(' { + expect(source).toContain("{ id: 'library', icon: Library }") + expect(source).toContain("{ id: 'playlists', icon: ListMusic }") + expect(source).toContain("{ id: 'search', icon: Search }") + expect(navigationSource).toContain(':active="activeTab === item.id"') + expect(navigationSource).toContain( + ':aria-label="phone.t(`Apps.music.tabs.${item.id}`)"', + ) + expect(navigationSource).toContain('@click="selectTab(item.id)"') + expect(navigationSource).toContain(':size="20"') + expect(source).toMatch( + /function selectTab\(tab: MusicTab\): void \{\s+activeTab\.value = tab\s+activePlaylist\.value = null\s+scrollToTop\(\)\s+\}/, + ) + }) + + it('reserves the absolute navigation and mini-player without changing playlist space', () => { + expect(source).toMatch( + /\.music-scroll\s*\{[^}]*padding:\s*8px 0\s*calc\(\s*var\(--sky-safe-area-bottom\) \+ var\(--sky-tabbar-height\) \+\s*var\(--sky-space-3\)\s*\)/s, + ) + expect(source).toMatch( + /\.music-app--playing \.music-scroll\s*\{[^}]*padding-bottom:\s*calc\(\s*var\(--music-mini-player-bottom\) \+ var\(--music-mini-player-height\) \+\s*var\(--sky-space-3\)\s*\)/s, + ) + expect(source).toContain( + 'var(--sky-safe-area-bottom) + var(--sky-tabbar-height) + var(--sky-space-2)', + ) + expect(source).toContain('--music-mini-player-height: 58px') + expect(source).toMatch( + /\.music-app--playlist \.music-scroll\s*\{[^}]*padding-bottom:\s*42px/s, + ) + expect(source).toMatch( + /\.music-app--playlist\.music-app--playing \.music-scroll\s*\{[^}]*padding-bottom:\s*100px/s, + ) + }) + + it('bridges the Music accent and light-dark theme into Sky UI', () => { + expect(source).toContain('class="music-app sky-ui-provider"') + expect(source).toContain("'sky-ui-provider--dark': phone.isDarkMode") + expect(source).toContain('--sky-app-accent: var(--music-accent)') + expect(source).toContain('class="music-navigation__item"') + expect(source).toMatch( + /\.music-navigation__item > span:last-child\s*\{[^}]*text-overflow:\s*ellipsis[^}]*white-space:\s*nowrap/s, + ) + expect(source).not.toContain('.music-tabbar') + }) +}) diff --git a/frontend/src/views/apps/MusicApp.vue b/frontend/src/views/apps/MusicApp.vue index 5de0de9..0ca455a 100644 --- a/frontend/src/views/apps/MusicApp.vue +++ b/frontend/src/views/apps/MusicApp.vue @@ -6,7 +6,6 @@ import { kDialog, kDialogButton, kGlass, - kIcon, kLink, kList, kListButton, @@ -19,10 +18,7 @@ import { kRange, kSearchbar, kSheet, - kTabbar, - kTabbarLink, kToast, - kToolbarPane, } from 'konsta/vue' import { Check, @@ -55,6 +51,7 @@ import type { MusicPlaylist, MusicTrack } from '@/types/music' import { easyShareMusicTarget } from '@/utils/easyshare' import { consumeEscape, handleEnterAction } from '@/utils/keyboard' import { musicEscapeLayer } from '@/utils/musicEscape' +import { SkyPillNavigation, SkySegmented, SkySegmentedButton } from '@/ui' type MusicTab = 'library' | 'playlists' | 'search' type MusicSheet = @@ -75,6 +72,14 @@ const easyShare = useEasyShareStore() const phone = usePhoneStore() const route = useRoute() const activeTab = ref('library') +const tabs = [ + { id: 'library', icon: Library }, + { id: 'playlists', icon: ListMusic }, + { id: 'search', icon: Search }, +] as const +const activeTabIndex = computed(() => + tabs.findIndex((item) => item.id === activeTab.value), +) const activePlaylist = ref(null) const addMenuOpened = ref(false) const actionMenuOpened = ref(false) @@ -566,10 +571,11 @@ onBeforeUnmount(() => {