From 0c44939288eddc7c10e103a511ee52c8172782e9 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 14 Oct 2024 15:19:59 +0100 Subject: [PATCH] feat: Implement disc numbers when viewing an album (#1845) --- .../assets/js/components/song/SongList.vue | 47 ++++++++++- .../js/components/song/SongListItem.spec.ts | 14 +++- .../js/components/song/SongListItem.vue | 81 +++++++++++-------- .../__snapshots__/SongListItem.spec.ts.snap | 9 ++- 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/resources/assets/js/components/song/SongList.vue b/resources/assets/js/components/song/SongList.vue index 73e6d9e4..ccca03c6 100644 --- a/resources/assets/js/components/song/SongList.vue +++ b/resources/assets/js/components/song/SongList.vue @@ -85,7 +85,7 @@ { await playbackService.play(playable) } +const discIndexMap = computed(() => { + const map: { [key: number]: number } = {} + rows.value.forEach((row, index) => { + const { disc } = row.playable + if (!Object.values(map).includes(disc)) { + map[index] = disc + } + }) + + return map +}) + +const noOrOneDiscOnly = computed(() => Object.keys(discIndexMap.value).length <= 1) +const sortingByTrack = computed(() => sortField.value === 'track') +const inAlbumContext = computed(() => context.type === 'Album') + +const noDiscLabel = computed(() => noOrOneDiscOnly.value || !sortingByTrack.value || !inAlbumContext.value) + +const showDiscLabel = (row: Playable) => { + if (noDiscLabel.value) { + return false + } + + const index = findIndex(rows.value, ({ playable }) => playable.id === row.id) + return discIndexMap.value[index] !== undefined +} + +const standardSongItemHeight = 64 +const discNumberHeight = 32.5 + +const calculatedItemHeight = computed(() => { + if (noDiscLabel.value) { + return standardSongItemHeight + } + + const discCount = Object.keys(discIndexMap.value).length + const totalAdditionalPixels = discCount * discNumberHeight + + const totalHeight = (rows.value.length * standardSongItemHeight) + totalAdditionalPixels + const averageHeight = totalHeight / rows.value.length + + return averageHeight +}) + defineExpose({ getAllPlayablesWithSort, }) diff --git a/resources/assets/js/components/song/SongListItem.spec.ts b/resources/assets/js/components/song/SongListItem.spec.ts index 26904840..1154e7df 100644 --- a/resources/assets/js/components/song/SongListItem.spec.ts +++ b/resources/assets/js/components/song/SongListItem.spec.ts @@ -29,9 +29,20 @@ new class extends UnitTestCase { await this.user.dblClick(screen.getByTestId('song-item')) expect(emitted().play).toBeTruthy() }) + + it('renders disc info when showDisc is true', async () => { + const song = factory('song', { + disc: 2, + title: 'Test Song', + }) + + const showDisc = true + const { getByText } = this.renderComponent(song, showDisc) + expect(getByText('DISC 2')).toBeTruthy() + }) } - private renderComponent (playable?: Playable) { + private renderComponent (playable?: Playable, showDisc = false) { playable = playable ?? factory('song') row = { @@ -42,6 +53,7 @@ new class extends UnitTestCase { return this.render(SongListItem, { props: { item: row, + showDisc, }, }) } diff --git a/resources/assets/js/components/song/SongListItem.vue b/resources/assets/js/components/song/SongListItem.vue index e0fa0550..be24970c 100644 --- a/resources/assets/js/components/song/SongListItem.vue +++ b/resources/assets/js/components/song/SongListItem.vue @@ -1,49 +1,58 @@