mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
feat: customizable item layout on fullscreen player (#1769)
* change container display to release type, readd badge styling to improve contrast * make everything customizable
This commit is contained in:
@@ -1013,6 +1013,8 @@
|
|||||||
"sidebarCollapsedNavigation": "sidebar (collapsed) navigation",
|
"sidebarCollapsedNavigation": "sidebar (collapsed) navigation",
|
||||||
"sidebarConfiguration_description": "select the items and order in which they appear in the sidebar",
|
"sidebarConfiguration_description": "select the items and order in which they appear in the sidebar",
|
||||||
"sidebarConfiguration": "sidebar configuration",
|
"sidebarConfiguration": "sidebar configuration",
|
||||||
|
"playerItemConfiguration_description": "configure what items are shown, and in what order, on the fullscreen player",
|
||||||
|
"playerItemConfiguration": "player item configuration",
|
||||||
"sidebarPlaylistList_description": "show or hide the playlist list in the sidebar",
|
"sidebarPlaylistList_description": "show or hide the playlist list in the sidebar",
|
||||||
"sidebarPlaylistList": "sidebar playlist list",
|
"sidebarPlaylistList": "sidebar playlist list",
|
||||||
"sidebarPlaylistSorting_description": "allows manual playlist sorting in the sidebar using drag and drop instead of the default server order",
|
"sidebarPlaylistSorting_description": "allows manual playlist sorting in the sidebar using drag and drop instead of the default server order",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { t } from 'i18next';
|
||||||
import { AnimatePresence, HTMLMotionProps, motion, Variants } from 'motion/react';
|
import { AnimatePresence, HTMLMotionProps, motion, Variants } from 'motion/react';
|
||||||
import { Fragment, useEffect, useRef } from 'react';
|
import { Fragment, useEffect, useRef } from 'react';
|
||||||
import { generatePath, Link } from 'react-router';
|
import { generatePath, Link } from 'react-router';
|
||||||
@@ -101,7 +102,7 @@ export const FullScreenPlayerImage = () => {
|
|||||||
|
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
const { nextSong } = usePlayerData();
|
const { nextSong } = usePlayerData();
|
||||||
const { blurExplicitImages } = useGeneralSettings();
|
const { blurExplicitImages, playerItems } = useGeneralSettings();
|
||||||
|
|
||||||
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
||||||
|
|
||||||
@@ -171,6 +172,38 @@ export const FullScreenPlayerImage = () => {
|
|||||||
nextSong?.explicitStatus,
|
nextSong?.explicitStatus,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const builtDataItems = {
|
||||||
|
bit_depth: currentSong?.bitDepth && <Badge>{currentSong?.bitDepth} bit</Badge>,
|
||||||
|
bit_rate: currentSong?.bitRate && <Badge>{currentSong?.bitRate} kbps</Badge>,
|
||||||
|
bpm: currentSong?.bpm && (
|
||||||
|
<Badge>
|
||||||
|
{currentSong?.bpm} {t('common.bpm')}
|
||||||
|
</Badge>
|
||||||
|
),
|
||||||
|
codec: currentSong?.container && <Badge>{currentSong?.container}</Badge>,
|
||||||
|
disc_number: currentSong?.discNumber && (
|
||||||
|
<Badge>
|
||||||
|
{t('common.disc')} {currentSong?.discNumber}
|
||||||
|
</Badge>
|
||||||
|
),
|
||||||
|
genres:
|
||||||
|
currentSong?.genres &&
|
||||||
|
currentSong?.genres
|
||||||
|
.slice(0, 2)
|
||||||
|
.map((genre) => <Badge key={genre.id}>{genre.name}</Badge>),
|
||||||
|
release_date: currentSong?.releaseDate && <Badge>{currentSong?.releaseDate}</Badge>,
|
||||||
|
release_type: currentSong?.tags?.releasetype && (
|
||||||
|
<Badge>{currentSong?.tags?.releasetype[0]}</Badge>
|
||||||
|
),
|
||||||
|
release_year: currentSong?.releaseYear && <Badge>{currentSong?.releaseYear}</Badge>,
|
||||||
|
sample_rate: currentSong?.sampleRate && <Badge>{currentSong?.sampleRate / 1000} kHz</Badge>,
|
||||||
|
track_number: currentSong?.trackNumber && (
|
||||||
|
<Badge>
|
||||||
|
{t('common.trackNumber')} {currentSong?.trackNumber}
|
||||||
|
</Badge>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
align="center"
|
align="center"
|
||||||
@@ -283,12 +316,7 @@ export const FullScreenPlayerImage = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
{!isPlayingRadio && (
|
{!isPlayingRadio && (
|
||||||
<Group justify="center" mt="sm">
|
<Group justify="center" mt="sm">
|
||||||
{currentSong?.container && (
|
{playerItems.map((i) => !i.disabled && builtDataItems[i.id])}
|
||||||
<Badge variant="transparent">{currentSong?.container}</Badge>
|
|
||||||
)}
|
|
||||||
{currentSong?.releaseYear && (
|
|
||||||
<Badge variant="transparent">{currentSong?.releaseYear}</Badge>
|
|
||||||
)}
|
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -555,7 +555,6 @@ const Controls = () => {
|
|||||||
/>
|
/>
|
||||||
</Option.Control>
|
</Option.Control>
|
||||||
</Option>
|
</Option>
|
||||||
<Divider my="sm" />
|
|
||||||
</Popover.Dropdown>
|
</Popover.Dropdown>
|
||||||
</Popover>
|
</Popover>
|
||||||
<ListConfigMenu
|
<ListConfigMenu
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ArtistReleaseTypeSettings,
|
ArtistReleaseTypeSettings,
|
||||||
ArtistSettings,
|
ArtistSettings,
|
||||||
} from '/@/renderer/features/settings/components/general/artist-settings';
|
} from '/@/renderer/features/settings/components/general/artist-settings';
|
||||||
|
import { FullscreenPlayerSettings } from '/@/renderer/features/settings/components/general/fullscreen-player-settings';
|
||||||
import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings';
|
import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings';
|
||||||
import { PathSettings } from '/@/renderer/features/settings/components/general/path-settings';
|
import { PathSettings } from '/@/renderer/features/settings/components/general/path-settings';
|
||||||
import {
|
import {
|
||||||
@@ -697,6 +698,7 @@ export const ApplicationSettings = memo(() => {
|
|||||||
<HomeSettings />
|
<HomeSettings />
|
||||||
<ArtistSettings />
|
<ArtistSettings />
|
||||||
<ArtistReleaseTypeSettings />
|
<ArtistReleaseTypeSettings />
|
||||||
|
<FullscreenPlayerSettings />
|
||||||
<PathSettings />
|
<PathSettings />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
import { DraggableItems } from '/@/renderer/features/settings/components/general/draggable-items';
|
||||||
|
import {
|
||||||
|
PlayerItem,
|
||||||
|
SortableItem,
|
||||||
|
useGeneralSettings,
|
||||||
|
useSettingsStoreActions,
|
||||||
|
} from '/@/renderer/store';
|
||||||
|
|
||||||
|
const PLAYER_ITEMS: Array<[PlayerItem, string]> = [
|
||||||
|
[PlayerItem.BIT_DEPTH, 'common.bitDepth'],
|
||||||
|
[PlayerItem.BIT_RATE, 'common.bitrate'],
|
||||||
|
[PlayerItem.BPM, 'common.bpm'],
|
||||||
|
[PlayerItem.CODEC, 'common.codec'],
|
||||||
|
[PlayerItem.DISC_NUMBER, 'table.config.label.discNumber'],
|
||||||
|
[PlayerItem.GENRES, 'entity.genre_other'],
|
||||||
|
[PlayerItem.RELEASE_DATE, 'filter.releaseDate'],
|
||||||
|
[PlayerItem.RELEASE_TYPE, 'common.releaseType'],
|
||||||
|
[PlayerItem.RELEASE_YEAR, 'filter.releaseYear'],
|
||||||
|
[PlayerItem.SAMPLE_RATE, 'common.sampleRate'],
|
||||||
|
[PlayerItem.TRACK_NUMBER, 'table.config.label.trackNumber'],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const FullscreenPlayerSettings = memo(() => {
|
||||||
|
const { playerItems } = useGeneralSettings();
|
||||||
|
const { setPlayerItems } = useSettingsStoreActions();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DraggableItems
|
||||||
|
description="setting.playerItemConfiguration"
|
||||||
|
itemLabels={PLAYER_ITEMS}
|
||||||
|
items={playerItems as SortableItem<PlayerItem>[]}
|
||||||
|
setItems={setPlayerItems}
|
||||||
|
title="setting.playerItemConfiguration"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -74,6 +74,20 @@ const HomeItemSchema = z.enum([
|
|||||||
'recentlyReleased',
|
'recentlyReleased',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const PlayerItemSchema = z.enum([
|
||||||
|
'bit_depth',
|
||||||
|
'bit_rate',
|
||||||
|
'bpm',
|
||||||
|
'disc_number',
|
||||||
|
'sample_rate',
|
||||||
|
'track_number',
|
||||||
|
'codec',
|
||||||
|
'release_year',
|
||||||
|
'release_type',
|
||||||
|
'release_date',
|
||||||
|
'genres',
|
||||||
|
]);
|
||||||
|
|
||||||
const ArtistItemSchema = z.enum([
|
const ArtistItemSchema = z.enum([
|
||||||
'biography',
|
'biography',
|
||||||
'compilations',
|
'compilations',
|
||||||
@@ -461,6 +475,7 @@ export const GeneralSettingsSchema = z.object({
|
|||||||
playButtonBehavior: z.nativeEnum(Play),
|
playButtonBehavior: z.nativeEnum(Play),
|
||||||
playerbarOpenDrawer: z.boolean(),
|
playerbarOpenDrawer: z.boolean(),
|
||||||
playerbarSlider: PlayerbarSliderSchema,
|
playerbarSlider: PlayerbarSliderSchema,
|
||||||
|
playerItems: z.array(SortableItemSchema(PlayerItemSchema)),
|
||||||
playlistTarget: PlaylistTargetSchema,
|
playlistTarget: PlaylistTargetSchema,
|
||||||
primaryShade: z.number().min(0).max(9),
|
primaryShade: z.number().min(0).max(9),
|
||||||
resume: z.boolean(),
|
resume: z.boolean(),
|
||||||
@@ -781,6 +796,20 @@ export enum PlayerbarSliderType {
|
|||||||
WAVEFORM = 'waveform',
|
WAVEFORM = 'waveform',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PlayerItem {
|
||||||
|
BIT_DEPTH = 'bit_depth',
|
||||||
|
BIT_RATE = 'bit_rate',
|
||||||
|
BPM = 'bpm',
|
||||||
|
CODEC = 'codec',
|
||||||
|
DISC_NUMBER = 'disc_number',
|
||||||
|
GENRES = 'genres',
|
||||||
|
RELEASE_DATE = 'release_date',
|
||||||
|
RELEASE_TYPE = 'release_type',
|
||||||
|
RELEASE_YEAR = 'release_year',
|
||||||
|
SAMPLE_RATE = 'sample_rate',
|
||||||
|
TRACK_NUMBER = 'track_number',
|
||||||
|
}
|
||||||
|
|
||||||
export enum PlaylistTarget {
|
export enum PlaylistTarget {
|
||||||
ALBUM = 'album',
|
ALBUM = 'album',
|
||||||
TRACK = 'track',
|
TRACK = 'track',
|
||||||
@@ -840,6 +869,7 @@ export interface SettingsSlice extends z.infer<typeof SettingsStateSchema> {
|
|||||||
setHomeItems: (item: SortableItem<HomeItem>[]) => void;
|
setHomeItems: (item: SortableItem<HomeItem>[]) => void;
|
||||||
setList: (type: ItemListKey, data: DeepPartial<ItemListSettings>) => void;
|
setList: (type: ItemListKey, data: DeepPartial<ItemListSettings>) => void;
|
||||||
setPlaybackFilters: (filters: PlayerFilter[]) => void;
|
setPlaybackFilters: (filters: PlayerFilter[]) => void;
|
||||||
|
setPlayerItems: (items: SortableItem<PlayerItem>[]) => void;
|
||||||
setPlaylistBehavior: (target: PlaylistTarget) => void;
|
setPlaylistBehavior: (target: PlaylistTarget) => void;
|
||||||
setSettings: (data: DeepPartial<SettingsState>) => void;
|
setSettings: (data: DeepPartial<SettingsState>) => void;
|
||||||
setSidebarItems: (items: SidebarItemType[]) => void;
|
setSidebarItems: (items: SidebarItemType[]) => void;
|
||||||
@@ -864,6 +894,53 @@ export type TranscodingConfig = z.infer<typeof TranscodingConfigSchema>;
|
|||||||
|
|
||||||
export type VersionedSettings = SettingsState & { version: number };
|
export type VersionedSettings = SettingsState & { version: number };
|
||||||
|
|
||||||
|
export const playerItems: SortableItem<PlayerItem>[] = [
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.BIT_DEPTH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.BIT_RATE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.BPM,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
id: PlayerItem.CODEC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.DISC_NUMBER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.GENRES,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.RELEASE_DATE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.RELEASE_TYPE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
id: PlayerItem.RELEASE_YEAR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.SAMPLE_RATE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disabled: true,
|
||||||
|
id: PlayerItem.TRACK_NUMBER,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export const sidebarItems: SidebarItemType[] = [
|
export const sidebarItems: SidebarItemType[] = [
|
||||||
{
|
{
|
||||||
disabled: true,
|
disabled: true,
|
||||||
@@ -1052,6 +1129,7 @@ const initialState: SettingsState = {
|
|||||||
barWidth: 2,
|
barWidth: 2,
|
||||||
type: PlayerbarSliderType.SLIDER,
|
type: PlayerbarSliderType.SLIDER,
|
||||||
},
|
},
|
||||||
|
playerItems,
|
||||||
playlistTarget: PlaylistTarget.TRACK,
|
playlistTarget: PlaylistTarget.TRACK,
|
||||||
primaryShade: 6,
|
primaryShade: 6,
|
||||||
resume: true,
|
resume: true,
|
||||||
@@ -1901,6 +1979,11 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
|||||||
state.playback.filters = filters;
|
state.playback.filters = filters;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setPlayerItems: (items: SortableItem<PlayerItem>[]) => {
|
||||||
|
set((state) => {
|
||||||
|
state.general.playerItems = items;
|
||||||
|
});
|
||||||
|
},
|
||||||
setPlaylistBehavior: (target: PlaylistTarget) => {
|
setPlaylistBehavior: (target: PlaylistTarget) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.general.playlistTarget = target;
|
state.general.playlistTarget = target;
|
||||||
@@ -2412,6 +2495,8 @@ export const useSidebarPlaylistListFilterRegex = () =>
|
|||||||
export const useSidebarItems = () =>
|
export const useSidebarItems = () =>
|
||||||
useSettingsStore((state) => state.general.sidebarItems, shallow);
|
useSettingsStore((state) => state.general.sidebarItems, shallow);
|
||||||
|
|
||||||
|
export const usePlayerItems = () => useSettingsStore((state) => state.general.playerItems, shallow);
|
||||||
|
|
||||||
export const useSidebarCollapsedNavigation = () =>
|
export const useSidebarCollapsedNavigation = () =>
|
||||||
useSettingsStore((state) => state.general.sidebarCollapsedNavigation, shallow);
|
useSettingsStore((state) => state.general.sidebarCollapsedNavigation, shallow);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user