From eb50c69a3540df958865ff95ccaa352c63cfcbee Mon Sep 17 00:00:00 2001 From: Pyx <91509874+pyxfluff@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:42:01 -0400 Subject: [PATCH] Album blur, allow clicking the playerbar to toggle the player, misc changes (#717) * Album blur, allow clicking the playerbar to toggle the player * Fix stopProporagion, sync package with upsteam, update translation * recommit my existing changes * Update default albumBackgroundBlur to 6 * according to git this commit resets the package files * merge with our fork because pyx forgot to add it * try adding a setting * change the playerbar animation * make the animation quicker bc its choppy * change playerbar to use a bool instead * requested opacity fix * Refactor classes to use clsx --------- Co-authored-by: iiPython Co-authored-by: Jeff <42182408+jeffvli@users.noreply.github.com> --- src/i18n/locales/en.json | 6 ++ src/i18n/locales/es.json | 2 +- .../albums/components/album-detail-header.tsx | 11 +-- .../albums/routes/album-detail-route.tsx | 17 +++-- .../components/album-artist-detail-header.tsx | 6 +- .../player/components/left-controls.tsx | 16 +++-- .../player/components/player-button.tsx | 8 +++ .../player/components/playerbar-slider.tsx | 3 + .../features/player/components/playerbar.tsx | 22 +++++- .../components/general/control-settings.tsx | 70 +++++++++++++++++++ .../components/library-header.module.scss | 6 ++ .../shared/components/library-header.tsx | 22 +++++- .../layouts/default-layout/player-bar.tsx | 23 +++++- src/renderer/store/settings.store.ts | 6 ++ src/renderer/themes/default.scss | 1 + src/renderer/themes/light.scss | 7 +- 16 files changed, 197 insertions(+), 29 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b6e23299..5fa7ba3e 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -451,6 +451,10 @@ "setting": { "accentColor": "accent color", "accentColor_description": "sets the accent color for the application", + "albumBackground": "album background image", + "albumBackground_description": "adds a background image for album pages containing the album art", + "albumBackgroundBlur": "album background image blur size", + "albumBackgroundBlur_description": "adjusts the amount of blur applied to the album background image", "applicationHotkeys": "application hotkeys", "applicationHotkeys_description": "configure application hotkeys. toggle the checkbox to set as a global hotkey (desktop only)", "audioDevice": "audio device", @@ -587,6 +591,8 @@ "playButtonBehavior_optionPlay": "$t(player.play)", "playerAlbumArtResolution": "player album art resolution", "playerAlbumArtResolution_description": "the resolution for the large player's album art preview. larger makes it look more crisp, but may slow loading down. defaults to 0, meaning auto", + "playerbarOpenDrawer": "playerbar fullscreen toggle", + "playerbarOpenDrawer_description": "allows clicking of the playerbar to open the full screen player", "remotePassword": "remote control server password", "remotePassword_description": "sets the password for the remote control server. These credentials are by default transferred insecurely, so you should use a unique password that you do not care about", "remotePort": "remote control server port", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 83f519e3..f2adaa91 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -517,7 +517,7 @@ }, "albumArtistDetail": { "viewAllTracks": "ver todo de $t(entity.track_other)", - "relatedArtists": "similar a $t(entity.artist_other)", + "relatedArtists": "$t(entity.artist_other) similar", "topSongs": "mejores canciones", "topSongsFrom": "las mejores canciones de {{title}}", "viewAll": "Ver todo", diff --git a/src/renderer/features/albums/components/album-detail-header.tsx b/src/renderer/features/albums/components/album-detail-header.tsx index 9cb017f7..98db7db9 100644 --- a/src/renderer/features/albums/components/album-detail-header.tsx +++ b/src/renderer/features/albums/components/album-detail-header.tsx @@ -13,7 +13,10 @@ import { useCurrentServer } from '/@/renderer/store'; import { formatDateAbsolute, formatDurationString } from '/@/renderer/utils'; interface AlbumDetailHeaderProps { - background: string; + background: { + background: string; + blur: number; + }; } export const AlbumDetailHeader = forwardRef( @@ -24,6 +27,8 @@ export const AlbumDetailHeader = forwardRef( const cq = useContainerQuery(); const { t } = useTranslation(); + const showRating = detailQuery?.data?.serverType === ServerType.NAVIDROME; + const originalDifferentFromRelease = detailQuery.data?.originalDate && detailQuery.data.originalDate !== detailQuery.data.releaseDate; @@ -78,16 +83,14 @@ export const AlbumDetailHeader = forwardRef( }); }; - const showRating = detailQuery?.data?.serverType === ServerType.NAVIDROME; - return ( diff --git a/src/renderer/features/albums/routes/album-detail-route.tsx b/src/renderer/features/albums/routes/album-detail-route.tsx index cf069ce7..8dab5922 100644 --- a/src/renderer/features/albums/routes/album-detail-route.tsx +++ b/src/renderer/features/albums/routes/album-detail-route.tsx @@ -10,17 +10,18 @@ import { AlbumDetailHeader } from '/@/renderer/features/albums/components/album- import { usePlayQueueAdd } from '/@/renderer/features/player'; import { usePlayButtonBehavior } from '/@/renderer/store/settings.store'; import { LibraryItem } from '/@/renderer/api/types'; -import { useCurrentServer } from '/@/renderer/store'; +import { useCurrentServer, useGeneralSettings } from '/@/renderer/store'; const AlbumDetailRoute = () => { const tableRef = useRef(null); const scrollAreaRef = useRef(null); const headerRef = useRef(null); + const { albumBackground, albumBackgroundBlur } = useGeneralSettings(); const { albumId } = useParams() as { albumId: string }; const server = useCurrentServer(); const detailQuery = useAlbumDetail({ query: { id: albumId }, serverId: server?.id }); - const { color: background, colorId } = useFastAverageColor({ + const { color: backgroundColor, colorId } = useFastAverageColor({ id: albumId, src: detailQuery.data?.imageUrl, srcLoaded: !detailQuery.isLoading, @@ -38,16 +39,19 @@ const AlbumDetailRoute = () => { }); }; - if (!background || colorId !== albumId) { + if (!backgroundColor || colorId !== albumId) { return ; } + const backgroundUrl = detailQuery.data?.imageUrl || ''; + const background = (albumBackground && `url(${backgroundUrl})`) || backgroundColor; + return ( @@ -62,7 +66,10 @@ const AlbumDetailRoute = () => { > ) => { const { albumArtistId } = useParams() as { albumArtistId: string }; const server = useCurrentServer(); + const { t } = useTranslation(); const detailQuery = useAlbumArtistDetail({ query: { id: albumArtistId }, serverId: server?.id, @@ -26,12 +28,12 @@ export const AlbumArtistDetailHeader = forwardRef( { id: 'albumCount', secondary: false, - value: detailQuery?.data?.albumCount && `${detailQuery?.data?.albumCount} albums`, + value: t('entity.albumWithCount', { count: detailQuery?.data?.albumCount || 0 }), }, { id: 'songCount', secondary: false, - value: detailQuery?.data?.songCount && `${detailQuery?.data?.songCount} songs`, + value: t('entity.trackWithCount', { count: detailQuery?.data?.songCount || 0 }), }, { id: 'duration', diff --git a/src/renderer/features/player/components/left-controls.tsx b/src/renderer/features/player/components/left-controls.tsx index 52f63f05..971a9b55 100644 --- a/src/renderer/features/player/components/left-controls.tsx +++ b/src/renderer/features/player/components/left-controls.tsx @@ -67,7 +67,7 @@ const PlayerbarImage = styled.img` const LineItem = styled.div<{ $secondary?: boolean }>` display: inline-block; - width: 95%; + width: fit-content; max-width: 20vw; overflow: hidden; line-height: 1.3; @@ -122,6 +122,8 @@ export const LeftControls = () => { setSideBar({ image: true }); }; + const stopPropagation = (e?: MouseEvent) => e?.stopPropagation(); + useHotkeys([ [ bindings.toggleFullscreenPlayer.allowGlobal @@ -207,7 +209,7 @@ export const LeftControls = () => { )} - + { )} - + {artists?.map((artist, index) => ( {index > 0 && } @@ -257,7 +262,10 @@ export const LeftControls = () => { ))} - + ( { + e.stopPropagation(); + rest.onClick?.(e); + }} > {icon} @@ -148,6 +152,10 @@ export const PlayerButton = forwardRef( { + e.stopPropagation(); + rest.onClick?.(e); + }} > {icon} diff --git a/src/renderer/features/player/components/playerbar-slider.tsx b/src/renderer/features/player/components/playerbar-slider.tsx index 6313cf89..7f1ba27b 100644 --- a/src/renderer/features/player/components/playerbar-slider.tsx +++ b/src/renderer/features/player/components/playerbar-slider.tsx @@ -41,6 +41,9 @@ export const PlayerbarSlider = ({ ...props }: SliderProps) => { }, }} {...props} + onClick={(e) => { + e?.stopPropagation(); + }} /> ); }; diff --git a/src/renderer/features/player/components/playerbar.tsx b/src/renderer/features/player/components/playerbar.tsx index 594e03a3..3b3be97c 100644 --- a/src/renderer/features/player/components/playerbar.tsx +++ b/src/renderer/features/player/components/playerbar.tsx @@ -1,6 +1,10 @@ -import { useCallback } from 'react'; +import { useCallback, MouseEvent } from 'react'; import styled from 'styled-components'; -import { usePlaybackType, useSettingsStore } from '/@/renderer/store/settings.store'; +import { + usePlaybackType, + useSettingsStore, + useGeneralSettings, +} from '/@/renderer/store/settings.store'; import { PlaybackType } from '/@/renderer/types'; import { AudioPlayer } from '/@/renderer/components'; import { @@ -11,6 +15,8 @@ import { usePlayer2Data, usePlayerControls, useVolume, + useSetFullScreenPlayerStore, + useFullScreenPlayerStore, } from '/@/renderer/store'; import { CenterControls } from './center-controls'; import { LeftControls } from './left-controls'; @@ -62,6 +68,7 @@ const CenterGridItem = styled.div` export const Playerbar = () => { const playersRef = PlayersRef; const settings = useSettingsStore((state) => state.playback); + const { playerbarOpenDrawer } = useGeneralSettings(); const playbackType = usePlaybackType(); const volume = useVolume(); const player1 = usePlayer1Data(); @@ -70,6 +77,13 @@ export const Playerbar = () => { const player = useCurrentPlayer(); const muted = useMuted(); const { autoNext } = usePlayerControls(); + const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore(); + const setFullScreenPlayerStore = useSetFullScreenPlayerStore(); + + const handleToggleFullScreenPlayer = (e?: MouseEvent | KeyboardEvent) => { + e?.stopPropagation(); + setFullScreenPlayerStore({ expanded: !isFullScreenPlayerExpanded }); + }; const autoNextFn = useCallback(() => { const playerData = autoNext(); @@ -77,7 +91,9 @@ export const Playerbar = () => { }, [autoNext]); return ( - + diff --git a/src/renderer/features/settings/components/general/control-settings.tsx b/src/renderer/features/settings/components/general/control-settings.tsx index 28afbe17..c6fbed83 100644 --- a/src/renderer/features/settings/components/general/control-settings.tsx +++ b/src/renderer/features/settings/components/general/control-settings.tsx @@ -467,6 +467,76 @@ export const ControlSettings = () => { isHidden: false, title: t('setting.homeFeature', { postProcess: 'sentenceCase' }), }, + { + control: ( + + setSettings({ + general: { + ...settings, + albumBackground: e.currentTarget.checked, + }, + }) + } + /> + ), + description: t('setting.albumBackground', { + context: 'description', + postProcess: 'sentenceCase', + }), + isHidden: false, + title: t('setting.albumBackground', { postProcess: 'sentenceCase' }), + }, + { + control: ( + `${e} rem`} + max={6} + min={0} + step={0.5} + w={100} + onChangeEnd={(e) => { + setSettings({ + general: { + ...settings, + albumBackgroundBlur: e, + }, + }); + }} + /> + ), + description: t('setting.albumBackgroundBlur', { + context: 'description', + postProcess: 'sentenceCase', + }), + isHidden: false, + title: t('setting.albumBackgroundBlur', { postProcess: 'sentenceCase' }), + }, + { + control: ( + + setSettings({ + general: { + ...settings, + playerbarOpenDrawer: e.currentTarget.checked, + }, + }) + } + /> + ), + description: t('setting.playerbarOpenDrawer', { + context: 'description', + postProcess: 'sentenceCase', + }), + isHidden: false, + title: t('setting.playerbarOpenDrawer', { postProcess: 'sentenceCase' }), + }, ]; return ; diff --git a/src/renderer/features/shared/components/library-header.module.scss b/src/renderer/features/shared/components/library-header.module.scss index 151cbb93..47d466f5 100644 --- a/src/renderer/features/shared/components/library-header.module.scss +++ b/src/renderer/features/shared/components/library-header.module.scss @@ -123,6 +123,8 @@ width: 100%; height: 100%; opacity: 0.9; + background-size: cover !important; + background-position: center !important; } .background-overlay { @@ -135,6 +137,10 @@ background: var(--bg-header-overlay); } +.opaque-overlay { + opacity: 0.5; +} + .title { display: -webkit-box; overflow: hidden; diff --git a/src/renderer/features/shared/components/library-header.tsx b/src/renderer/features/shared/components/library-header.tsx index 56888f05..46f62b8b 100644 --- a/src/renderer/features/shared/components/library-header.tsx +++ b/src/renderer/features/shared/components/library-header.tsx @@ -1,15 +1,18 @@ import { forwardRef, ReactNode, Ref, useState } from 'react'; import { Group } from '@mantine/core'; import { AutoTextSize } from 'auto-text-size'; +import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import styles from './library-header.module.scss'; import { LibraryItem } from '/@/renderer/api/types'; import { Text } from '/@/renderer/components'; import { ItemImagePlaceholder } from '/@/renderer/features/shared/components/item-image-placeholder'; +import { useGeneralSettings } from '/@/renderer/store'; interface LibraryHeaderProps { background: string; + blur?: number; children?: ReactNode; imagePlaceholderUrl?: string | null; imageUrl?: string | null; @@ -19,11 +22,20 @@ interface LibraryHeaderProps { export const LibraryHeader = forwardRef( ( - { imageUrl, imagePlaceholderUrl, background, title, item, children }: LibraryHeaderProps, + { + imageUrl, + imagePlaceholderUrl, + background, + blur, + title, + item, + children, + }: LibraryHeaderProps, ref: Ref, ) => { const { t } = useTranslation(); const [isImageError, setIsImageError] = useState(false); + const { albumBackground } = useGeneralSettings(); const onImageError = () => { setIsImageError(true); @@ -53,9 +65,13 @@ export const LibraryHeader = forwardRef( >
+
-
{imageUrl && !isImageError ? ( ` z-index: 200; grid-area: player; background: var(--playerbar-bg); + transition: background 0.5s; + + ${(props) => + props.drawerEffect && + ` + &:hover { + background: var(--playerbar-bg-active); + } + `} `; export const PlayerBar = () => { + const { playerbarOpenDrawer } = useGeneralSettings(); + return ( - + ); diff --git a/src/renderer/store/settings.store.ts b/src/renderer/store/settings.store.ts index ecd8f66b..85330efd 100644 --- a/src/renderer/store/settings.store.ts +++ b/src/renderer/store/settings.store.ts @@ -205,6 +205,8 @@ export interface SettingsState { general: { accent: string; albumArtRes?: number | null; + albumBackground: boolean; + albumBackgroundBlur: number; buttonSize: number; defaultFullPlaylist: boolean; disabledContextMenu: { [k in ContextMenuItemType]?: boolean }; @@ -218,6 +220,7 @@ export interface SettingsState { nativeAspectRatio: boolean; passwordStore?: string; playButtonBehavior: Play; + playerbarOpenDrawer: boolean; resume: boolean; showQueueDrawerButton: boolean; sideQueueType: SideQueueType; @@ -342,6 +345,8 @@ const initialState: SettingsState = { general: { accent: 'rgb(53, 116, 252)', albumArtRes: undefined, + albumBackground: false, + albumBackgroundBlur: 6, buttonSize: 20, defaultFullPlaylist: true, disabledContextMenu: {}, @@ -355,6 +360,7 @@ const initialState: SettingsState = { nativeAspectRatio: false, passwordStore: undefined, playButtonBehavior: Play.NOW, + playerbarOpenDrawer: false, resume: false, showQueueDrawerButton: false, sideQueueType: 'sideQueue', diff --git a/src/renderer/themes/default.scss b/src/renderer/themes/default.scss index 6e830b88..2248fc7f 100644 --- a/src/renderer/themes/default.scss +++ b/src/renderer/themes/default.scss @@ -23,6 +23,7 @@ --sidebar-handle-bg: #4d4d4d; --sidebar-border: 2px rgba(18, 18, 18, 70%) solid; --playerbar-bg: rgb(16, 16, 16); + --playerbar-bg-active: rgb(11, 11, 11); --playerbar-btn-main-fg: rgb(0, 0, 0); --playerbar-btn-main-fg-hover: rgb(0, 0, 0); --playerbar-btn-main-bg: rgb(230, 230, 230); diff --git a/src/renderer/themes/light.scss b/src/renderer/themes/light.scss index 3e1d79b6..c1d589a1 100644 --- a/src/renderer/themes/light.scss +++ b/src/renderer/themes/light.scss @@ -15,11 +15,8 @@ body[data-theme='defaultLight'] { --sidebar-fg-hover: rgb(85, 85, 85); --sidebar-handle-bg: rgb(220, 220, 220); --sidebar-border: 1px rgba(220, 220, 220, 70%) solid; - --playerbar-bg: linear-gradient( - rgb(220, 220, 220) 0%, - rgb(240, 240, 240) 50%, - rgb(220, 220, 220) 100% - ); + --playerbar-bg: rgb(220, 220, 220); + --playerbar-bg-active: rgb(175, 175, 175); --playerbar-btn-main-fg: rgb(0, 0, 0); --playerbar-btn-main-fg-hover: rgb(0, 0, 0); --playerbar-btn-main-bg: transparent;