From 237fb91a60caa7d70424f9a2ad91d77c1ccc91de Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Sun, 18 Feb 2024 20:22:38 -0800 Subject: [PATCH 1/2] [enhancement]: Differentiate shared and owner playlists for Navidrome Resolves #368. If Navidrome, post-process the playlist list and separate into owned and shared (in that order). --- src/i18n/locales/en.json | 1 + .../components/sidebar-playlist-list.tsx | 55 +++++++++++++++---- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 438e44a22..fbede376c 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -364,6 +364,7 @@ "playlists": "$t(entity.playlist_other)", "search": "$t(common.search)", "settings": "$t(common.setting_other)", + "shared": "shared $t(entity.playlist_other)", "tracks": "$t(entity.track_other)" }, "trackList": { diff --git a/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx b/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx index 8078043ca..3a1cf83ca 100644 --- a/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx +++ b/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx @@ -1,20 +1,20 @@ import { useCallback, useMemo, useState } from 'react'; -import { Flex, Group } from '@mantine/core'; +import { Box, Flex, Group } from '@mantine/core'; import { useDebouncedValue } from '@mantine/hooks'; import { useTranslation } from 'react-i18next'; import { RiAddBoxFill, RiAddCircleFill, RiPlayFill } from 'react-icons/ri'; import { generatePath } from 'react-router'; import { Link } from 'react-router-dom'; -import { LibraryItem } from '/@/renderer/api/types'; +import { LibraryItem, Playlist } from '/@/renderer/api/types'; import { Button, Text } from '/@/renderer/components'; import { usePlayQueueAdd } from '/@/renderer/features/player'; import { usePlaylistList } from '/@/renderer/features/playlists'; import { AppRoute } from '/@/renderer/router/routes'; -import { Play } from '/@/renderer/types'; +import { Play, ServerType } from '/@/renderer/types'; import AutoSizer from 'react-virtualized-auto-sizer'; import { FixedSizeList, ListChildComponentProps } from 'react-window'; import { useHideScrollbar } from '/@/renderer/hooks'; -import { useGeneralSettings } from '/@/renderer/store'; +import { useCurrentServer, useGeneralSettings } from '/@/renderer/store'; interface SidebarPlaylistListProps { data: ReturnType['data']; @@ -22,6 +22,20 @@ interface SidebarPlaylistListProps { const PlaylistRow = ({ index, data, style }: ListChildComponentProps) => { const { t } = useTranslation(); + + if (data?.items[index] === null) { + return ( +
+ + {t('page.sidebar.shared', { postProcess: 'titleCase' })} + +
+ ); + } + const path = data?.items[index].id ? data.defaultFullPlaylist ? generatePath(AppRoute.PLAYLISTS_DETAIL_SONGS, { playlistId: data.items[index].id }) @@ -125,6 +139,7 @@ export const SidebarPlaylistList = ({ data }: SidebarPlaylistListProps) => { const { isScrollbarHidden, hideScrollbarElementProps } = useHideScrollbar(0); const handlePlayQueueAdd = usePlayQueueAdd(); const { defaultFullPlaylist } = useGeneralSettings(); + const { type, username } = useCurrentServer() || {}; const [rect, setRect] = useState({ height: 0, @@ -147,12 +162,30 @@ export const SidebarPlaylistList = ({ data }: SidebarPlaylistListProps) => { ); const memoizedItemData = useMemo(() => { - return { - defaultFullPlaylist, - handlePlay: handlePlayPlaylist, - items: data?.items, - }; - }, [data?.items, defaultFullPlaylist, handlePlayPlaylist]); + const base = { defaultFullPlaylist, handlePlay: handlePlayPlaylist }; + + if (!type || !username || type === ServerType.JELLYFIN || !data?.items) { + return { ...base, items: data?.items }; + } + + const owned: Array = []; + const shared: Playlist[] = []; + + for (const playlist of data.items) { + if (playlist.owner !== username) { + shared.push(playlist); + } else { + owned.push(playlist); + } + } + + if (shared.length > 0) { + // Use `null` as a separator between owned and shared playlists + owned.push(null); + } + + return { ...base, items: owned.concat(shared) }; + }, [data?.items, defaultFullPlaylist, handlePlayPlaylist, type, username]); return ( { : 'overlay-scrollbar' } height={debounced.height} - itemCount={data?.items?.length || 0} + itemCount={memoizedItemData?.items?.length || 0} itemData={memoizedItemData} itemSize={25} overscanCount={20} From 83b5afb1879c0ce33dec9fa8bc02a8e00764aad1 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Mon, 4 Mar 2024 02:37:37 -0800 Subject: [PATCH 2/2] Remove ServerType check on shared playlist display --- .../features/sidebar/components/sidebar-playlist-list.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx b/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx index 3a1cf83ca..2592e1917 100644 --- a/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx +++ b/src/renderer/features/sidebar/components/sidebar-playlist-list.tsx @@ -10,7 +10,7 @@ import { Button, Text } from '/@/renderer/components'; import { usePlayQueueAdd } from '/@/renderer/features/player'; import { usePlaylistList } from '/@/renderer/features/playlists'; import { AppRoute } from '/@/renderer/router/routes'; -import { Play, ServerType } from '/@/renderer/types'; +import { Play } from '/@/renderer/types'; import AutoSizer from 'react-virtualized-auto-sizer'; import { FixedSizeList, ListChildComponentProps } from 'react-window'; import { useHideScrollbar } from '/@/renderer/hooks'; @@ -164,7 +164,7 @@ export const SidebarPlaylistList = ({ data }: SidebarPlaylistListProps) => { const memoizedItemData = useMemo(() => { const base = { defaultFullPlaylist, handlePlay: handlePlayPlaylist }; - if (!type || !username || type === ServerType.JELLYFIN || !data?.items) { + if (!type || !username || !data?.items) { return { ...base, items: data?.items }; }