mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 02:13:33 +00:00
Redesign sidebar / header and other misc. improvements (#24)
* Remove 1920px max width * Fix position of list controls menu * Match size and color of search input * Adjust library header sizing * Move app menu to sidebar * Increase row buffer on play queue list * Fix query builder styles * Fix playerbar slider track bg * Adjust titlebar styles * Fix invalid modal prop * Various adjustments to detail pages * Fix sidebar height calculation * Fix list null indicators, add filter indicator * Adjust playqueue styles * Fix jellyfin releaseYear normalization * Suppress browser context menu on ag-grid * Add radius to drawer queue -- normalize layout * Add modal styles to provider theme * Fix playlist song list pagination * Add disc number to albums with more than one disc * Fix query builder boolean values * Adjust input placeholder color * Properly handle rating/favorite from context menu on table * Conform dropdown menu styles to context menu * Increase sort type select width * Fix drawer queue radius * Change primary color * Prevent volume wheel from invalid values * Add icons to query builder dropdowns * Update notification styles * Update scrollbar thumb styles * Remove "add to playlist" on smart playlists * Fix "add to playlist" from context menu
This commit is contained in:
parent
d2c0d4c11f
commit
9f2e873366
80 changed files with 1427 additions and 1101 deletions
|
|
@ -45,25 +45,25 @@ export const JellyfinSongFilters = ({ handleFilterChange }: JellyfinSongFiltersP
|
|||
},
|
||||
];
|
||||
|
||||
const handleMinYearFilter = debounce((e: number | undefined) => {
|
||||
if (e && (e < 1700 || e > 2300)) return;
|
||||
const handleMinYearFilter = debounce((e: number | string) => {
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilters({
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
includeItemTypes: 'Audio',
|
||||
minYear: e,
|
||||
minYear: e === '' ? undefined : (e as number),
|
||||
},
|
||||
});
|
||||
handleFilterChange(updatedFilters);
|
||||
}, 500);
|
||||
|
||||
const handleMaxYearFilter = debounce((e: number | undefined) => {
|
||||
if (e && (e < 1700 || e > 2300)) return;
|
||||
const handleMaxYearFilter = debounce((e: number | string) => {
|
||||
if (typeof e === 'number' && (e < 1700 || e > 2300)) return;
|
||||
const updatedFilters = setFilters({
|
||||
jfParams: {
|
||||
...filter.jfParams,
|
||||
includeItemTypes: 'Audio',
|
||||
maxYear: e,
|
||||
maxYear: e === '' ? undefined : (e as number),
|
||||
},
|
||||
});
|
||||
handleFilterChange(updatedFilters);
|
||||
|
|
@ -99,17 +99,17 @@ export const JellyfinSongFilters = ({ handleFilterChange }: JellyfinSongFiltersP
|
|||
<Group grow>
|
||||
<NumberInput
|
||||
required
|
||||
defaultValue={filter.jfParams?.minYear}
|
||||
label="From year"
|
||||
max={2300}
|
||||
min={1700}
|
||||
value={filter.jfParams?.minYear}
|
||||
onChange={handleMinYearFilter}
|
||||
/>
|
||||
<NumberInput
|
||||
defaultValue={filter.jfParams?.maxYear}
|
||||
label="To year"
|
||||
max={2300}
|
||||
min={1700}
|
||||
value={filter.jfParams?.maxYear}
|
||||
onChange={handleMaxYearFilter}
|
||||
/>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -46,11 +46,11 @@ export const NavidromeSongFilters = ({ handleFilterChange }: NavidromeSongFilter
|
|||
},
|
||||
];
|
||||
|
||||
const handleYearFilter = debounce((e: number | undefined) => {
|
||||
const handleYearFilter = debounce((e: number | string) => {
|
||||
const updatedFilters = setFilters({
|
||||
ndParams: {
|
||||
...filter.ndParams,
|
||||
year: e,
|
||||
year: e === '' ? undefined : (e as number),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ export const NavidromeSongFilters = ({ handleFilterChange }: NavidromeSongFilter
|
|||
min={0}
|
||||
value={filter.ndParams?.year}
|
||||
width={50}
|
||||
onChange={handleYearFilter}
|
||||
onChange={(e) => handleYearFilter(e)}
|
||||
/>
|
||||
<Select
|
||||
clearable
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export const SongListContent = ({ customFilters, itemCount, tableRef }: SongList
|
|||
);
|
||||
|
||||
const songs = api.normalize.songList(songsRes, server);
|
||||
params.successCallback(songs?.items || [], songsRes?.totalRecordCount);
|
||||
params.successCallback(songs?.items || [], songsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, ChangeEvent, MutableRefObject, MouseEvent } from 'react';
|
||||
import { useCallback, useMemo, ChangeEvent, MutableRefObject, MouseEvent } from 'react';
|
||||
import { IDatasource } from '@ag-grid-community/core';
|
||||
import { Flex, Group, Stack } from '@mantine/core';
|
||||
import { openModal } from '@mantine/modals';
|
||||
|
|
@ -6,9 +6,13 @@ import {
|
|||
RiSortAsc,
|
||||
RiSortDesc,
|
||||
RiFolder2Line,
|
||||
RiFilter3Line,
|
||||
RiMoreFill,
|
||||
RiSettings3Fill,
|
||||
RiPlayFill,
|
||||
RiAddBoxFill,
|
||||
RiAddCircleFill,
|
||||
RiRefreshLine,
|
||||
RiFilterFill,
|
||||
} from 'react-icons/ri';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
|
|
@ -138,7 +142,7 @@ export const SongListHeaderFilters = ({
|
|||
);
|
||||
|
||||
const songs = api.normalize.songList(songsRes, server);
|
||||
params.successCallback(songs?.items || [], songsRes?.totalRecordCount);
|
||||
params.successCallback(songs?.items || [], songsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
|
|
@ -274,6 +278,22 @@ export const SongListHeaderFilters = ({
|
|||
});
|
||||
};
|
||||
|
||||
const isFilterApplied = useMemo(() => {
|
||||
const isNavidromeFilterApplied =
|
||||
server?.type === ServerType.NAVIDROME &&
|
||||
page.filter.ndParams &&
|
||||
Object.values(page.filter.ndParams).some((value) => value !== undefined);
|
||||
|
||||
const isJellyfinFilterApplied =
|
||||
server?.type === ServerType.JELLYFIN &&
|
||||
page.filter.jfParams &&
|
||||
Object.values(page.filter.jfParams)
|
||||
.filter((value) => value !== 'Audio') // Don't account for includeItemTypes: Audio
|
||||
.some((value) => value !== undefined);
|
||||
|
||||
return isNavidromeFilterApplied || isJellyfinFilterApplied;
|
||||
}, [page.filter.jfParams, page.filter.ndParams, server?.type]);
|
||||
|
||||
return (
|
||||
<Flex justify="space-between">
|
||||
<Group
|
||||
|
|
@ -350,15 +370,6 @@ export const SongListHeaderFilters = ({
|
|||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
<Button
|
||||
compact
|
||||
fw="600"
|
||||
size="md"
|
||||
variant="subtle"
|
||||
onClick={handleOpenFiltersModal}
|
||||
>
|
||||
{cq.isSm ? 'Filters' : <RiFilter3Line size="1.3rem" />}
|
||||
</Button>
|
||||
<DropdownMenu position="bottom-start">
|
||||
<DropdownMenu.Target>
|
||||
<Button
|
||||
|
|
@ -371,20 +382,49 @@ export const SongListHeaderFilters = ({
|
|||
</Button>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
<DropdownMenu.Item onClick={() => handlePlay(Play.NOW)}>Play</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onClick={() => handlePlay(Play.LAST)}>
|
||||
<DropdownMenu.Item
|
||||
icon={<RiPlayFill />}
|
||||
onClick={() => handlePlay(Play.NOW)}
|
||||
>
|
||||
Play
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
icon={<RiAddBoxFill />}
|
||||
onClick={() => handlePlay(Play.LAST)}
|
||||
>
|
||||
Add to queue
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onClick={() => handlePlay(Play.NEXT)}>
|
||||
<DropdownMenu.Item
|
||||
icon={<RiAddCircleFill />}
|
||||
onClick={() => handlePlay(Play.NEXT)}
|
||||
>
|
||||
Add to queue next
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Divider />
|
||||
<DropdownMenu.Item onClick={handleRefresh}>Refresh</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
icon={<RiRefreshLine />}
|
||||
onClick={handleRefresh}
|
||||
>
|
||||
Refresh
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
</Group>
|
||||
<Group>
|
||||
<DropdownMenu position="bottom-start">
|
||||
<Group
|
||||
noWrap
|
||||
spacing="sm"
|
||||
>
|
||||
<Button
|
||||
compact
|
||||
size="md"
|
||||
sx={{ svg: { fill: isFilterApplied ? 'var(--primary-color) !important' : undefined } }}
|
||||
tooltip={{ label: 'Filters' }}
|
||||
variant="subtle"
|
||||
onClick={handleOpenFiltersModal}
|
||||
>
|
||||
<RiFilterFill size="1.3rem" />
|
||||
</Button>
|
||||
<DropdownMenu position="bottom-end">
|
||||
<DropdownMenu.Target>
|
||||
<Button
|
||||
compact
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export const SongListHeader = ({
|
|||
);
|
||||
|
||||
const songs = api.normalize.songList(songsRes, server);
|
||||
params.successCallback(songs?.items || [], songsRes?.totalRecordCount);
|
||||
params.successCallback(songs?.items || [], songsRes?.totalRecordCount || 0);
|
||||
},
|
||||
rowCount: undefined,
|
||||
};
|
||||
|
|
@ -114,13 +114,11 @@ export const SongListHeader = ({
|
|||
<PageHeader backgroundColor="var(--titlebar-bg)">
|
||||
<Flex
|
||||
justify="space-between"
|
||||
py="1rem"
|
||||
w="100%"
|
||||
>
|
||||
<LibraryHeaderBar>
|
||||
<Group>
|
||||
<LibraryHeaderBar.PlayButton onClick={() => handlePlay(playButtonBehavior)} />
|
||||
<LibraryHeaderBar.Title>{title || 'Tracks'}</LibraryHeaderBar.Title>
|
||||
</Group>
|
||||
<LibraryHeaderBar.PlayButton onClick={() => handlePlay(playButtonBehavior)} />
|
||||
<LibraryHeaderBar.Title>{title || 'Tracks'}</LibraryHeaderBar.Title>
|
||||
<Paper
|
||||
fw="600"
|
||||
px="1rem"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue