From ec4a2b6831fb8aa3c5e8f1fc658c235ced59cfd9 Mon Sep 17 00:00:00 2001 From: Lei Nelissen Date: Sun, 21 Jul 2024 23:48:33 +0200 Subject: [PATCH] feat: separate discs in album view when multiple are available fixes #179 --- src/localisation/lang/en/locale.json | 3 +- src/localisation/types.ts | 3 +- .../Music/stacks/components/TrackListView.tsx | 180 ++++++++++-------- src/store/music/types.ts | 1 + src/utility/JellyfinApi/album.ts | 2 +- 5 files changed, 111 insertions(+), 78 deletions(-) diff --git a/src/localisation/lang/en/locale.json b/src/localisation/lang/en/locale.json index de3d2b5..727c397 100644 --- a/src/localisation/lang/en/locale.json +++ b/src/localisation/lang/en/locale.json @@ -74,5 +74,6 @@ "privacy-policy": "Privacy Policy", "sleep-timer": "Sleep timer", "delete": "Delete", - "cancel": "Cancel" + "cancel": "Cancel", + "disc": "Disc" } diff --git a/src/localisation/types.ts b/src/localisation/types.ts index 2739f56..5e46cd3 100644 --- a/src/localisation/types.ts +++ b/src/localisation/types.ts @@ -73,4 +73,5 @@ export type LocaleKeys = 'play-next' | 'privacy-policy' | 'sleep-timer' | 'delete' -| 'cancel' \ No newline at end of file +| 'cancel' +| 'disc' \ No newline at end of file diff --git a/src/screens/Music/stacks/components/TrackListView.tsx b/src/screens/Music/stacks/components/TrackListView.tsx index 06a71b1..55bd037 100644 --- a/src/screens/Music/stacks/components/TrackListView.tsx +++ b/src/screens/Music/stacks/components/TrackListView.tsx @@ -25,6 +25,8 @@ import CoverImage from '@/components/CoverImage'; import ticksToDuration from '@/utility/ticksToDuration'; import { t } from '@/localisation'; import { SafeScrollView, useNavigationOffsets } from '@/components/SafeNavigatorView'; +import { groupBy } from 'lodash'; +import Divider from '@/components/Divider'; const styles = StyleSheet.create({ index: { @@ -34,6 +36,12 @@ const styles = StyleSheet.create({ activeText: { fontWeight: '500', }, + discContainer: { + flexDirection: 'row', + gap: 24, + alignItems: 'center', + marginBottom: 12, + } }); const AlbumImageContainer = styled.View` @@ -54,7 +62,7 @@ const TrackContainer = styled.View<{ isPlaying: boolean, small?: boolean }>` `} ${props => props.small && css` - padding: ${Platform.select({ ios: '8px 4px', android: '4px'})}; + padding: ${Platform.select({ ios: '8px 4px', android: '4px' })}; `} `; @@ -99,6 +107,18 @@ const TrackListView: React.FC = ({ ), 0) ), [trackIds, tracks]); + // Split all tracks into trackgroups depending on their parent id (i.e. disc + // number). + const trackGroups: [string, string[]][] = useMemo(() => { + // GUARD: Only apply this rendering style for albums + if (listNumberingStyle !== 'album') { + return [['0', trackIds]]; + } + + const groups = groupBy(trackIds, (id) => tracks[id].ParentIndexNumber); + return Object.entries(groups); + }, [trackIds, tracks, listNumberingStyle]); + // Retrieve helpers const getImage = useGetImage(); const playTracks = usePlayTracks(); @@ -111,14 +131,14 @@ const TrackListView: React.FC = ({ // Retrieve the largest index in the current set of tracks const largestIndex = trackIds.reduce((max, trackId, i) => { // Retrieve the index for this trackid, depending on settings - const index = listNumberingStyle === 'index' + const index = listNumberingStyle === 'index' ? i + 1 : tracks[trackId]?.IndexNumber; // Check that the current index is larger than the current max. - return index > max ? index: max; + return index > max ? index : max; }, 0); - + // Retrieve the number of digits in the largest index const noDigits = largestIndex.toFixed(0).toString().length; @@ -134,8 +154,8 @@ const TrackListView: React.FC = ({ await TrackPlayer.skip(index); await TrackPlayer.play(); }, [playTracks, trackIds]); - const longPressTrack = useCallback((index: number) => { - navigation.navigate('TrackPopupMenu', { trackId: trackIds[index].toString() }); + const longPressTrack = useCallback((index: number) => { + navigation.navigate('TrackPopupMenu', { trackId: trackIds[index].toString() }); }, [navigation, trackIds]); const downloadAllTracks = useCallback(() => { trackIds.forEach((trackId) => dispatch(queueTrackForDownload(trackId))); @@ -162,86 +182,96 @@ const TrackListView: React.FC = ({ - {trackIds.map((trackId, i) => - - - ( + + {trackGroups.length > 1 && ( + + {t('disc')} {discNo} + + + )} + {groupTrackIds.map((trackId, i) => + - {listNumberingStyle === 'index' - ? i + 1 - : tracks[trackId]?.IndexNumber} - - - - {tracks[trackId]?.Name} - - {itemDisplayStyle === 'playlist' && ( - {tracks[trackId]?.Artists.join(', ')} + {listNumberingStyle === 'index' + ? i + 1 + : tracks[trackId]?.IndexNumber} - )} - - - - {ticksToDuration(tracks[trackId]?.RunTimeTicks || 0)} - - - - - - )} + + + {tracks[trackId]?.Name} + + {itemDisplayStyle === 'playlist' && ( + + {tracks[trackId]?.Artists.join(', ')} + + )} + + + + {ticksToDuration(tracks[trackId]?.RunTimeTicks || 0)} + + + + + + )} + + ))} {t('total-duration')}{': '}{ticksToDuration(totalDuration)} diff --git a/src/store/music/types.ts b/src/store/music/types.ts index c80fcdb..89ff567 100644 --- a/src/store/music/types.ts +++ b/src/store/music/types.ts @@ -52,6 +52,7 @@ export interface AlbumTrack { RunTimeTicks: number; ProductionYear: number; IndexNumber: number; + ParentIndexNumber: number; IsFolder: boolean; Type: 'Audio'; UserData: UserData; diff --git a/src/utility/JellyfinApi/album.ts b/src/utility/JellyfinApi/album.ts index 921f1e3..1542016 100644 --- a/src/utility/JellyfinApi/album.ts +++ b/src/utility/JellyfinApi/album.ts @@ -59,7 +59,7 @@ export async function retrieveRecentAlbums(numberOfAlbums = 24) { export async function retrieveAlbumTracks(ItemId: string) { const singleAlbumOptions = { ParentId: ItemId, - SortBy: 'IndexNumber,SortName', + SortBy: 'ParentIndexNumber,IndexNumber,SortName', }; const singleAlbumParams = new URLSearchParams(singleAlbumOptions).toString();