Browse Source

Update default prettier config

- Change printWidth from 80 -> 100
master
jeffvli 3 years ago
parent
commit
2243d40a66
  1. 4
      .prettierrc
  2. 15
      package.json
  3. 5
      src/App.tsx
  4. 4
      src/__tests__/App.test.tsx
  5. 35
      src/api/api.ts
  6. 28
      src/components/card/Card.tsx
  7. 27
      src/components/debug/DebugWindow.tsx
  8. 9
      src/components/layout/GenericPage.tsx
  9. 13
      src/components/layout/GenericPageHeader.tsx
  10. 5
      src/components/layout/Layout.tsx
  11. 5
      src/components/layout/NavToggle.tsx
  12. 25
      src/components/layout/Titlebar.tsx
  13. 22
      src/components/layout/styled.tsx
  14. 4
      src/components/library/AlbumList.tsx
  15. 33
      src/components/library/AlbumView.tsx
  16. 27
      src/components/library/ArtistView.tsx
  17. 19
      src/components/library/LibraryView.tsx
  18. 6
      src/components/loader/PageLoader.tsx
  19. 21
      src/components/modal/PageModal.tsx
  20. 13
      src/components/player/NowPlayingMiniView.tsx
  21. 6
      src/components/player/NowPlayingView.tsx
  22. 111
      src/components/player/Player.tsx
  23. 155
      src/components/player/PlayerBar.tsx
  24. 19
      src/components/player/styled.tsx
  25. 22
      src/components/playlist/PlaylistList.tsx
  26. 62
      src/components/playlist/PlaylistView.tsx
  27. 20
      src/components/scrollingmenu/ScrollingMenu.tsx
  28. 7
      src/components/selectionbar/SelectionBar.tsx
  29. 24
      src/components/selectionbar/SelectionButtons.tsx
  30. 4
      src/components/settings/Config.tsx
  31. 43
      src/components/settings/ConfigPanels/CacheConfig.tsx
  32. 5
      src/components/settings/ConfigPanels/DebugConfig.tsx
  33. 19
      src/components/settings/ConfigPanels/ListViewConfig.tsx
  34. 23
      src/components/settings/ConfigPanels/LookAndFeelConfig.tsx
  35. 48
      src/components/settings/ConfigPanels/PlaybackConfig.tsx
  36. 9
      src/components/settings/ConfigPanels/PlayerConfig.tsx
  37. 61
      src/components/shared/ContextMenu.tsx
  38. 17
      src/components/shared/ToolbarButtons.tsx
  39. 30
      src/components/shared/styled.ts
  40. 5
      src/components/shared/toast.ts
  41. 26
      src/components/starred/StarredView.tsx
  42. 21
      src/components/viewtypes/GridViewType.tsx
  43. 152
      src/components/viewtypes/ListViewTable.tsx
  44. 8
      src/components/viewtypes/ListViewType.tsx
  45. 6
      src/components/viewtypes/ViewTypeButtons.tsx
  46. 10
      src/components/viewtypes/styled.tsx
  47. 10
      src/hooks/useSearchQuery.ts
  48. 4
      src/index.html
  49. 53
      src/main.dev.js
  50. 39
      src/menu.ts
  51. 11
      src/redux/miscSlice.ts
  52. 16
      src/redux/multiSelectSlice.ts
  53. 146
      src/redux/playQueueSlice.ts
  54. 44
      src/shared/utils.ts
  55. 7
      yarn.lock

4
.prettierrc

@ -2,5 +2,7 @@
"trailingComma": "es5", "trailingComma": "es5",
"tabWidth": 2, "tabWidth": 2,
"semi": true, "semi": true,
"singleQuote": false "singleQuote": true,
"printWidth": 100,
"arrowParens": "always"
} }

15
package.json

@ -297,21 +297,6 @@
"yarn": ">=1.21.3" "yarn": ">=1.21.3"
}, },
"browserslist": [], "browserslist": [],
"prettier": {
"overrides": [
{
"files": [
".prettierrc",
".babelrc",
".eslintrc"
],
"options": {
"parser": "json"
}
}
],
"singleQuote": true
},
"renovate": { "renovate": {
"extends": [ "extends": [
"bliss" "bliss"

5
src/App.tsx

@ -55,10 +55,7 @@ const App = () => {
document.getElementById('local-search-input')?.focus(); document.getElementById('local-search-input')?.focus();
}, []); }, []);
if ( if (!localStorage.getItem('server') || !localStorage.getItem('serverBase64')) {
!localStorage.getItem('server') ||
!localStorage.getItem('serverBase64')
) {
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<Layout disableSidebar footer={<MockFooter />} font={font}> <Layout disableSidebar footer={<MockFooter />} font={font}>

4
src/__tests__/App.test.tsx

@ -9,9 +9,7 @@ import { Player } from '../redux/playerSlice';
import { General } from '../redux/miscSlice'; import { General } from '../redux/miscSlice';
import App from '../App'; import App from '../App';
const middlewares: const middlewares: Middleware<Record<string, unknown>, any, Dispatch<AnyAction>>[] | undefined = [];
| Middleware<Record<string, unknown>, any, Dispatch<AnyAction>>[]
| undefined = [];
const mockStore = configureMockStore(middlewares); const mockStore = configureMockStore(middlewares);
const playQueueState: PlayQueue = { const playQueueState: PlayQueue = {

35
src/api/api.ts

@ -141,18 +141,13 @@ export const getPlaylists = async (sortBy: string) => {
return a.changed > b.changed ? -1 : a.changed < b.changed ? 1 : 0; return a.changed > b.changed ? -1 : a.changed < b.changed ? 1 : 0;
}) })
: sortBy === 'name' : sortBy === 'name'
? _.orderBy( ? _.orderBy(data.playlists.playlist || [], [(entry) => entry.name.toLowerCase()], 'asc')
data.playlists.playlist || [],
[(entry) => entry.name.toLowerCase()],
'asc'
)
: data.playlists?.playlist; : data.playlists?.playlist;
return (newData || []).map((playlist: any) => ({ return (newData || []).map((playlist: any) => ({
...playlist, ...playlist,
name: playlist.name, name: playlist.name,
image: image: playlist.songCount > 0 ? getCoverArtUrl(playlist) : 'img/placeholder.jpg',
playlist.songCount > 0 ? getCoverArtUrl(playlist) : 'img/placeholder.jpg',
})); }));
}; };
@ -168,10 +163,7 @@ export const getPlaylist = async (id: string) => {
index, index,
uniqueId: nanoid(), uniqueId: nanoid(),
})), })),
image: image: data.playlist.songCount > 0 ? getCoverArtUrl(data.playlist) : 'img/placeholder.jpg',
data.playlist.songCount > 0
? getCoverArtUrl(data.playlist)
: 'img/placeholder.jpg',
}; };
}; };
@ -250,8 +242,7 @@ export const getAlbumsDirect = async (options: any, coverArtSize = 150) => {
params: options, params: options,
}); });
const albums = (data.albumList2.album || []).map( const albums = (data.albumList2.album || []).map((entry: any, index: any) => ({
(entry: any, index: any) => ({
...entry, ...entry,
albumId: entry.id, albumId: entry.id,
image: getCoverArtUrl(entry, coverArtSize), image: getCoverArtUrl(entry, coverArtSize),
@ -259,8 +250,7 @@ export const getAlbumsDirect = async (options: any, coverArtSize = 150) => {
type: 'album', type: 'album',
index, index,
uniqueId: nanoid(), uniqueId: nanoid(),
}) }));
);
return albums; return albums;
}; };
@ -348,9 +338,7 @@ export const getArtists = async () => {
const { data } = await api.get(`/getArtists`); const { data } = await api.get(`/getArtists`);
const artistList: any[] = []; const artistList: any[] = [];
const artists = (data.artists?.index || []).flatMap( const artists = (data.artists?.index || []).flatMap((index: any) => index.artist);
(index: any) => index.artist
);
artists.map((artist: any) => artists.map((artist: any) =>
artistList.push({ artistList.push({
@ -523,11 +511,7 @@ export const setRating = async (id: string, rating: number) => {
return data; return data;
}; };
export const getSimilarSongs = async ( export const getSimilarSongs = async (id: string, count: number, coverArtSize = 150) => {
id: string,
count: number,
coverArtSize = 150
) => {
const { data } = await api.get(`/getSimilarSongs2`, { const { data } = await api.get(`/getSimilarSongs2`, {
params: { id, count }, params: { id, count },
}); });
@ -559,10 +543,7 @@ export const updatePlaylistSongs = async (id: string, entry: any[]) => {
return data; return data;
}; };
export const updatePlaylistSongsLg = async ( export const updatePlaylistSongsLg = async (playlistId: string, entry: any[]) => {
playlistId: string,
entry: any[]
) => {
const entryIds = _.map(entry, 'id'); const entryIds = _.map(entry, 'id');
// Set these in chunks so the api doesn't break // Set these in chunks so the api doesn't break

28
src/components/card/Card.tsx

@ -7,11 +7,7 @@ import { useQueryClient } from 'react-query';
import cacheImage from '../shared/cacheImage'; import cacheImage from '../shared/cacheImage';
import { getAlbum, getPlaylist, star, unstar } from '../../api/api'; import { getAlbum, getPlaylist, star, unstar } from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { import { appendPlayQueue, fixPlayer2Index, setPlayQueue } from '../../redux/playQueueSlice';
appendPlayQueue,
fixPlayer2Index,
setPlayQueue,
} from '../../redux/playQueueSlice';
import { isCached, getImageCachePath } from '../../shared/utils'; import { isCached, getImageCachePath } from '../../shared/utils';
import { import {
@ -126,9 +122,7 @@ const Card = ({
{lazyLoad ? ( {lazyLoad ? (
<LazyCardImg <LazyCardImg
src={ src={
isCached( isCached(`${cachePath}${rest.details.cacheType}_${rest.details.id}.jpg`)
`${cachePath}${rest.details.cacheType}_${rest.details.id}.jpg`
)
? `${cachePath}${rest.details.cacheType}_${rest.details.id}.jpg` ? `${cachePath}${rest.details.cacheType}_${rest.details.id}.jpg`
: rest.coverArt : rest.coverArt
} }
@ -147,12 +141,7 @@ const Card = ({
}} }}
/> />
) : ( ) : (
<CardImg <CardImg src={rest.coverArt} alt="img" onClick={handleClick} cardsize={size} />
src={rest.coverArt}
alt="img"
onClick={handleClick}
cardsize={size}
/>
)} )}
{hasHoverButtons && ( {hasHoverButtons && (
@ -172,9 +161,7 @@ const Card = ({
<FavoriteOverlayButton <FavoriteOverlayButton
onClick={handleFavorite} onClick={handleFavorite}
size="xs" size="xs"
icon={ icon={<Icon icon={rest.details.starred ? 'heart' : 'heart-o'} />}
<Icon icon={rest.details.starred ? 'heart' : 'heart-o'} />
}
/> />
)} )}
{!rest.isModal && ( {!rest.isModal && (
@ -189,12 +176,7 @@ const Card = ({
</Overlay> </Overlay>
<InfoPanel cardsize={size}> <InfoPanel cardsize={size}>
<InfoSpan> <InfoSpan>
<CardTitleButton <CardTitleButton appearance="link" size="sm" onClick={handleClick} cardsize={size}>
appearance="link"
size="sm"
onClick={handleClick}
cardsize={size}
>
{rest.title} {rest.title}
</CardTitleButton> </CardTitleButton>
</InfoSpan> </InfoSpan>

27
src/components/debug/DebugWindow.tsx

@ -145,9 +145,7 @@ const DebugWindow = ({ ...rest }) => {
<table style={{ tableLayout: 'fixed', textAlign: 'center' }}> <table style={{ tableLayout: 'fixed', textAlign: 'center' }}>
<tbody> <tbody>
<tr> <tr>
<th style={{ textAlign: 'left' }}> <th style={{ textAlign: 'left' }}>Player [{playQueue.currentPlayer}]</th>
Player [{playQueue.currentPlayer}]
</th>
<th <th
style={{ style={{
color: 'rgb(0, 150, 0)', color: 'rgb(0, 150, 0)',
@ -170,12 +168,8 @@ const DebugWindow = ({ ...rest }) => {
</tr> </tr>
<tr> <tr>
<td style={{ width: '80px', textAlign: 'left' }}>volume</td> <td style={{ width: '80px', textAlign: 'left' }}>volume</td>
<td style={{ width: '65px' }}> <td style={{ width: '65px' }}>{Number(playQueue.player1.volume).toFixed(2)}</td>
{Number(playQueue.player1.volume).toFixed(2)} <td style={{ width: '65px' }}>{Number(playQueue.player2.volume).toFixed(2)}</td>
</td>
<td style={{ width: '65px' }}>
{Number(playQueue.player2.volume).toFixed(2)}
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -186,10 +180,7 @@ const DebugWindow = ({ ...rest }) => {
<h6>Volume fade</h6> <h6>Volume fade</h6>
</FlexboxGrid.Item> </FlexboxGrid.Item>
<FlexboxGrid.Item> <FlexboxGrid.Item>
<Button <Button size="xs" onClick={() => dispatch(setFadeData({ clear: true }))}>
size="xs"
onClick={() => dispatch(setFadeData({ clear: true }))}
>
Reset Reset
</Button> </Button>
</FlexboxGrid.Item> </FlexboxGrid.Item>
@ -224,18 +215,16 @@ const DebugWindow = ({ ...rest }) => {
}} }}
> >
<li> <li>
lastSelected: [{multiSelect.lastSelected.rowIndex}]{' '} lastSelected: [{multiSelect.lastSelected.rowIndex}] {multiSelect.lastSelected.title}{' '}
{multiSelect.lastSelected.title} {multiSelect.lastSelected.id} {multiSelect.lastSelected.id}
</li> </li>
<li> <li>
range (start): [ range (start): [{multiSelect.lastRangeSelected.lastSelected.rowIndex}]{' '}
{multiSelect.lastRangeSelected.lastSelected.rowIndex}]{' '}
{multiSelect.lastRangeSelected.lastSelected.title}{' '} {multiSelect.lastRangeSelected.lastSelected.title}{' '}
{multiSelect.lastRangeSelected.lastSelected.id} {multiSelect.lastRangeSelected.lastSelected.id}
</li> </li>
<li> <li>
range (end): [ range (end): [{multiSelect.lastRangeSelected.lastRangeSelected.rowIndex}]{' '}
{multiSelect.lastRangeSelected.lastRangeSelected.rowIndex}]{' '}
{multiSelect.lastRangeSelected.lastRangeSelected.title}{' '} {multiSelect.lastRangeSelected.lastRangeSelected.title}{' '}
{multiSelect.lastRangeSelected.lastRangeSelected.id} {multiSelect.lastRangeSelected.lastRangeSelected.id}
</li> </li>

9
src/components/layout/GenericPage.tsx

@ -14,10 +14,7 @@ const GenericPage = ({ header, children, hideDivider, ...rest }: any) => {
useEffect(() => { useEffect(() => {
if (misc.dynamicBackground) { if (misc.dynamicBackground) {
const cachedImagePath = `${cachePath}album_${playQueue.current?.albumId}.jpg`; const cachedImagePath = `${cachePath}album_${playQueue.current?.albumId}.jpg`;
const serverImagePath = playQueue.current?.image.replace( const serverImagePath = playQueue.current?.image.replace(/size=\d+/, 'size=500');
/size=\d+/,
'size=500'
);
const cssBackgroundImagePath = `${cachePath}album_${playQueue.current?.albumId}.jpg`.replaceAll( const cssBackgroundImagePath = `${cachePath}album_${playQueue.current?.albumId}.jpg`.replaceAll(
'\\', '\\',
'/' '/'
@ -28,9 +25,7 @@ const GenericPage = ({ header, children, hideDivider, ...rest }: any) => {
preloadImage.src = serverImagePath; preloadImage.src = serverImagePath;
} }
const imagePath = isCached(cachedImagePath) const imagePath = isCached(cachedImagePath) ? cssBackgroundImagePath : serverImagePath;
? cssBackgroundImagePath
: serverImagePath;
setBackgroundImage(imagePath); setBackgroundImage(imagePath);
} }

13
src/components/layout/GenericPageHeader.tsx

@ -60,9 +60,7 @@ const GenericPageHeader = ({
alignSelf: 'center', alignSelf: 'center',
}} }}
> >
{sidetitle && ( {sidetitle && <span style={{ display: 'inline-block' }}>{sidetitle}</span>}
<span style={{ display: 'inline-block' }}>{sidetitle}</span>
)}
{showSearchBar && ( {showSearchBar && (
<span style={{ display: 'inline-block' }}> <span style={{ display: 'inline-block' }}>
<StyledInputGroup inside> <StyledInputGroup inside>
@ -74,10 +72,7 @@ const GenericPageHeader = ({
onChange={handleSearch} onChange={handleSearch}
/> />
{searchQuery !== '' && ( {searchQuery !== '' && (
<InputGroup.Button <InputGroup.Button appearance="subtle" onClick={clearSearchQuery}>
appearance="subtle"
onClick={clearSearchQuery}
>
<Icon icon="close" /> <Icon icon="close" />
</InputGroup.Button> </InputGroup.Button>
)} )}
@ -105,9 +100,7 @@ const GenericPageHeader = ({
{subtitle} {subtitle}
</span> </span>
<span style={{ alignSelf: 'center' }}> <span style={{ alignSelf: 'center' }}>
{subsidetitle && ( {subsidetitle && <span style={{ display: 'inline-block' }}>{subsidetitle}</span>}
<span style={{ display: 'inline-block' }}>{subsidetitle}</span>
)}
{showViewTypeButtons && ( {showViewTypeButtons && (
<span style={{ display: 'inline-block' }}> <span style={{ display: 'inline-block' }}>
<ViewTypeButtons <ViewTypeButtons

5
src/components/layout/Layout.tsx

@ -74,10 +74,7 @@ const Layout = ({ footer, children, disableSidebar, font }: any) => {
}) })
); );
} }
if ( if (multiSelect.selected.length > 0 && !multiSelect.isSelectDragging) {
multiSelect.selected.length > 0 &&
!multiSelect.isSelectDragging
) {
dispatch(clearSelected()); dispatch(clearSelected());
} }
}} }}

5
src/components/layout/NavToggle.tsx

@ -20,10 +20,7 @@ const NavToggle = ({ expand, onChange }: any) => {
</Nav> </Nav>
<Nav pullRight> <Nav pullRight>
<Nav.Item <Nav.Item onClick={onChange} style={{ width: 56, textAlign: 'center' }}>
onClick={onChange}
style={{ width: 56, textAlign: 'center' }}
>
<Icon icon={expand ? 'angle-left' : 'angle-right'} /> <Icon icon={expand ? 'angle-left' : 'angle-right'} />
</Nav.Item> </Nav.Item>
</Nav> </Nav>

25
src/components/layout/Titlebar.tsx

@ -1,10 +1,5 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { import { TitleHeader, DragRegion, WindowControl, WindowControlButton } from './styled';
TitleHeader,
DragRegion,
WindowControl,
WindowControlButton,
} from './styled';
import { useAppSelector } from '../../redux/hooks'; import { useAppSelector } from '../../redux/hooks';
const Titlebar = ({ font }: any) => { const Titlebar = ({ font }: any) => {
@ -21,16 +16,12 @@ const Titlebar = ({ font }: any) => {
const currentEntryList = playQueue.shuffle ? 'shuffledEntry' : 'entry'; const currentEntryList = playQueue.shuffle ? 'shuffledEntry' : 'entry';
const playStatus = const playStatus =
player.status !== 'PLAYING' && playQueue[currentEntryList].length > 0 player.status !== 'PLAYING' && playQueue[currentEntryList].length > 0 ? '(Paused)' : '';
? '(Paused)'
: '';
const songTitle = playQueue[currentEntryList][playQueue.currentIndex]?.title const songTitle = playQueue[currentEntryList][playQueue.currentIndex]?.title
? `(${playQueue.currentIndex + 1} / ${ ? `(${playQueue.currentIndex + 1} / ${playQueue[currentEntryList].length}) ~ ${
playQueue[currentEntryList].length playQueue[currentEntryList][playQueue.currentIndex]?.title
}) ~ ${playQueue[currentEntryList][playQueue.currentIndex]?.title} ~ ${ } ~ ${playQueue[currentEntryList][playQueue.currentIndex]?.artist} `
playQueue[currentEntryList][playQueue.currentIndex]?.artist
} `
: 'sonixd'; : 'sonixd';
setTitle(`${playStatus} ${songTitle}`); setTitle(`${playStatus} ${songTitle}`);
@ -59,11 +50,7 @@ const Titlebar = ({ font }: any) => {
alt="" alt=""
/> />
</WindowControlButton> </WindowControlButton>
<WindowControlButton <WindowControlButton restoreButton className="button" id="restore-button">
restoreButton
className="button"
id="restore-button"
>
<img <img
className="icon" className="icon"
srcSet="img/icons/restore-w-10.png 1x, img/icons/restore-w-12.png 1.25x, img/icons/restore-w-15.png 1.5x, img/icons/restore-w-15.png 1.75x, img/icons/restore-w-20.png 2x, img/icons/restore-w-20.png 2.25x, img/icons/restore-w-24.png 2.5x, img/icons/restore-w-30.png 3x, img/icons/restore-w-30.png 3.5x" srcSet="img/icons/restore-w-10.png 1x, img/icons/restore-w-12.png 1.25x, img/icons/restore-w-15.png 1.5x, img/icons/restore-w-15.png 1.75x, img/icons/restore-w-20.png 2x, img/icons/restore-w-20.png 2.25x, img/icons/restore-w-24.png 2.5x, img/icons/restore-w-30.png 3x, img/icons/restore-w-30.png 3.5x"

22
src/components/layout/styled.tsx

@ -19,12 +19,9 @@ interface ContainerProps {
children: any; children: any;
} }
const StyledContainer = ({ const StyledContainer = ({ id, expanded, children, ...props }: ContainerProps) => (
id, <Container {...props}>{children}</Container>
expanded, );
children,
...props
}: ContainerProps) => <Container {...props}>{children}</Container>;
export const MainContainer = styled(StyledContainer)` export const MainContainer = styled(StyledContainer)`
padding-left: ${(props) => (props.expanded ? '193px' : '56px')}; padding-left: ${(props) => (props.expanded ? '193px' : '56px')};
@ -44,8 +41,7 @@ export const TitleHeader = styled.header<{ font: string }>`
display: block; display: block;
position: fixed; position: fixed;
height: 32px; height: 32px;
width: ${(props) => width: ${(props) => (props.className?.includes('maximized') ? '100%' : 'calc(100%)')};
props.className?.includes('maximized') ? '100%' : 'calc(100%)'};
background: ${(props) => props.theme.primary.titleBar}; background: ${(props) => props.theme.primary.titleBar};
padding: 4px; padding: 4px;
color: ${(props) => props.theme.primary.titleText}; color: ${(props) => props.theme.primary.titleText};
@ -86,8 +82,7 @@ export const WindowControlButton = styled.div<{
align-items: center; align-items: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
grid-column: ${(props) => grid-column: ${(props) => (props.minButton ? 1 : props.maxButton || props.restoreButton ? 2 : 3)};
props.minButton ? 1 : props.maxButton || props.restoreButton ? 2 : 3};
&:hover { &:hover {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
@ -113,9 +108,7 @@ export const PageContainer = styled(Container)<{ $backgroundSrc?: string }>`
right: 0; right: 0;
display: block; display: block;
background-image: ${ background-image: ${props.$backgroundSrc ? `url(${props.$backgroundSrc})` : undefined};
props.$backgroundSrc ? `url(${props.$backgroundSrc})` : undefined
};
transition: background-image 1s ease-in-out; transition: background-image 1s ease-in-out;
width: 100%; width: 100%;
@ -155,8 +148,7 @@ export const FixedSidebar = styled(Sidebar)<{ font: string }>`
export const CoverArtWrapper = styled.div` export const CoverArtWrapper = styled.div`
display: inline-block; display: inline-block;
filter: ${(props) => filter: ${(props) => `drop-shadow(0px 5px 8px ${props.theme.primary.coverArtShadow})`};
`drop-shadow(0px 5px 8px ${props.theme.primary.coverArtShadow})`};
`; `;
export const PageHeaderTitle = styled.h1` export const PageHeaderTitle = styled.h1`

4
src/components/library/AlbumList.tsx

@ -54,9 +54,7 @@ const AlbumList = () => {
dispatch(toggleSelected(rowData)); dispatch(toggleSelected(rowData));
} else if (e.shiftKey) { } else if (e.shiftKey) {
dispatch(setRangeSelected(rowData)); dispatch(setRangeSelected(rowData));
dispatch( dispatch(toggleRangeSelected(searchQuery !== '' ? filteredData : albums));
toggleRangeSelected(searchQuery !== '' ? filteredData : albums)
);
} }
}, 100); }, 100);
} }

33
src/components/library/AlbumView.tsx

@ -3,11 +3,7 @@ import settings from 'electron-settings';
import { ButtonToolbar, Tag } from 'rsuite'; import { ButtonToolbar, Tag } from 'rsuite';
import { useQuery, useQueryClient } from 'react-query'; import { useQuery, useQueryClient } from 'react-query';
import { useParams, useHistory } from 'react-router-dom'; import { useParams, useHistory } from 'react-router-dom';
import { import { FavoriteButton, PlayAppendButton, PlayButton } from '../shared/ToolbarButtons';
FavoriteButton,
PlayAppendButton,
PlayButton,
} from '../shared/ToolbarButtons';
import { getAlbum, star, unstar } from '../../api/api'; import { getAlbum, star, unstar } from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { import {
@ -44,9 +40,8 @@ const AlbumView = ({ ...rest }: any) => {
const { id } = useParams<AlbumParams>(); const { id } = useParams<AlbumParams>();
const albumId = rest.id ? rest.id : id; const albumId = rest.id ? rest.id : id;
const { isLoading, isError, data, error }: any = useQuery( const { isLoading, isError, data, error }: any = useQuery(['album', albumId], () =>
['album', albumId], getAlbum(albumId)
() => getAlbum(albumId)
); );
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const filteredData = useSearchQuery(searchQuery, data?.song, [ const filteredData = useSearchQuery(searchQuery, data?.song, [
@ -66,9 +61,7 @@ const AlbumView = ({ ...rest }: any) => {
dispatch(toggleSelected(rowData)); dispatch(toggleSelected(rowData));
} else if (e.shiftKey) { } else if (e.shiftKey) {
dispatch(setRangeSelected(rowData)); dispatch(setRangeSelected(rowData));
dispatch( dispatch(toggleRangeSelected(searchQuery !== '' ? filteredData : data.song));
toggleRangeSelected(searchQuery !== '' ? filteredData : data.song)
);
} }
}, 100); }, 100);
} }
@ -171,21 +164,9 @@ const AlbumView = ({ ...rest }: any) => {
</div> </div>
<div style={{ marginTop: '10px' }}> <div style={{ marginTop: '10px' }}>
<ButtonToolbar> <ButtonToolbar>
<PlayButton <PlayButton appearance="primary" size="lg" onClick={handlePlay} />
appearance="primary" <PlayAppendButton appearance="primary" size="lg" onClick={handlePlayAppend} />
size="lg" <FavoriteButton size="lg" isFavorite={data.starred} onClick={handleFavorite} />
onClick={handlePlay}
/>
<PlayAppendButton
appearance="primary"
size="lg"
onClick={handlePlayAppend}
/>
<FavoriteButton
size="lg"
isFavorite={data.starred}
onClick={handleFavorite}
/>
</ButtonToolbar> </ButtonToolbar>
</div> </div>
</div> </div>

27
src/components/library/ArtistView.tsx

@ -7,10 +7,7 @@ import { useParams, useHistory } from 'react-router-dom';
import { PlayAppendButton, PlayButton } from '../shared/ToolbarButtons'; import { PlayAppendButton, PlayButton } from '../shared/ToolbarButtons';
import { getArtist, getArtistInfo } from '../../api/api'; import { getArtist, getArtistInfo } from '../../api/api';
import { useAppDispatch } from '../../redux/hooks'; import { useAppDispatch } from '../../redux/hooks';
import { import { fixPlayer2Index, setPlayQueueByRowClick } from '../../redux/playQueueSlice';
fixPlayer2Index,
setPlayQueueByRowClick,
} from '../../redux/playQueueSlice';
import { import {
toggleSelected, toggleSelected,
setRangeSelected, setRangeSelected,
@ -35,16 +32,13 @@ interface ArtistParams {
const ArtistView = ({ ...rest }: any) => { const ArtistView = ({ ...rest }: any) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const history = useHistory(); const history = useHistory();
const [viewType, setViewType] = useState( const [viewType, setViewType] = useState(settings.getSync('albumViewType') || 'list');
settings.getSync('albumViewType') || 'list'
);
const { id } = useParams<ArtistParams>(); const { id } = useParams<ArtistParams>();
const artistId = rest.id ? rest.id : id; const artistId = rest.id ? rest.id : id;
const { isLoading, isError, data, error }: any = useQuery( const { isLoading, isError, data, error }: any = useQuery(['artist', artistId], () =>
['artist', artistId], getArtist(artistId)
() => getArtist(artistId)
); );
const { const {
isLoading: isLoadingAI, isLoading: isLoadingAI,
@ -54,10 +48,7 @@ const ArtistView = ({ ...rest }: any) => {
}: any = useQuery(['artistInfo', artistId], () => getArtistInfo(artistId, 8)); }: any = useQuery(['artistInfo', artistId], () => getArtistInfo(artistId, 8));
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const filteredData = useSearchQuery(searchQuery, data?.album, [ const filteredData = useSearchQuery(searchQuery, data?.album, ['name', 'artist']);
'name',
'artist',
]);
let timeout: any = null; let timeout: any = null;
const handleRowClick = (e: any, rowData: any) => { const handleRowClick = (e: any, rowData: any) => {
@ -69,9 +60,7 @@ const ArtistView = ({ ...rest }: any) => {
dispatch(toggleSelected(rowData)); dispatch(toggleSelected(rowData));
} else if (e.shiftKey) { } else if (e.shiftKey) {
dispatch(setRangeSelected(rowData)); dispatch(setRangeSelected(rowData));
dispatch( dispatch(toggleRangeSelected(searchQuery !== '' ? filteredData : data.album));
toggleRangeSelected(searchQuery !== '' ? filteredData : data.album)
);
} }
}, 100); }, 100);
} }
@ -147,9 +136,7 @@ const ArtistView = ({ ...rest }: any) => {
<TagLink <TagLink
onClick={() => { onClick={() => {
if (!rest.isModal) { if (!rest.isModal) {
history.push( history.push(`/library/artist/${artist.id}`);
`/library/artist/${artist.id}`
);
} else { } else {
dispatch( dispatch(
addModalPage({ addModalPage({

19
src/components/library/LibraryView.tsx

@ -28,21 +28,13 @@ const LibraryView = () => {
const [data, setData] = useState<any[]>([]); const [data, setData] = useState<any[]>([]);
const [offset, setOffset] = useState(0); const [offset, setOffset] = useState(0);
const [viewType, setViewType] = useState(settings.getSync('albumViewType')); const [viewType, setViewType] = useState(settings.getSync('albumViewType'));
const { isLoading: isLoadingArtists, data: artists }: any = useQuery( const { isLoading: isLoadingArtists, data: artists }: any = useQuery('artists', getArtists, {
'artists',
getArtists,
{
enabled: currentPage === 'artists', enabled: currentPage === 'artists',
} });
);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const filteredData = useSearchQuery( const filteredData = useSearchQuery(
searchQuery, searchQuery,
currentPage === 'artists' currentPage === 'artists' ? artists : currentPage === 'albums' ? data : data,
? artists
: currentPage === 'albums'
? data
: data,
['name', 'artist'] ['name', 'artist']
); );
@ -116,10 +108,7 @@ const LibraryView = () => {
{artists && ( {artists && (
<> <>
{currentPage === 'artists' && ( {currentPage === 'artists' && (
<ArtistList <ArtistList viewType={viewType} data={searchQuery === '' ? artists : filteredData} />
viewType={viewType}
data={searchQuery === '' ? artists : filteredData}
/>
)} )}
</> </>
)} )}

6
src/components/loader/PageLoader.tsx

@ -10,12 +10,10 @@ const LoaderContainer = styled.div`
div { div {
span { span {
&:after { &:after {
border-color: ${(props) => border-color: ${(props) => `${props.theme.primary.main} transparent transparent`};
`${props.theme.primary.main} transparent transparent`};
} }
&:before { &:before {
border: ${(props) => border: ${(props) => `3px solid ${props.theme.primary.spinnerBackground}`};
`3px solid ${props.theme.primary.spinnerBackground}`};
} }
} }
} }

21
src/components/modal/PageModal.tsx

@ -42,25 +42,14 @@ const PageModal = () => {
</StyledButton> </StyledButton>
</Modal.Header> </Modal.Header>
<Modal.Body style={{ height: '800px' }}> <Modal.Body style={{ height: '800px' }}>
{misc.modalPages[misc.modal.currentPageIndex]?.pageType === {misc.modalPages[misc.modal.currentPageIndex]?.pageType === 'artist' && (
'artist' && ( <ArtistView id={misc.modalPages[misc.modal.currentPageIndex].id} isModal />
<ArtistView
id={misc.modalPages[misc.modal.currentPageIndex].id}
isModal
/>
)} )}
{misc.modalPages[misc.modal.currentPageIndex]?.pageType === 'album' && ( {misc.modalPages[misc.modal.currentPageIndex]?.pageType === 'album' && (
<AlbumView <AlbumView id={misc.modalPages[misc.modal.currentPageIndex].id} isModal />
id={misc.modalPages[misc.modal.currentPageIndex].id}
isModal
/>
)} )}
{misc.modalPages[misc.modal.currentPageIndex]?.pageType === {misc.modalPages[misc.modal.currentPageIndex]?.pageType === 'playlist' && (
'playlist' && ( <PlaylistView id={misc.modalPages[misc.modal.currentPageIndex].id} isModal />
<PlaylistView
id={misc.modalPages[misc.modal.currentPageIndex].id}
isModal
/>
)} )}
</Modal.Body> </Modal.Body>
</StyledModal> </StyledModal>

13
src/components/player/NowPlayingMiniView.tsx

@ -50,12 +50,7 @@ const NowPlayingMiniView = () => {
); );
}, 100); }, 100);
} }
}, [ }, [playQueue.currentIndex, tableRef, playQueue.displayQueue, playQueue.scrollWithCurrentSong]);
playQueue.currentIndex,
tableRef,
playQueue.displayQueue,
playQueue.scrollWithCurrentSong,
]);
let timeout: any = null; let timeout: any = null;
const handleRowClick = (e: any, rowData: any) => { const handleRowClick = (e: any, rowData: any) => {
@ -143,11 +138,7 @@ const NowPlayingMiniView = () => {
padding="0px" padding="0px"
header={ header={
<> <>
<FlexboxGrid <FlexboxGrid justify="space-between" align="middle" style={{ height: '50px' }}>
justify="space-between"
align="middle"
style={{ height: '50px' }}
>
<FlexboxGrid.Item> <FlexboxGrid.Item>
<ButtonToolbar> <ButtonToolbar>
<StyledIconButton <StyledIconButton

6
src/components/player/NowPlayingView.tsx

@ -37,11 +37,7 @@ const NowPlayingView = () => {
const multiSelect = useAppSelector((state) => state.multiSelect); const multiSelect = useAppSelector((state) => state.multiSelect);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const filteredData = useSearchQuery(searchQuery, playQueue.entry, [ const filteredData = useSearchQuery(searchQuery, playQueue.entry, ['title', 'artist', 'album']);
'title',
'artist',
'album',
]);
useEffect(() => { useEffect(() => {
if (playQueue.scrollWithCurrentSong) { if (playQueue.scrollWithCurrentSong) {

111
src/components/player/Player.tsx

@ -49,8 +49,7 @@ const gaplessListenHandler = (
// Add a bit of leeway for the second track to start since the // Add a bit of leeway for the second track to start since the
// seek value doesn't always reach the duration // seek value doesn't always reach the duration
const durationPadding = const durationPadding = pollingInterval <= 10 ? 0.12 : pollingInterval <= 20 ? 0.13 : 0.15;
pollingInterval <= 10 ? 0.12 : pollingInterval <= 20 ? 0.13 : 0.15;
if (seek + durationPadding >= duration) { if (seek + durationPadding >= duration) {
nextPlayerRef.current.audioEl.current.play(); nextPlayerRef.current.audioEl.current.play();
} }
@ -68,15 +67,13 @@ const listenHandler = (
volumeFade: boolean, volumeFade: boolean,
debug: boolean debug: boolean
) => { ) => {
const currentSeek = const currentSeek = currentPlayerRef.current?.audioEl.current?.currentTime || 0;
currentPlayerRef.current?.audioEl.current?.currentTime || 0;
const duration = currentPlayerRef.current?.audioEl.current?.duration; const duration = currentPlayerRef.current?.audioEl.current?.duration;
const fadeAtTime = duration - fadeDuration; const fadeAtTime = duration - fadeDuration;
// Fade only if repeat is 'all' or if not on the last track // Fade only if repeat is 'all' or if not on the last track
if ( if (
playQueue[`player${player}`].index + 1 < playQueue[`player${player}`].index + 1 < playQueue[currentEntryList].length ||
playQueue[currentEntryList].length ||
playQueue.repeat === 'all' playQueue.repeat === 'all'
) { ) {
// Detect to start fading when seek is greater than the fade time // Detect to start fading when seek is greater than the fade time
@ -100,18 +97,15 @@ const listenHandler = (
Math.sqrt(0.5 * (2 - percentageOfFadeLeft)) * playQueue.volume; Math.sqrt(0.5 * (2 - percentageOfFadeLeft)) * playQueue.volume;
break; break;
case 'linear': case 'linear':
currentPlayerVolumeCalculation = currentPlayerVolumeCalculation = (timeLeft / fadeDuration) * playQueue.volume;
(timeLeft / fadeDuration) * playQueue.volume;
nextPlayerVolumeCalculation = nextPlayerVolumeCalculation =
((fadeDuration - timeLeft) / fadeDuration) * playQueue.volume; ((fadeDuration - timeLeft) / fadeDuration) * playQueue.volume;
break; break;
case 'dipped': case 'dipped':
// https://math.stackexchange.com/a/4622 // https://math.stackexchange.com/a/4622
percentageOfFadeLeft = timeLeft / fadeDuration; percentageOfFadeLeft = timeLeft / fadeDuration;
currentPlayerVolumeCalculation = currentPlayerVolumeCalculation = percentageOfFadeLeft ** 2 * playQueue.volume;
percentageOfFadeLeft ** 2 * playQueue.volume; nextPlayerVolumeCalculation = (percentageOfFadeLeft - 1) ** 2 * playQueue.volume;
nextPlayerVolumeCalculation =
(percentageOfFadeLeft - 1) ** 2 * playQueue.volume;
break; break;
case fadeType.match(/constantPower.*/)?.input: case fadeType.match(/constantPower.*/)?.input:
// https://math.stackexchange.com/a/26159 // https://math.stackexchange.com/a/26159
@ -126,29 +120,22 @@ const listenHandler = (
percentageOfFadeLeft = timeLeft / fadeDuration; percentageOfFadeLeft = timeLeft / fadeDuration;
currentPlayerVolumeCalculation = currentPlayerVolumeCalculation =
Math.cos( Math.cos((Math.PI / 4) * ((2 * percentageOfFadeLeft - 1) ** (2 * n + 1) - 1)) *
(Math.PI / 4) * playQueue.volume;
((2 * percentageOfFadeLeft - 1) ** (2 * n + 1) - 1)
) * playQueue.volume;
nextPlayerVolumeCalculation = nextPlayerVolumeCalculation =
Math.cos( Math.cos((Math.PI / 4) * ((2 * percentageOfFadeLeft - 1) ** (2 * n + 1) + 1)) *
(Math.PI / 4) * playQueue.volume;
((2 * percentageOfFadeLeft - 1) ** (2 * n + 1) + 1)
) * playQueue.volume;
break; break;
default: default:
currentPlayerVolumeCalculation = currentPlayerVolumeCalculation = (timeLeft / fadeDuration) * playQueue.volume;
(timeLeft / fadeDuration) * playQueue.volume;
nextPlayerVolumeCalculation = nextPlayerVolumeCalculation =
((fadeDuration - timeLeft) / fadeDuration) * playQueue.volume; ((fadeDuration - timeLeft) / fadeDuration) * playQueue.volume;
break; break;
} }
const currentPlayerVolume = const currentPlayerVolume =
currentPlayerVolumeCalculation >= 0 currentPlayerVolumeCalculation >= 0 ? currentPlayerVolumeCalculation : 0;
? currentPlayerVolumeCalculation
: 0;
const nextPlayerVolume = const nextPlayerVolume =
nextPlayerVolumeCalculation <= playQueue.volume nextPlayerVolumeCalculation <= playQueue.volume
@ -223,9 +210,7 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
const [fadeDuration, setFadeDuration] = useState(playQueue.fadeDuration); const [fadeDuration, setFadeDuration] = useState(playQueue.fadeDuration);
const [fadeType, setFadeType] = useState(playQueue.fadeType); const [fadeType, setFadeType] = useState(playQueue.fadeType);
const [volumeFade, setVolumeFade] = useState(playQueue.volumeFade); const [volumeFade, setVolumeFade] = useState(playQueue.volumeFade);
const [pollingInterval, setPollingInterval] = useState( const [pollingInterval, setPollingInterval] = useState(playQueue.pollingInterval);
playQueue.pollingInterval
);
const getSrc1 = useCallback(() => { const getSrc1 = useCallback(() => {
const cachedSongPath = `${cachePath}/${ const cachedSongPath = `${cachePath}/${
@ -369,16 +354,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
if (cacheSongs) { if (cacheSongs) {
cacheSong( cacheSong(
`${playQueue[currentEntryList][playQueue.player1.index].id}.mp3`, `${playQueue[currentEntryList][playQueue.player1.index].id}.mp3`,
playQueue[currentEntryList][playQueue.player1.index].streamUrl.replace( playQueue[currentEntryList][playQueue.player1.index].streamUrl.replace(/stream/, 'download')
/stream/,
'download'
)
); );
} }
if ( if (playQueue.repeat === 'none' && playQueue.player1.index > playQueue.player2.index) {
playQueue.repeat === 'none' &&
playQueue.player1.index > playQueue.player2.index
) {
dispatch(fixPlayer2Index()); dispatch(fixPlayer2Index());
setTimeout(() => { setTimeout(() => {
player1Ref.current.audioEl.current.pause(); player1Ref.current.audioEl.current.pause();
@ -387,15 +366,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
} else { } else {
if (!playQueue.autoIncremented) { if (!playQueue.autoIncremented) {
dispatch(incrementCurrentIndex('none')); dispatch(incrementCurrentIndex('none'));
dispatch( dispatch(setCurrentIndex(playQueue[currentEntryList][playQueue.player2.index]));
setCurrentIndex(playQueue[currentEntryList][playQueue.player2.index])
);
dispatch(setAutoIncremented(true)); dispatch(setAutoIncremented(true));
} }
if ( if (playQueue[currentEntryList].length > 1 || playQueue.repeat === 'all') {
playQueue[currentEntryList].length > 1 ||
playQueue.repeat === 'all'
) {
dispatch(setCurrentPlayer(2)); dispatch(setCurrentPlayer(2));
dispatch(incrementPlayerIndex(1)); dispatch(incrementPlayerIndex(1));
if (fadeDuration !== 0) { if (fadeDuration !== 0) {
@ -414,16 +388,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
if (cacheSongs) { if (cacheSongs) {
cacheSong( cacheSong(
`${playQueue[currentEntryList][playQueue.player2.index].id}.mp3`, `${playQueue[currentEntryList][playQueue.player2.index].id}.mp3`,
playQueue[currentEntryList][playQueue.player2.index].streamUrl.replace( playQueue[currentEntryList][playQueue.player2.index].streamUrl.replace(/stream/, 'download')
/stream/,
'download'
)
); );
} }
if ( if (playQueue.repeat === 'none' && playQueue.player2.index > playQueue.player1.index) {
playQueue.repeat === 'none' &&
playQueue.player2.index > playQueue.player1.index
) {
dispatch(fixPlayer2Index()); dispatch(fixPlayer2Index());
setTimeout(() => { setTimeout(() => {
player1Ref.current.audioEl.current.pause(); player1Ref.current.audioEl.current.pause();
@ -432,15 +400,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
} else { } else {
if (!playQueue.autoIncremented) { if (!playQueue.autoIncremented) {
dispatch(incrementCurrentIndex('none')); dispatch(incrementCurrentIndex('none'));
dispatch( dispatch(setCurrentIndex(playQueue[currentEntryList][playQueue.player1.index]));
setCurrentIndex(playQueue[currentEntryList][playQueue.player1.index])
);
dispatch(setAutoIncremented(true)); dispatch(setAutoIncremented(true));
} }
if ( if (playQueue[currentEntryList].length > 1 || playQueue.repeat === 'all') {
playQueue[currentEntryList].length > 1 ||
playQueue.repeat === 'all'
) {
dispatch(setCurrentPlayer(1)); dispatch(setCurrentPlayer(1));
dispatch(incrementPlayerIndex(2)); dispatch(incrementPlayerIndex(2));
if (fadeDuration !== 0) { if (fadeDuration !== 0) {
@ -454,25 +417,11 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
}; };
const handleGaplessPlayer1 = () => { const handleGaplessPlayer1 = () => {
gaplessListenHandler( gaplessListenHandler(player1Ref, player2Ref, playQueue, 1, dispatch, pollingInterval);
player1Ref,
player2Ref,
playQueue,
1,
dispatch,
pollingInterval
);
}; };
const handleGaplessPlayer2 = () => { const handleGaplessPlayer2 = () => {
gaplessListenHandler( gaplessListenHandler(player2Ref, player1Ref, playQueue, 2, dispatch, pollingInterval);
player2Ref,
player1Ref,
playQueue,
2,
dispatch,
pollingInterval
);
}; };
return ( return (
@ -486,9 +435,7 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
src={playQueue.player1.src} src={playQueue.player1.src}
listenInterval={pollingInterval} listenInterval={pollingInterval}
preload="auto" preload="auto"
onListen={ onListen={fadeDuration === 0 ? handleGaplessPlayer1 : handleListenPlayer1}
fadeDuration === 0 ? handleGaplessPlayer1 : handleListenPlayer1
}
onEnded={handleOnEndedPlayer1} onEnded={handleOnEndedPlayer1}
volume={playQueue.player1.volume} volume={playQueue.player1.volume}
autoPlay={ autoPlay={
@ -500,8 +447,7 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
if (playQueue[currentEntryList].length > 0) { if (playQueue[currentEntryList].length > 0) {
console.log('player error', e); console.log('player error', e);
player1Ref.current.audioEl.current.src = player1Ref.current.audioEl.current.src = './components/player/dummy.mp3';
'./components/player/dummy.mp3';
player1Ref.current.audioEl.current.src = getSrc1(); player1Ref.current.audioEl.current.src = getSrc1();
} }
}} }}
@ -512,9 +458,7 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
src={playQueue.player2.src} src={playQueue.player2.src}
listenInterval={pollingInterval} listenInterval={pollingInterval}
preload="auto" preload="auto"
onListen={ onListen={fadeDuration === 0 ? handleGaplessPlayer2 : handleListenPlayer2}
fadeDuration === 0 ? handleGaplessPlayer2 : handleListenPlayer2
}
onEnded={handleOnEndedPlayer2} onEnded={handleOnEndedPlayer2}
volume={playQueue.player2.volume} volume={playQueue.player2.volume}
autoPlay={ autoPlay={
@ -526,8 +470,7 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
if (playQueue[currentEntryList].length > 0) { if (playQueue[currentEntryList].length > 0) {
console.log('player error', e); console.log('player error', e);
player2Ref.current.audioEl.current.src = player2Ref.current.audioEl.current.src = './components/player/dummy.mp3';
'./components/player/dummy.mp3';
player2Ref.current.audioEl.current.src = getSrc2(); player2Ref.current.audioEl.current.src = getSrc2();
} }
}} }}

155
src/components/player/PlayerBar.tsx

@ -50,9 +50,7 @@ const PlayerBar = () => {
const [isDraggingVolume, setIsDraggingVolume] = useState(false); const [isDraggingVolume, setIsDraggingVolume] = useState(false);
const [manualSeek, setManualSeek] = useState(0); const [manualSeek, setManualSeek] = useState(0);
const [currentEntryList, setCurrentEntryList] = useState('entry'); const [currentEntryList, setCurrentEntryList] = useState('entry');
const [localVolume, setLocalVolume] = useState( const [localVolume, setLocalVolume] = useState(Number(settings.getSync('volume')));
Number(settings.getSync('volume'))
);
const playersRef = useRef<any>(); const playersRef = useRef<any>();
const history = useHistory(); const history = useHistory();
@ -91,13 +89,7 @@ const PlayerBar = () => {
}, 200); }, 200);
return () => clearTimeout(debounce); return () => clearTimeout(debounce);
}, [ }, [dispatch, isDraggingVolume, localVolume, playQueue.currentPlayer, playQueue.fadeDuration]);
dispatch,
isDraggingVolume,
localVolume,
playQueue.currentPlayer,
playQueue.fadeDuration,
]);
useEffect(() => { useEffect(() => {
// Set the seek back to 0 when the player is incremented/decremented, otherwise the // Set the seek back to 0 when the player is incremented/decremented, otherwise the
@ -164,9 +156,7 @@ const PlayerBar = () => {
const handleVolumeKey = (e: any) => { const handleVolumeKey = (e: any) => {
if (e.keyCode === keyCodes.UP) { if (e.keyCode === keyCodes.UP) {
const vol = Number( const vol = Number((playQueue.volume + 0.05 > 1 ? 1 : playQueue.volume + 0.05).toFixed(2));
(playQueue.volume + 0.05 > 1 ? 1 : playQueue.volume + 0.05).toFixed(2)
);
dispatch(setVolume(vol)); dispatch(setVolume(vol));
dispatch( dispatch(
setPlayerVolume({ setPlayerVolume({
@ -175,9 +165,7 @@ const PlayerBar = () => {
}) })
); );
} else if (e.keyCode === keyCodes.DOWN) { } else if (e.keyCode === keyCodes.DOWN) {
const vol = Number( const vol = Number((playQueue.volume - 0.05 < 0 ? 0 : playQueue.volume - 0.05).toFixed(2));
(playQueue.volume - 0.05 < 0 ? 0 : playQueue.volume - 0.05).toFixed(2)
);
dispatch(setVolume(vol)); dispatch(setVolume(vol));
dispatch( dispatch(
setPlayerVolume({ setPlayerVolume({
@ -190,9 +178,7 @@ const PlayerBar = () => {
const handleClickForward = () => { const handleClickForward = () => {
if (playQueue[currentEntryList].length > 0) { if (playQueue[currentEntryList].length > 0) {
const seekForwardInterval = Number( const seekForwardInterval = Number(settings.getSync('seekForwardInterval'));
settings.getSync('seekForwardInterval')
);
setIsDragging(true); setIsDragging(true);
if (playQueue.isFading) { if (playQueue.isFading) {
@ -211,30 +197,20 @@ const PlayerBar = () => {
if (playQueue.currentPlayer === 1) { if (playQueue.currentPlayer === 1) {
const calculatedTime = const calculatedTime =
playersRef.current.player1.audioEl.current.currentTime + playersRef.current.player1.audioEl.current.currentTime + seekForwardInterval;
seekForwardInterval; const songDuration = playersRef.current.player1.audioEl.current.duration;
const songDuration = setManualSeek(calculatedTime > songDuration ? songDuration - 1 : calculatedTime);
playersRef.current.player1.audioEl.current.duration;
setManualSeek(
calculatedTime > songDuration ? songDuration - 1 : calculatedTime
);
} else { } else {
const calculatedTime = const calculatedTime =
playersRef.current.player2.audioEl.current.currentTime + playersRef.current.player2.audioEl.current.currentTime + seekForwardInterval;
seekForwardInterval; const songDuration = playersRef.current.player2.audioEl.current.duration;
const songDuration = setManualSeek(calculatedTime > songDuration ? songDuration - 1 : calculatedTime);
playersRef.current.player2.audioEl.current.duration;
setManualSeek(
calculatedTime > songDuration ? songDuration - 1 : calculatedTime
);
} }
} }
}; };
const handleClickBackward = () => { const handleClickBackward = () => {
const seekBackwardInterval = Number( const seekBackwardInterval = Number(settings.getSync('seekBackwardInterval'));
settings.getSync('seekBackwardInterval')
);
if (playQueue[currentEntryList].length > 0) { if (playQueue[currentEntryList].length > 0) {
setIsDragging(true); setIsDragging(true);
@ -254,13 +230,11 @@ const PlayerBar = () => {
if (playQueue.currentPlayer === 1) { if (playQueue.currentPlayer === 1) {
const calculatedTime = const calculatedTime =
playersRef.current.player1.audioEl.current.currentTime - playersRef.current.player1.audioEl.current.currentTime - seekBackwardInterval;
seekBackwardInterval;
setManualSeek(calculatedTime < 0 ? 0 : calculatedTime); setManualSeek(calculatedTime < 0 ? 0 : calculatedTime);
} else { } else {
const calculatedTime = const calculatedTime =
playersRef.current.player2.audioEl.current.currentTime - playersRef.current.player2.audioEl.current.currentTime - seekBackwardInterval;
seekBackwardInterval;
setManualSeek(calculatedTime < 0 ? 0 : calculatedTime); setManualSeek(calculatedTime < 0 ? 0 : calculatedTime);
} }
} }
@ -290,12 +264,7 @@ const PlayerBar = () => {
const handleRepeat = () => { const handleRepeat = () => {
const currentRepeat = settings.getSync('repeat'); const currentRepeat = settings.getSync('repeat');
const newRepeat = const newRepeat = currentRepeat === 'none' ? 'all' : currentRepeat === 'all' ? 'one' : 'none';
currentRepeat === 'none'
? 'all'
: currentRepeat === 'all'
? 'one'
: 'none';
dispatch(toggleRepeat()); dispatch(toggleRepeat());
settings.setSync('repeat', newRepeat); settings.setSync('repeat', newRepeat);
}; };
@ -311,10 +280,7 @@ const PlayerBar = () => {
const handleFavorite = async () => { const handleFavorite = async () => {
if (!playQueue[currentEntryList][playQueue.currentIndex].starred) { if (!playQueue[currentEntryList][playQueue.currentIndex].starred) {
await star( await star(playQueue[currentEntryList][playQueue.currentIndex].id, 'music');
playQueue[currentEntryList][playQueue.currentIndex].id,
'music'
);
dispatch( dispatch(
setStar({ setStar({
id: playQueue[currentEntryList][playQueue.currentIndex].id, id: playQueue[currentEntryList][playQueue.currentIndex].id,
@ -322,10 +288,7 @@ const PlayerBar = () => {
}) })
); );
} else { } else {
await unstar( await unstar(playQueue[currentEntryList][playQueue.currentIndex].id, 'song');
playQueue[currentEntryList][playQueue.currentIndex].id,
'song'
);
dispatch( dispatch(
setStar({ setStar({
id: playQueue[currentEntryList][playQueue.currentIndex].id, id: playQueue[currentEntryList][playQueue.currentIndex].id,
@ -347,15 +310,10 @@ const PlayerBar = () => {
return ( return (
<Player ref={playersRef} currentEntryList={currentEntryList}> <Player ref={playersRef} currentEntryList={currentEntryList}>
{playQueue.showDebugWindow && ( {playQueue.showDebugWindow && <DebugWindow currentEntryList={currentEntryList} />}
<DebugWindow currentEntryList={currentEntryList} />
)}
<PlayerContainer> <PlayerContainer>
<FlexboxGrid align="middle" style={{ height: '100%' }}> <FlexboxGrid align="middle" style={{ height: '100%' }}>
<FlexboxGrid.Item <FlexboxGrid.Item colspan={6} style={{ textAlign: 'left', paddingLeft: '10px' }}>
colspan={6}
style={{ textAlign: 'left', paddingLeft: '10px' }}
>
<PlayerColumn left height="80px"> <PlayerColumn left height="80px">
<Grid> <Grid>
<Row <Row
@ -370,8 +328,8 @@ const PlayerBar = () => {
<LazyLoadImage <LazyLoadImage
tabIndex={0} tabIndex={0}
src={ src={
playQueue[currentEntryList][playQueue.currentIndex] playQueue[currentEntryList][playQueue.currentIndex]?.image ||
?.image || placeholderImg placeholderImg
} }
alt="trackImg" alt="trackImg"
effect="opacity" effect="opacity"
@ -400,30 +358,24 @@ const PlayerBar = () => {
enterable enterable
placement="topStart" placement="topStart"
text={ text={
playQueue[currentEntryList][playQueue.currentIndex] playQueue[currentEntryList][playQueue.currentIndex]?.title ||
?.title || 'Unknown title' 'Unknown title'
} }
> >
<LinkButton <LinkButton
tabIndex={0} tabIndex={0}
onClick={() => { onClick={() => {
if ( if (playQueue[currentEntryList][playQueue.currentIndex]?.albumId) {
playQueue[currentEntryList][
playQueue.currentIndex
]?.albumId
) {
history.push( history.push(
`/library/album/${ `/library/album/${
playQueue[currentEntryList][ playQueue[currentEntryList][playQueue.currentIndex]?.albumId
playQueue.currentIndex
]?.albumId
}` }`
); );
} }
}} }}
> >
{playQueue[currentEntryList][playQueue.currentIndex] {playQueue[currentEntryList][playQueue.currentIndex]?.title ||
?.title || 'Unknown title'} 'Unknown title'}
</LinkButton> </LinkButton>
</CustomTooltip> </CustomTooltip>
</Row> </Row>
@ -439,8 +391,8 @@ const PlayerBar = () => {
enterable enterable
placement="topStart" placement="topStart"
text={ text={
playQueue[currentEntryList][playQueue.currentIndex] playQueue[currentEntryList][playQueue.currentIndex]?.artist ||
?.artist || 'Unknown artist' 'Unknown artist'
} }
> >
<span <span
@ -454,23 +406,17 @@ const PlayerBar = () => {
tabIndex={0} tabIndex={0}
subtitle="true" subtitle="true"
onClick={() => { onClick={() => {
if ( if (playQueue[currentEntryList][playQueue.currentIndex]?.artistId) {
playQueue[currentEntryList][
playQueue.currentIndex
]?.artistId
) {
history.push( history.push(
`/library/artist/${ `/library/artist/${
playQueue[currentEntryList][ playQueue[currentEntryList][playQueue.currentIndex]?.artistId
playQueue.currentIndex
]?.artistId
}` }`
); );
} }
}} }}
> >
{playQueue[currentEntryList][playQueue.currentIndex] {playQueue[currentEntryList][playQueue.currentIndex]?.artist ||
?.artist || 'Unknown artist'} 'Unknown artist'}
</LinkButton> </LinkButton>
</span> </span>
</CustomTooltip> </CustomTooltip>
@ -480,10 +426,7 @@ const PlayerBar = () => {
</Grid> </Grid>
</PlayerColumn> </PlayerColumn>
</FlexboxGrid.Item> </FlexboxGrid.Item>
<FlexboxGrid.Item <FlexboxGrid.Item colspan={12} style={{ textAlign: 'center', verticalAlign: 'middle' }}>
colspan={12}
style={{ textAlign: 'center', verticalAlign: 'middle' }}
>
<PlayerColumn center height="45px"> <PlayerColumn center height="45px">
{/* Seek Backward Button */} {/* Seek Backward Button */}
<CustomTooltip text="Seek backward" delay={1000}> <CustomTooltip text="Seek backward" delay={1000}>
@ -579,9 +522,7 @@ const PlayerBar = () => {
userSelect: 'none', userSelect: 'none',
}} }}
> >
<DurationSpan> <DurationSpan>{format((isDragging ? manualSeek : seek) * 1000)}</DurationSpan>
{format((isDragging ? manualSeek : seek) * 1000)}
</DurationSpan>
</FlexboxGrid.Item> </FlexboxGrid.Item>
<FlexboxGrid.Item colspan={16}> <FlexboxGrid.Item colspan={16}>
{/* Seek Slider */} {/* Seek Slider */}
@ -591,10 +532,7 @@ const PlayerBar = () => {
value={isDragging ? manualSeek : seek} value={isDragging ? manualSeek : seek}
$isDragging={isDragging} $isDragging={isDragging}
tooltip={false} tooltip={false}
max={ max={playQueue[currentEntryList][playQueue.currentIndex]?.duration || 0}
playQueue[currentEntryList][playQueue.currentIndex]
?.duration || 0
}
onChange={handleSeekSlider} onChange={handleSeekSlider}
style={{ width: '100%' }} style={{ width: '100%' }}
/> />
@ -609,18 +547,14 @@ const PlayerBar = () => {
> >
<DurationSpan> <DurationSpan>
{format( {format(
playQueue[currentEntryList][playQueue.currentIndex] playQueue[currentEntryList][playQueue.currentIndex]?.duration * 1000 || 0
?.duration * 1000 || 0
)} )}
</DurationSpan> </DurationSpan>
</FlexboxGrid.Item> </FlexboxGrid.Item>
</FlexboxGrid> </FlexboxGrid>
</PlayerColumn> </PlayerColumn>
</FlexboxGrid.Item> </FlexboxGrid.Item>
<FlexboxGrid.Item <FlexboxGrid.Item colspan={6} style={{ textAlign: 'right', paddingRight: '10px' }}>
colspan={6}
style={{ textAlign: 'right', paddingRight: '10px' }}
>
<PlayerColumn right height="80px"> <PlayerColumn right height="80px">
<Grid> <Grid>
<Row <Row
@ -637,16 +571,14 @@ const PlayerBar = () => {
<PlayerControlIcon <PlayerControlIcon
tabIndex={0} tabIndex={0}
icon={ icon={
playQueue[currentEntryList][playQueue.currentIndex] playQueue[currentEntryList][playQueue.currentIndex]?.starred
?.starred
? 'heart' ? 'heart'
: 'heart-o' : 'heart-o'
} }
size="lg" size="lg"
fixedWidth fixedWidth
active={ active={
playQueue[currentEntryList][playQueue.currentIndex] playQueue[currentEntryList][playQueue.currentIndex]?.starred
?.starred
? 'true' ? 'true'
: 'false' : 'false'
} }
@ -676,14 +608,11 @@ const PlayerBar = () => {
} }
}} }}
active={ active={
playQueue.repeat === 'all' || playQueue.repeat === 'all' || playQueue.repeat === 'one'
playQueue.repeat === 'one'
? 'true' ? 'true'
: 'false' : 'false'
} }
flip={ flip={playQueue.repeat === 'one' ? 'horizontal' : undefined}
playQueue.repeat === 'one' ? 'horizontal' : undefined
}
/> />
</CustomTooltip> </CustomTooltip>
{/* Shuffle Button */} {/* Shuffle Button */}

19
src/components/player/styled.tsx

@ -17,21 +17,13 @@ export const PlayerColumn = styled.div<{
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: ${(props) => justify-content: ${(props) =>
props.left props.left ? 'flex-start' : props.center ? 'center' : props.right ? 'flex-end' : 'center'};
? 'flex-start'
: props.center
? 'center'
: props.right
? 'flex-end'
: 'center'};
`; `;
export const PlayerControlIcon = styled(Icon)` export const PlayerControlIcon = styled(Icon)`
font-size: medium; font-size: medium;
color: ${(props) => color: ${(props) =>
props.active === 'true' props.active === 'true' ? props.theme.primary.main : props.theme.primary.playerBarButtons};
? props.theme.primary.main
: props.theme.primary.playerBarButtons};
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
&:hover { &:hover {
@ -82,9 +74,7 @@ export const CustomSlider = styled(Slider)<{ isDragging?: boolean }>`
} }
.rs-slider-progress-bar { .rs-slider-progress-bar {
background-color: ${(props) => background-color: ${(props) =>
props.$isDragging props.$isDragging ? props.theme.primary.main : props.theme.primary.sliderBackground};
? props.theme.primary.main
: props.theme.primary.sliderBackground};
} }
.rs-slider-handle::before { .rs-slider-handle::before {
@ -118,8 +108,7 @@ export const MiniViewContainer = styled.div<{ display: string }>`
opacity: ${(props) => (props.display === 'true' ? 0.9 : 0)}; opacity: ${(props) => (props.display === 'true' ? 0.9 : 0)};
border-radius: 10px; border-radius: 10px;
color: ${(props) => `${props.theme.primary.text} !important`}; color: ${(props) => `${props.theme.primary.text} !important`};
animation-name: ${(props) => animation-name: ${(props) => (props.display === 'true' ? 'fadeInOpacity' : 'fadeOutOpacity')};
props.display === 'true' ? 'fadeInOpacity' : 'fadeOutOpacity'};
animation-iteration-count: 1; animation-iteration-count: 1;
animation-timing-function: ease-in-out; animation-timing-function: ease-in-out;
animation-duration: 0.5s; animation-duration: 0.5s;

22
src/components/playlist/PlaylistList.tsx

@ -21,18 +21,12 @@ const PlaylistList = () => {
const playlistTriggerRef = useRef<any>(); const playlistTriggerRef = useRef<any>();
const [sortBy] = useState('name'); const [sortBy] = useState('name');
const [newPlaylistName, setNewPlaylistName] = useState(''); const [newPlaylistName, setNewPlaylistName] = useState('');
const [viewType, setViewType] = useState( const [viewType, setViewType] = useState(settings.getSync('playlistViewType') || 'list');
settings.getSync('playlistViewType') || 'list' const { isLoading, isError, data: playlists, error }: any = useQuery(['playlists', sortBy], () =>
); getPlaylists(sortBy)
const { isLoading, isError, data: playlists, error }: any = useQuery(
['playlists', sortBy],
() => getPlaylists(sortBy)
); );
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const filteredData = useSearchQuery(searchQuery, playlists, [ const filteredData = useSearchQuery(searchQuery, playlists, ['name', 'comment']);
'name',
'comment',
]);
const handleCreatePlaylist = async (name: string) => { const handleCreatePlaylist = async (name: string) => {
try { try {
@ -131,12 +125,8 @@ const PlaylistList = () => {
? playlists ? playlists
: playlists.filter((playlist: any) => { : playlists.filter((playlist: any) => {
return ( return (
playlist.name playlist.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
.toLowerCase() playlist.comment?.toLowerCase().includes(searchQuery.toLowerCase())
.includes(searchQuery.toLowerCase()) ||
playlist.comment
?.toLowerCase()
.includes(searchQuery.toLowerCase())
); );
}) })
} }

62
src/components/playlist/PlaylistView.tsx

@ -49,15 +49,8 @@ import PageLoader from '../loader/PageLoader';
import GenericPageHeader from '../layout/GenericPageHeader'; import GenericPageHeader from '../layout/GenericPageHeader';
import { setStatus } from '../../redux/playerSlice'; import { setStatus } from '../../redux/playerSlice';
import { notifyToast } from '../shared/toast'; import { notifyToast } from '../shared/toast';
import { import { addProcessingPlaylist, removeProcessingPlaylist } from '../../redux/miscSlice';
addProcessingPlaylist, import { StyledButton, StyledCheckbox, StyledInputGroup } from '../shared/styled';
removeProcessingPlaylist,
} from '../../redux/miscSlice';
import {
StyledButton,
StyledCheckbox,
StyledInputGroup,
} from '../shared/styled';
interface PlaylistParams { interface PlaylistParams {
id: string; id: string;
@ -73,7 +66,9 @@ const PlaylistView = ({ ...rest }) => {
const { isLoading, isError, data, error }: any = useQuery( const { isLoading, isError, data, error }: any = useQuery(
['playlist', playlistId], ['playlist', playlistId],
() => getPlaylist(playlistId), () => getPlaylist(playlistId),
{ refetchOnWindowFocus: false } {
refetchOnWindowFocus: false,
}
); );
const [editName, setEditName] = useState(''); const [editName, setEditName] = useState('');
const [editDescription, setEditDescription] = useState(''); const [editDescription, setEditDescription] = useState('');
@ -87,17 +82,10 @@ const PlaylistView = ({ ...rest }) => {
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [recoveryPath, setRecoveryPath] = useState(''); const [recoveryPath, setRecoveryPath] = useState('');
const [needsRecovery, setNeedsRecovery] = useState(false); const [needsRecovery, setNeedsRecovery] = useState(false);
const filteredData = useSearchQuery(searchQuery, localPlaylistData, [ const filteredData = useSearchQuery(searchQuery, localPlaylistData, ['title', 'artist', 'album']);
'title',
'artist',
'album',
]);
useEffect(() => { useEffect(() => {
const recoveryFilePath = path.join( const recoveryFilePath = path.join(getRecoveryPath(), `playlist_${data?.id}.json`);
getRecoveryPath(),
`playlist_${data?.id}.json`
);
setRecoveryPath(recoveryFilePath); setRecoveryPath(recoveryFilePath);
setNeedsRecovery(fs.existsSync(recoveryFilePath)); setNeedsRecovery(fs.existsSync(recoveryFilePath));
@ -129,11 +117,7 @@ const PlaylistView = ({ ...rest }) => {
dispatch(toggleSelected(rowData)); dispatch(toggleSelected(rowData));
} else if (e.shiftKey) { } else if (e.shiftKey) {
dispatch(setRangeSelected(rowData)); dispatch(setRangeSelected(rowData));
dispatch( dispatch(toggleRangeSelected(searchQuery !== '' ? filteredData : localPlaylistData));
toggleRangeSelected(
searchQuery !== '' ? filteredData : localPlaylistData
)
);
} }
}, 100); }, 100);
} }
@ -230,12 +214,7 @@ const PlaylistView = ({ ...rest }) => {
const handleEdit = async () => { const handleEdit = async () => {
setIsSubmittingEdit(true); setIsSubmittingEdit(true);
const res = await updatePlaylist( const res = await updatePlaylist(data.id, editName, editDescription, editPublic);
data.id,
editName,
editDescription,
editPublic
);
if (isFailedResponse(res)) { if (isFailedResponse(res)) {
notifyToast('error', errorMessages(res)[0]); notifyToast('error', errorMessages(res)[0]);
@ -266,11 +245,7 @@ const PlaylistView = ({ ...rest }) => {
const handleDragEnd = () => { const handleDragEnd = () => {
if (multiSelect.isDragging) { if (multiSelect.isDragging) {
setLocalPlaylistData( setLocalPlaylistData(
moveToIndex( moveToIndex(localPlaylistData, multiSelect.selected, multiSelect.currentMouseOverId)
localPlaylistData,
multiSelect.selected,
multiSelect.currentMouseOverId
)
); );
dispatch(setIsDragging(false)); dispatch(setIsDragging(false));
} }
@ -318,9 +293,7 @@ const PlaylistView = ({ ...rest }) => {
<SaveButton <SaveButton
size="lg" size="lg"
text={needsRecovery ? 'Recover playlist' : undefined} text={needsRecovery ? 'Recover playlist' : undefined}
color={ color={needsRecovery ? 'red' : isModified ? 'green' : undefined}
needsRecovery ? 'red' : isModified ? 'green' : undefined
}
disabled={ disabled={
(!needsRecovery && !isModified) || (!needsRecovery && !isModified) ||
misc.isProcessingPlaylist.includes(data?.id) misc.isProcessingPlaylist.includes(data?.id)
@ -330,13 +303,9 @@ const PlaylistView = ({ ...rest }) => {
/> />
<UndoButton <UndoButton
size="lg" size="lg"
color={ color={needsRecovery ? 'red' : isModified ? 'green' : undefined}
needsRecovery ? 'red' : isModified ? 'green' : undefined
}
disabled={ disabled={
needsRecovery || needsRecovery || !isModified || misc.isProcessingPlaylist.includes(data?.id)
!isModified ||
misc.isProcessingPlaylist.includes(data?.id)
} }
onClick={() => setLocalPlaylistData(data?.song)} onClick={() => setLocalPlaylistData(data?.song)}
/> />
@ -385,10 +354,7 @@ const PlaylistView = ({ ...rest }) => {
</Popover> </Popover>
} }
> >
<EditButton <EditButton size="lg" disabled={misc.isProcessingPlaylist.includes(data?.id)} />
size="lg"
disabled={misc.isProcessingPlaylist.includes(data?.id)}
/>
</Whisper> </Whisper>
<Whisper <Whisper

20
src/components/scrollingmenu/ScrollingMenu.tsx

@ -18,11 +18,7 @@ const LeftArrow = () => {
const { isFirstItemVisible, scrollPrev } = useContext(VisibilityContext); const { isFirstItemVisible, scrollPrev } = useContext(VisibilityContext);
return ( return (
<Button <Button appearance="link" disabled={isFirstItemVisible} onClick={() => scrollPrev()}>
appearance="link"
disabled={isFirstItemVisible}
onClick={() => scrollPrev()}
>
<StyledIcon icon="arrow-left" /> <StyledIcon icon="arrow-left" />
</Button> </Button>
); );
@ -32,23 +28,13 @@ const RightArrow = () => {
const { isLastItemVisible, scrollNext } = useContext(VisibilityContext); const { isLastItemVisible, scrollNext } = useContext(VisibilityContext);
return ( return (
<Button <Button appearance="link" disabled={isLastItemVisible} onClick={() => scrollNext()}>
appearance="link"
disabled={isLastItemVisible}
onClick={() => scrollNext()}
>
<StyledIcon icon="arrow-right" /> <StyledIcon icon="arrow-right" />
</Button> </Button>
); );
}; };
const ScrollingMenu = ({ const ScrollingMenu = ({ cardTitle, cardSubtitle, data, title, cardSize }: any) => {
cardTitle,
cardSubtitle,
data,
title,
cardSize,
}: any) => {
return ( return (
<ScrollMenuContainer> <ScrollMenuContainer>
<Title>{title}</Title> <Title>{title}</Title>

7
src/components/selectionbar/SelectionBar.tsx

@ -27,12 +27,7 @@ const StyledTag = styled(Tag)`
background: transparent; background: transparent;
`; `;
const SelectionBar = ({ const SelectionBar = ({ children, handleUpClick, handleDownClick, handleManualClick }: any) => {
children,
handleUpClick,
handleDownClick,
handleManualClick,
}: any) => {
const multiSelect = useAppSelector((state) => state.multiSelect); const multiSelect = useAppSelector((state) => state.multiSelect);
return ( return (

24
src/components/selectionbar/SelectionButtons.tsx

@ -24,33 +24,15 @@ const CustomIconButton = ({ tooltipText, icon, handleClick, ...rest }: any) => {
}; };
export const MoveUpButton = ({ handleClick }: any) => { export const MoveUpButton = ({ handleClick }: any) => {
return ( return <CustomIconButton handleClick={handleClick} tooltipText="Move up" icon="arrow-up2" />;
<CustomIconButton
handleClick={handleClick}
tooltipText="Move up"
icon="arrow-up2"
/>
);
}; };
export const MoveDownButton = ({ handleClick }: any) => { export const MoveDownButton = ({ handleClick }: any) => {
return ( return <CustomIconButton handleClick={handleClick} tooltipText="Move down" icon="arrow-down2" />;
<CustomIconButton
handleClick={handleClick}
tooltipText="Move down"
icon="arrow-down2"
/>
);
}; };
export const MoveManualButton = ({ handleClick }: any) => { export const MoveManualButton = ({ handleClick }: any) => {
return ( return <CustomIconButton handleClick={handleClick} tooltipText="Move to index" icon="arrows-v" />;
<CustomIconButton
handleClick={handleClick}
tooltipText="Move to index"
icon="arrows-v"
/>
);
}; };
export const DeselectAllButton = ({ handleClick }: any) => { export const DeselectAllButton = ({ handleClick }: any) => {

4
src/components/settings/Config.tsx

@ -72,9 +72,7 @@ const Config = () => {
trigger="click" trigger="click"
speaker={ speaker={
<Popover title="Confirm"> <Popover title="Confirm">
<div> <div>Are you sure you want to reset your settings to default?</div>
Are you sure you want to reset your settings to default?
</div>
<div> <div>
<Button <Button
id="reset-submit-button" id="reset-submit-button"

43
src/components/settings/ConfigPanels/CacheConfig.tsx

@ -15,25 +15,15 @@ const CacheConfig = () => {
const [isEditingCachePath, setIsEditingCachePath] = useState(false); const [isEditingCachePath, setIsEditingCachePath] = useState(false);
const [newCachePath, setNewCachePath] = useState(''); const [newCachePath, setNewCachePath] = useState('');
const [errorMessage, setErrorMessage] = useState(''); const [errorMessage, setErrorMessage] = useState('');
const [cacheSongs, setCacheSongs] = useState( const [cacheSongs, setCacheSongs] = useState(Boolean(settings.getSync('cacheSongs')));
Boolean(settings.getSync('cacheSongs')) const [cacheImages, setCacheImages] = useState(Boolean(settings.getSync('cacheImages')));
);
const [cacheImages, setCacheImages] = useState(
Boolean(settings.getSync('cacheImages'))
);
useEffect(() => { useEffect(() => {
// Retrieve cache sizes on render // Retrieve cache sizes on render
try { try {
setImgCacheSize( setImgCacheSize(Number((fsUtils.fsizeSync(getImageCachePath()) / 1000 / 1000).toFixed(0)));
Number(
(fsUtils.fsizeSync(getImageCachePath()) / 1000 / 1000).toFixed(0)
)
);
setSongCacheSize( setSongCacheSize(Number((fsUtils.fsizeSync(getSongCachePath()) / 1000 / 1000).toFixed(0)));
Number((fsUtils.fsizeSync(getSongCachePath()) / 1000 / 1000).toFixed(0))
);
} catch (err) { } catch (err) {
setImgCacheSize(0); setImgCacheSize(0);
setSongCacheSize(0); setSongCacheSize(0);
@ -51,18 +41,15 @@ const CacheConfig = () => {
</> </>
)} )}
<p> <p>
Songs are cached only when playback for the track fully completes and Songs are cached only when playback for the track fully completes and ends. Skipping to the
ends. Skipping to the next or previous track after only partially next or previous track after only partially completing the track will not begin the caching
completing the track will not begin the caching process. process.
</p> </p>
<br /> <br />
{isEditingCachePath && ( {isEditingCachePath && (
<> <>
<InputGroup> <InputGroup>
<StyledInput <StyledInput value={newCachePath} onChange={(e: string) => setNewCachePath(e)} />
value={newCachePath}
onChange={(e: string) => setNewCachePath(e)}
/>
<InputGroup.Button <InputGroup.Button
onClick={() => { onClick={() => {
const check = fs.existsSync(newCachePath); const check = fs.existsSync(newCachePath);
@ -74,9 +61,7 @@ const CacheConfig = () => {
return setIsEditingCachePath(false); return setIsEditingCachePath(false);
} }
return setErrorMessage( return setErrorMessage(`Path: ${newCachePath} not found. Enter a valid path.`);
`Path: ${newCachePath} not found. Enter a valid path.`
);
}} }}
> >
<Icon icon="check" /> <Icon icon="check" />
@ -91,8 +76,7 @@ const CacheConfig = () => {
</InputGroup.Button> </InputGroup.Button>
</InputGroup> </InputGroup>
<p style={{ fontSize: 'smaller' }}> <p style={{ fontSize: 'smaller' }}>
*You will need to manually move any existing cached files to their *You will need to manually move any existing cached files to their new location.
new location.
</p> </p>
</> </>
)} )}
@ -118,8 +102,7 @@ const CacheConfig = () => {
> >
Songs{' '} Songs{' '}
<Tag> <Tag>
{songCacheSize} MB{' '} {songCacheSize} MB {imgCacheSize === 9999999 && '- Folder not found'}
{imgCacheSize === 9999999 && '- Folder not found'}
</Tag> </Tag>
</StyledCheckbox> </StyledCheckbox>
<StyledCheckbox <StyledCheckbox
@ -135,9 +118,7 @@ const CacheConfig = () => {
</Tag> </Tag>
</StyledCheckbox> </StyledCheckbox>
<br /> <br />
<Button onClick={() => setIsEditingCachePath(true)}> <Button onClick={() => setIsEditingCachePath(true)}>Edit cache location</Button>
Edit cache location
</Button>
</div> </div>
</ConfigPanel> </ConfigPanel>
); );

5
src/components/settings/ConfigPanels/DebugConfig.tsx

@ -15,10 +15,7 @@ const DebugConfig = () => {
<StyledCheckbox <StyledCheckbox
defaultChecked={showDebugWindow} defaultChecked={showDebugWindow}
onChange={() => { onChange={() => {
settings.setSync( settings.setSync('showDebugWindow', !settings.getSync('showDebugWindow'));
'showDebugWindow',
!settings.getSync('showDebugWindow')
);
dispatch( dispatch(
setPlaybackSetting({ setPlaybackSetting({
setting: 'showDebugWindow', setting: 'showDebugWindow',

19
src/components/settings/ConfigPanels/ListViewConfig.tsx

@ -3,12 +3,7 @@ import settings from 'electron-settings';
import { TagPicker, ControlLabel } from 'rsuite'; import { TagPicker, ControlLabel } from 'rsuite';
import { StyledInputNumber } from '../../shared/styled'; import { StyledInputNumber } from '../../shared/styled';
const ListViewConfig = ({ const ListViewConfig = ({ defaultColumns, columnPicker, columnList, settingsConfig }: any) => {
defaultColumns,
columnPicker,
columnList,
settingsConfig,
}: any) => {
return ( return (
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<br /> <br />
@ -21,9 +16,7 @@ const ListViewConfig = ({
if (e) { if (e) {
e.map((selected: string) => { e.map((selected: string) => {
const selectedColumn = columnList.find( const selectedColumn = columnList.find((column: any) => column.label === selected);
(column: any) => column.label === selected
);
if (selectedColumn) { if (selectedColumn) {
return columns.push(selectedColumn.value); return columns.push(selectedColumn.value);
} }
@ -40,9 +33,7 @@ const ListViewConfig = ({
<div style={{ marginTop: '20px' }}> <div style={{ marginTop: '20px' }}>
<ControlLabel>Row height</ControlLabel> <ControlLabel>Row height</ControlLabel>
<StyledInputNumber <StyledInputNumber
defaultValue={ defaultValue={String(settings.getSync(settingsConfig.rowHeight)) || '0'}
String(settings.getSync(settingsConfig.rowHeight)) || '0'
}
step={1} step={1}
min={15} min={15}
max={100} max={100}
@ -55,9 +46,7 @@ const ListViewConfig = ({
<div style={{ marginTop: '20px' }}> <div style={{ marginTop: '20px' }}>
<ControlLabel>Font size</ControlLabel> <ControlLabel>Font size</ControlLabel>
<StyledInputNumber <StyledInputNumber
defaultValue={ defaultValue={String(settings.getSync(settingsConfig.fontSize)) || '0'}
String(settings.getSync(settingsConfig.fontSize)) || '0'
}
step={0.5} step={0.5}
min={1} min={1}
max={100} max={100}

23
src/components/settings/ConfigPanels/LookAndFeelConfig.tsx

@ -12,11 +12,7 @@ import {
import ListViewConfig from './ListViewConfig'; import ListViewConfig from './ListViewConfig';
import { Fonts } from '../Fonts'; import { Fonts } from '../Fonts';
import { useAppDispatch } from '../../../redux/hooks'; import { useAppDispatch } from '../../../redux/hooks';
import { import { setTheme, setFont, setDynamicBackground } from '../../../redux/miscSlice';
setTheme,
setFont,
setDynamicBackground,
} from '../../../redux/miscSlice';
import { import {
songColumnPicker, songColumnPicker,
songColumnList, songColumnList,
@ -35,10 +31,8 @@ const LookAndFeelConfig = () => {
const playlistCols: any = settings.getSync('playlistListColumns'); const playlistCols: any = settings.getSync('playlistListColumns');
const miniCols: any = settings.getSync('miniListColumns'); const miniCols: any = settings.getSync('miniListColumns');
const currentSongColumns = songCols?.map((column: any) => column.label) || []; const currentSongColumns = songCols?.map((column: any) => column.label) || [];
const currentAlbumColumns = const currentAlbumColumns = albumCols?.map((column: any) => column.label) || [];
albumCols?.map((column: any) => column.label) || []; const currentPlaylistColumns = playlistCols?.map((column: any) => column.label) || [];
const currentPlaylistColumns =
playlistCols?.map((column: any) => column.label) || [];
const currentMiniColumns = miniCols?.map((column: any) => column.label) || []; const currentMiniColumns = miniCols?.map((column: any) => column.label) || [];
return ( return (
@ -62,15 +56,8 @@ const LookAndFeelConfig = () => {
<StyledCheckbox <StyledCheckbox
defaultChecked={settings.getSync('dynamicBackground')} defaultChecked={settings.getSync('dynamicBackground')}
onChange={() => { onChange={() => {
settings.setSync( settings.setSync('dynamicBackground', !settings.getSync('dynamicBackground'));
'dynamicBackground', dispatch(setDynamicBackground(Boolean(settings.getSync('dynamicBackground'))));
!settings.getSync('dynamicBackground')
);
dispatch(
setDynamicBackground(
Boolean(settings.getSync('dynamicBackground'))
)
);
}} }}
> >
Enable dynamic background Enable dynamic background

48
src/components/settings/ConfigPanels/PlaybackConfig.tsx

@ -4,10 +4,7 @@ import { ControlLabel, RadioGroup } from 'rsuite';
import { ConfigPanel } from '../styled'; import { ConfigPanel } from '../styled';
import { StyledInputNumber, StyledRadio } from '../../shared/styled'; import { StyledInputNumber, StyledRadio } from '../../shared/styled';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import { import { setPlaybackSetting, setPlayerVolume } from '../../../redux/playQueueSlice';
setPlaybackSetting,
setPlayerVolume,
} from '../../../redux/playQueueSlice';
const PlaybackConfig = () => { const PlaybackConfig = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -15,28 +12,25 @@ const PlaybackConfig = () => {
return ( return (
<ConfigPanel header="Playback" bordered> <ConfigPanel header="Playback" bordered>
<p> <p>
Fading works by polling the audio player on an interval to determine Fading works by polling the audio player on an interval to determine when to start fading to
when to start fading to the next track. Due to this, you may notice the the next track. Due to this, you may notice the fade timing may not be 100% perfect.
fade timing may not be 100% perfect. Lowering the player polling Lowering the player polling interval can increase the accuracy of the fade, but may also
interval can increase the accuracy of the fade, but may also decrease decrease application performance as calculations are running for the fade.
application performance as calculations are running for the fade.
</p> </p>
<p> <p>
If volume fade is disabled, then the fading-in track will start at the If volume fade is disabled, then the fading-in track will start at the specified crossfade
specified crossfade duration at full volume. duration at full volume.
</p> </p>
<p> <p>
Setting the crossfade duration to <code>0</code> will enable{' '} Setting the crossfade duration to <code>0</code> will enable{' '}
<strong>gapless playback</strong>. All other playback settings except <strong>gapless playback</strong>. All other playback settings except the polling interval
the polling interval will be ignored. It is recommended that you use a will be ignored. It is recommended that you use a polling interval between <code>1</code>{' '}
polling interval between <code>1</code> and <code>20</code> for and <code>20</code> for increased transition accuracy.
increased transition accuracy.
</p> </p>
<p style={{ fontSize: 'smaller' }}> <p style={{ fontSize: 'smaller' }}>
*Enable the debug window if you want to view the differences between *Enable the debug window if you want to view the differences between each fade type
each fade type
</p> </p>
<div style={{ width: '300px', paddingTop: '20px' }}> <div style={{ width: '300px', paddingTop: '20px' }}>
@ -57,12 +51,8 @@ const PlaybackConfig = () => {
); );
if (Number(e) === 0) { if (Number(e) === 0) {
dispatch( dispatch(setPlayerVolume({ player: 1, volume: playQueue.volume }));
setPlayerVolume({ player: 1, volume: playQueue.volume }) dispatch(setPlayerVolume({ player: 2, volume: playQueue.volume }));
);
dispatch(
setPlayerVolume({ player: 2, volume: playQueue.volume })
);
} }
}} }}
/> />
@ -100,15 +90,9 @@ const PlaybackConfig = () => {
<StyledRadio value="linear">Linear</StyledRadio> <StyledRadio value="linear">Linear</StyledRadio>
<StyledRadio value="dipped">Dipped</StyledRadio> <StyledRadio value="dipped">Dipped</StyledRadio>
<StyledRadio value="constantPower">Constant Power</StyledRadio> <StyledRadio value="constantPower">Constant Power</StyledRadio>
<StyledRadio value="constantPowerSlowFade"> <StyledRadio value="constantPowerSlowFade">Constant Power (slow fade)</StyledRadio>
Constant Power (slow fade) <StyledRadio value="constantPowerSlowCut">Constant Power (slow cut)</StyledRadio>
</StyledRadio> <StyledRadio value="constantPowerFastCut">Constant Power (fast cut)</StyledRadio>
<StyledRadio value="constantPowerSlowCut">
Constant Power (slow cut)
</StyledRadio>
<StyledRadio value="constantPowerFastCut">
Constant Power (fast cut)
</StyledRadio>
</RadioGroup> </RadioGroup>
<br /> <br />
<ControlLabel>Volume fade</ControlLabel> <ControlLabel>Volume fade</ControlLabel>

9
src/components/settings/ConfigPanels/PlayerConfig.tsx

@ -11,8 +11,8 @@ const PlayerConfig = () => {
return ( return (
<ConfigPanel header="Player" bordered> <ConfigPanel header="Player" bordered>
<p> <p>
Configure the number of seconds to skip forwards/backwards by when Configure the number of seconds to skip forwards/backwards by when clicking the seek
clicking the seek forward/backward buttons. forward/backward buttons.
</p> </p>
<br /> <br />
<ControlLabel>Seek forward (s)</ControlLabel> <ControlLabel>Seek forward (s)</ControlLabel>
@ -42,10 +42,7 @@ const PlayerConfig = () => {
<StyledCheckbox <StyledCheckbox
defaultChecked={globalMediaHotkeys} defaultChecked={globalMediaHotkeys}
onChange={() => { onChange={() => {
settings.setSync( settings.setSync('globalMediaHotkeys', !settings.getSync('globalMediaHotkeys'));
'globalMediaHotkeys',
!settings.getSync('globalMediaHotkeys')
);
setGlobalMediaHotkeys(!globalMediaHotkeys); setGlobalMediaHotkeys(!globalMediaHotkeys);
}} }}
> >

61
src/components/shared/ContextMenu.tsx

@ -79,21 +79,12 @@ export const GlobalContextMenu = () => {
const [shouldCreatePlaylist, setShouldCreatePlaylist] = useState(false); const [shouldCreatePlaylist, setShouldCreatePlaylist] = useState(false);
const [newPlaylistName, setNewPlaylistName] = useState(''); const [newPlaylistName, setNewPlaylistName] = useState('');
const { data: playlists }: any = useQuery(['playlists', 'name'], () => const { data: playlists }: any = useQuery(['playlists', 'name'], () => getPlaylists('name'));
getPlaylists('name')
);
const handleAddToQueue = () => { const handleAddToQueue = () => {
const entriesByRowIndexAsc = _.orderBy( const entriesByRowIndexAsc = _.orderBy(multiSelect.selected, 'rowIndex', 'asc');
multiSelect.selected,
'rowIndex',
'asc'
);
notifyToast( notifyToast('info', `Added ${multiSelect.selected.length} song(s) to the queue`);
'info',
`Added ${multiSelect.selected.length} song(s) to the queue`
);
dispatch(appendPlayQueue({ entries: entriesByRowIndexAsc })); dispatch(appendPlayQueue({ entries: entriesByRowIndexAsc }));
dispatch(setContextMenu({ show: false })); dispatch(setContextMenu({ show: false }));
@ -117,10 +108,7 @@ export const GlobalContextMenu = () => {
); );
try { try {
const res = await updatePlaylistSongsLg( const res = await updatePlaylistSongsLg(localSelectedPlaylistId, sortedEntries);
localSelectedPlaylistId,
sortedEntries
);
if (isFailedResponse(res)) { if (isFailedResponse(res)) {
notifyToast('error', errorMessages(res)[0]); notifyToast('error', errorMessages(res)[0]);
@ -130,11 +118,7 @@ export const GlobalContextMenu = () => {
<> <>
<p> <p>
Added {sortedEntries.length} song(s) to playlist &quot; Added {sortedEntries.length} song(s) to playlist &quot;
{ {playlists.find((playlist: any) => playlist.id === localSelectedPlaylistId)?.name}
playlists.find(
(playlist: any) => playlist.id === localSelectedPlaylistId
)?.name
}
&quot; &quot;
</p> </p>
<StyledButton <StyledButton
@ -215,9 +199,7 @@ export const GlobalContextMenu = () => {
const handleUnfavorite = async () => { const handleUnfavorite = async () => {
dispatch(setContextMenu({ show: false })); dispatch(setContextMenu({ show: false }));
const starredEntries = multiSelect.selected.filter( const starredEntries = multiSelect.selected.filter((entry: any) => entry.starred);
(entry: any) => entry.starred
);
const ids = _.map(starredEntries, 'id'); const ids = _.map(starredEntries, 'id');
@ -246,9 +228,7 @@ export const GlobalContextMenu = () => {
numOfButtons={6} numOfButtons={6}
numOfDividers={3} numOfDividers={3}
> >
<ContextMenuButton <ContextMenuButton text={`Selected: ${multiSelect.selected.length}`} />
text={`Selected: ${multiSelect.selected.length}`}
/>
<ContextMenuDivider /> <ContextMenuDivider />
<ContextMenuButton <ContextMenuButton
text="Add to queue" text="Add to queue"
@ -258,9 +238,7 @@ export const GlobalContextMenu = () => {
<ContextMenuButton <ContextMenuButton
text="Remove from current" text="Remove from current"
onClick={handleRemoveFromQueue} onClick={handleRemoveFromQueue}
disabled={misc.contextMenu.disabledOptions.includes( disabled={misc.contextMenu.disabledOptions.includes('removeFromCurrent')}
'removeFromCurrent'
)}
/> />
<ContextMenuDivider /> <ContextMenuDivider />
@ -282,12 +260,9 @@ export const GlobalContextMenu = () => {
/> />
<StyledButton <StyledButton
disabled={ disabled={
!selectedPlaylistId || !selectedPlaylistId || misc.isProcessingPlaylist.includes(selectedPlaylistId)
misc.isProcessingPlaylist.includes(selectedPlaylistId)
} }
loading={misc.isProcessingPlaylist.includes( loading={misc.isProcessingPlaylist.includes(selectedPlaylistId)}
selectedPlaylistId
)}
onClick={handleAddToPlaylist} onClick={handleAddToPlaylist}
> >
Add Add
@ -295,9 +270,7 @@ export const GlobalContextMenu = () => {
<div> <div>
<StyledButton <StyledButton
appearance="link" appearance="link"
onClick={() => onClick={() => setShouldCreatePlaylist(!shouldCreatePlaylist)}
setShouldCreatePlaylist(!shouldCreatePlaylist)
}
> >
Create new playlist Create new playlist
</StyledButton> </StyledButton>
@ -338,25 +311,19 @@ export const GlobalContextMenu = () => {
? playlistTriggerRef.current.close() ? playlistTriggerRef.current.close()
: playlistTriggerRef.current.open() : playlistTriggerRef.current.open()
} }
disabled={misc.contextMenu.disabledOptions.includes( disabled={misc.contextMenu.disabledOptions.includes('addToPlaylist')}
'addToPlaylist'
)}
/> />
</Whisper> </Whisper>
<ContextMenuDivider /> <ContextMenuDivider />
<ContextMenuButton <ContextMenuButton
text="Add to favorites" text="Add to favorites"
onClick={handleFavorite} onClick={handleFavorite}
disabled={misc.contextMenu.disabledOptions.includes( disabled={misc.contextMenu.disabledOptions.includes('addToFavorites')}
'addToFavorites'
)}
/> />
<ContextMenuButton <ContextMenuButton
text="Remove from favorites" text="Remove from favorites"
onClick={handleUnfavorite} onClick={handleUnfavorite}
disabled={misc.contextMenu.disabledOptions.includes( disabled={misc.contextMenu.disabledOptions.includes('removeFromFavorites')}
'removeFromFavorites'
)}
/> />
</ContextMenu> </ContextMenu>
)} )}

17
src/components/shared/ToolbarButtons.tsx

@ -29,15 +29,8 @@ export const PlayAppendButton = ({ ...rest }) => {
export const PlayShuffleAppendButton = ({ ...rest }) => { export const PlayShuffleAppendButton = ({ ...rest }) => {
return ( return (
<CustomTooltip <CustomTooltip text="Add shuffled to queue" placement="bottom" onClick={rest.onClick}>
text="Add shuffled to queue" <StyledIconButton tabIndex={0} icon={<Icon icon="plus-square" {...rest} />} />
placement="bottom"
onClick={rest.onClick}
>
<StyledIconButton
tabIndex={0}
icon={<Icon icon="plus-square" {...rest} />}
/>
</CustomTooltip> </CustomTooltip>
); );
}; };
@ -89,11 +82,7 @@ export const FavoriteButton = ({ isFavorite, ...rest }: any) => {
export const DownloadButton = ({ ...rest }) => { export const DownloadButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text="Toggle favorite" placement="bottom"> <CustomTooltip text="Toggle favorite" placement="bottom">
<StyledIconButton <StyledIconButton tabIndex={0} icon={<Icon icon="download" />} {...rest} />
tabIndex={0}
icon={<Icon icon="download" />}
{...rest}
/>
</CustomTooltip> </CustomTooltip>
); );
}; };

30
src/components/shared/styled.ts

@ -22,9 +22,7 @@ export const HeaderButton = styled(Button)`
export const StyledButton = styled(Button)<{ width: number }>` export const StyledButton = styled(Button)<{ width: number }>`
background: ${(props) => background: ${(props) =>
props.appearance === 'primary' props.appearance === 'primary' ? `${props.theme.primary.main} !important` : undefined};
? `${props.theme.primary.main} !important`
: undefined};
width: ${(props) => `${props.width}px`}; width: ${(props) => `${props.width}px`};
`; `;
@ -60,9 +58,7 @@ export const StyledCheckbox = styled(Checkbox)`
span { span {
&:before { &:before {
background-color: ${(props) => background-color: ${(props) =>
props.defaultChecked props.defaultChecked ? `${props.theme.primary.main} !important` : undefined};
? `${props.theme.primary.main} !important`
: undefined};
} }
&:after { &:after {
border: transparent !important; border: transparent !important;
@ -94,14 +90,12 @@ export const StyledIconButton = styled(IconButton)`
&:hover { &:hover {
background: ${(props) => background: ${(props) =>
props.appearance === 'subtle' ? 'transparent !important' : undefined}; props.appearance === 'subtle' ? 'transparent !important' : undefined};
color: ${(props) => color: ${(props) => (props.appearance === 'subtle' ? props.theme.primary.main : undefined)};
props.appearance === 'subtle' ? props.theme.primary.main : undefined};
} }
&:focus { &:focus {
background: ${(props) => background: ${(props) =>
props.appearance === 'subtle' ? 'transparent !important' : undefined}; props.appearance === 'subtle' ? 'transparent !important' : undefined};
color: ${(props) => color: ${(props) => (props.appearance === 'subtle' ? props.theme.primary.main : undefined)};
props.appearance === 'subtle' ? props.theme.primary.main : undefined};
} }
background-color: ${(props) => background-color: ${(props) =>
props.appearance === 'primary' ? props.theme.primary.main : undefined}; props.appearance === 'primary' ? props.theme.primary.main : undefined};
@ -110,8 +104,7 @@ export const StyledIconButton = styled(IconButton)`
export const StyledNavItem = styled(Nav.Item)` export const StyledNavItem = styled(Nav.Item)`
a { a {
color: ${(props) => color: ${(props) => (props.active ? `${props.theme.primary.main} !important;` : undefined)};
props.active ? `${props.theme.primary.main} !important;` : undefined};
&:hover { &:hover {
color: ${(props) => `${props.theme.primary.main} !important;`}; color: ${(props) => `${props.theme.primary.main} !important;`};
@ -122,9 +115,7 @@ export const StyledNavItem = styled(Nav.Item)`
export const StyledIconToggle = styled(Icon)<{ active: string }>` export const StyledIconToggle = styled(Icon)<{ active: string }>`
cursor: pointer; cursor: pointer;
color: ${(props) => color: ${(props) =>
props.active === 'true' props.active === 'true' ? props.theme.primary.main : props.theme.primary.text};
? props.theme.primary.main
: props.theme.primary.text};
`; `;
export const StyledRate = styled(Rate)` export const StyledRate = styled(Rate)`
@ -145,8 +136,7 @@ export const StyledInputPicker = styled(InputPicker)<{ width?: number }>`
} }
.rs-btn-default { .rs-btn-default {
background: ${(props) => background: ${(props) => `${props.theme.primary.inputBackground} !important`};
`${props.theme.primary.inputBackground} !important`};
} }
width: ${(props) => `${props.width}px`}; width: ${(props) => `${props.width}px`};
@ -168,11 +158,7 @@ export const ContextMenuWindow = styled.div<{
top: ${(props) => `${props.yPos}px`}; top: ${(props) => `${props.yPos}px`};
left: ${(props) => `${props.xPos}px`}; left: ${(props) => `${props.xPos}px`};
height: ${(props) => height: ${(props) =>
`${ `${props.numOfButtons * 30 + props.numOfDividers * 2 + (props.hasTitle ? 16 : 0)}px`};
props.numOfButtons * 30 +
props.numOfDividers * 2 +
(props.hasTitle ? 16 : 0)
}px`};
width: ${(props) => `${props.width}px`}; width: ${(props) => `${props.width}px`};
margin: 0px; margin: 0px;
white-space: normal; white-space: normal;

5
src/components/shared/toast.ts

@ -1,9 +1,6 @@
import { Notification } from 'rsuite'; import { Notification } from 'rsuite';
export const notifyToast = ( export const notifyToast = (type: 'info' | 'success' | 'warning' | 'error', message: any) => {
type: 'info' | 'success' | 'warning' | 'error',
message: any
) => {
Notification[type]({ Notification[type]({
title: `${type.charAt(0).toUpperCase()}${type.slice(1)}`, title: `${type.charAt(0).toUpperCase()}${type.slice(1)}`,
description: message, description: message,

26
src/components/starred/StarredView.tsx

@ -5,10 +5,7 @@ import { Nav } from 'rsuite';
import settings from 'electron-settings'; import settings from 'electron-settings';
import useSearchQuery from '../../hooks/useSearchQuery'; import useSearchQuery from '../../hooks/useSearchQuery';
import { useAppDispatch } from '../../redux/hooks'; import { useAppDispatch } from '../../redux/hooks';
import { import { fixPlayer2Index, setPlayQueueByRowClick } from '../../redux/playQueueSlice';
fixPlayer2Index,
setPlayQueueByRowClick,
} from '../../redux/playQueueSlice';
import { import {
clearSelected, clearSelected,
toggleSelected, toggleSelected,
@ -28,21 +25,12 @@ const StarredView = () => {
const history = useHistory(); const history = useHistory();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [currentPage, setCurrentPage] = useState('Tracks'); const [currentPage, setCurrentPage] = useState('Tracks');
const [viewType, setViewType] = useState( const [viewType, setViewType] = useState(settings.getSync('albumViewType') || 'list');
settings.getSync('albumViewType') || 'list' const { isLoading, isError, data, error }: any = useQuery('starred', getStarred);
);
const { isLoading, isError, data, error }: any = useQuery(
'starred',
getStarred
);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const filteredData = useSearchQuery( const filteredData = useSearchQuery(
searchQuery, searchQuery,
currentPage === 'Tracks' currentPage === 'Tracks' ? data?.song : currentPage === 'Albums' ? data?.album : data?.song,
? data?.song
: currentPage === 'Albums'
? data?.album
: data?.song,
['title', 'artist', 'album', 'name', 'genre'] ['title', 'artist', 'album', 'name', 'genre']
); );
@ -64,11 +52,7 @@ const StarredView = () => {
} }
} else if (currentPage === 'Albums') { } else if (currentPage === 'Albums') {
dispatch(setRangeSelected(rowData)); dispatch(setRangeSelected(rowData));
dispatch( dispatch(toggleRangeSelected(searchQuery !== '' ? filteredData : data?.album));
toggleRangeSelected(
searchQuery !== '' ? filteredData : data?.album
)
);
} }
} }
}, 100); }, 100);

21
src/components/viewtypes/GridViewType.tsx

@ -29,23 +29,17 @@ const GridCard = ({ data, index, style }: any) => {
> >
<Card <Card
title={data.data[i][data.cardTitle.property]} title={data.data[i][data.cardTitle.property]}
subtitle={`${data.data[i][data.cardSubtitle.property]}${ subtitle={`${data.data[i][data.cardSubtitle.property]}${data.cardSubtitle.unit}`}
data.cardSubtitle.unit
}`}
coverArt={data.data[i].image} coverArt={data.data[i].image}
size={`${data.size}px`} size={`${data.size}px`}
url={ url={
data.cardTitle.urlProperty data.cardTitle.urlProperty
? `${data.cardTitle.prefix}/${ ? `${data.cardTitle.prefix}/${data.data[i][data.cardTitle.urlProperty]}`
data.data[i][data.cardTitle.urlProperty]
}`
: undefined : undefined
} }
subUrl={ subUrl={
data.cardSubtitle.urlProperty data.cardSubtitle.urlProperty
? `${data.cardSubtitle.prefix}/${ ? `${data.cardSubtitle.prefix}/${data.data[i][data.cardSubtitle.urlProperty]}`
data.data[i][data.cardSubtitle.urlProperty]
}`
: undefined : undefined
} }
lazyLoad lazyLoad
@ -134,14 +128,7 @@ function ListWrapper({
); );
} }
const GridViewType = ({ const GridViewType = ({ data, cardTitle, cardSubtitle, playClick, size, cacheType }: any) => {
data,
cardTitle,
cardSubtitle,
playClick,
size,
cacheType,
}: any) => {
return ( return (
<AutoSizer> <AutoSizer>
{({ height, width }: any) => ( {({ height, width }: any) => (

152
src/components/viewtypes/ListViewTable.tsx

@ -16,21 +16,11 @@ import {
StyledTableHeaderCell, StyledTableHeaderCell,
TableCellWrapper, TableCellWrapper,
} from './styled'; } from './styled';
import { import { formatSongDuration, isCached, getImageCachePath, formatDate } from '../../shared/utils';
formatSongDuration,
isCached,
getImageCachePath,
formatDate,
} from '../../shared/utils';
import cacheImage from '../shared/cacheImage'; import cacheImage from '../shared/cacheImage';
import { setRating, star, unstar } from '../../api/api'; import { setRating, star, unstar } from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { import { fixPlayer2Index, setSort, setStar, sortPlayQueue } from '../../redux/playQueueSlice';
fixPlayer2Index,
setSort,
setStar,
sortPlayQueue,
} from '../../redux/playQueueSlice';
import { StyledIconToggle, StyledRate } from '../shared/styled'; import { StyledIconToggle, StyledRate } from '../shared/styled';
import { addModalPage, setContextMenu } from '../../redux/miscSlice'; import { addModalPage, setContextMenu } from '../../redux/miscSlice';
import { import {
@ -170,9 +160,7 @@ const ListViewTable = ({
// to the mouse-entered row // to the mouse-entered row
const debouncedMouseEnterFn = _.debounce((rowData: any) => { const debouncedMouseEnterFn = _.debounce((rowData: any) => {
dispatch(setRangeSelected(rowData)); dispatch(setRangeSelected(rowData));
dispatch( dispatch(toggleRangeSelected(sortColumn && !nowPlaying ? sortedData : data));
toggleRangeSelected(sortColumn && !nowPlaying ? sortedData : data)
);
}, 100); }, 100);
const handleSelectMouseEnter = (rowData: any) => { const handleSelectMouseEnter = (rowData: any) => {
@ -185,13 +173,9 @@ const ListViewTable = ({
if (!nowPlaying) { if (!nowPlaying) {
if (sortColumn && sortType) { if (sortColumn && sortType) {
// Since the column title(id) won't always match the actual column dataKey, we need to match it // Since the column title(id) won't always match the actual column dataKey, we need to match it
const normalizedSortColumn = columns.find( const normalizedSortColumn = columns.find((c: any) => c.id === sortColumn);
(c: any) => c.id === sortColumn
);
const sortColumnDataKey = const sortColumnDataKey =
normalizedSortColumn.dataKey === 'combinedtitle' normalizedSortColumn.dataKey === 'combinedtitle' ? 'title' : normalizedSortColumn.dataKey;
? 'title'
: normalizedSortColumn.dataKey;
const sortData = _.orderBy( const sortData = _.orderBy(
data, data,
@ -213,13 +197,9 @@ const ListViewTable = ({
useEffect(() => { useEffect(() => {
if (nowPlaying) { if (nowPlaying) {
if (playQueue.sortColumn && playQueue.sortType) { if (playQueue.sortColumn && playQueue.sortType) {
const actualSortColumn = columns.find( const actualSortColumn = columns.find((c: any) => c.id === playQueue.sortColumn);
(c: any) => c.id === playQueue.sortColumn
);
const sortColumnDataKey = const sortColumnDataKey =
actualSortColumn.dataKey === 'combinedtitle' actualSortColumn.dataKey === 'combinedtitle' ? 'title' : actualSortColumn.dataKey;
? 'title'
: actualSortColumn.dataKey;
dispatch( dispatch(
sortPlayQueue({ sortPlayQueue({
@ -271,11 +251,9 @@ const ListViewTable = ({
onRowContextMenu={(rowData: any, e: any) => { onRowContextMenu={(rowData: any, e: any) => {
e.preventDefault(); e.preventDefault();
if ( if (
(misc.contextMenu.show === false || (misc.contextMenu.show === false || misc.contextMenu.rowId !== rowData.uniqueId) &&
misc.contextMenu.rowId !== rowData.uniqueId) && multiSelect.selected.filter((entry: any) => entry.uniqueId === rowData.uniqueId)
multiSelect.selected.filter( .length > 0
(entry: any) => entry.uniqueId === rowData.uniqueId
).length > 0
) { ) {
dispatch( dispatch(
setContextMenu({ setContextMenu({
@ -313,15 +291,9 @@ const ListViewTable = ({
); );
if (!miniView) { if (!miniView) {
settings.setSync( settings.setSync(`${listType}ListColumns[${resizedColumnIndex}].width`, width);
`${listType}ListColumns[${resizedColumnIndex}].width`,
width
);
} else { } else {
settings.setSync( settings.setSync(`miniListColumns[${resizedColumnIndex}].width`, width);
`miniListColumns[${resizedColumnIndex}].width`,
width
);
} }
}} }}
> >
@ -333,16 +305,13 @@ const ListViewTable = ({
return ( return (
<TableCellWrapper <TableCellWrapper
playing={ playing={
(rowData.uniqueId === playQueue?.currentSongUniqueId && (rowData.uniqueId === playQueue?.currentSongUniqueId && nowPlaying) ||
nowPlaying) ||
(!nowPlaying && rowData.id === playQueue?.currentSongId) (!nowPlaying && rowData.id === playQueue?.currentSongId)
? 'true' ? 'true'
: 'false' : 'false'
} }
rowselected={ rowselected={
multiSelect?.selected.find( multiSelect?.selected.find((e: any) => e.uniqueId === rowData.uniqueId)
(e: any) => e.uniqueId === rowData.uniqueId
)
? 'true' ? 'true'
: 'false' : 'false'
} }
@ -374,11 +343,7 @@ const ListViewTable = ({
} }
}} }}
onMouseLeave={() => { onMouseLeave={() => {
if ( if ((multiSelect.currentMouseOverId || multiSelect.isDragging) && dnd) {
(multiSelect.currentMouseOverId ||
multiSelect.isDragging) &&
dnd
) {
dispatch( dispatch(
setCurrentMouseOverId({ setCurrentMouseOverId({
uniqueId: undefined, uniqueId: undefined,
@ -395,10 +360,7 @@ const ListViewTable = ({
); );
// Handle cases where we want to quickly drag/drop single rows // Handle cases where we want to quickly drag/drop single rows
if ( if (multiSelect.selected.length <= 1 || !isSelected) {
multiSelect.selected.length <= 1 ||
!isSelected
) {
dispatch(setSelectedSingle(rowData)); dispatch(setSelectedSingle(rowData));
dispatch( dispatch(
setCurrentMouseOverId({ setCurrentMouseOverId({
@ -447,9 +409,7 @@ const ListViewTable = ({
return ( return (
<TableCellWrapper <TableCellWrapper
rowselected={ rowselected={
multiSelect?.selected.find( multiSelect?.selected.find((e: any) => e.uniqueId === rowData.uniqueId)
(e: any) => e.uniqueId === rowData.uniqueId
)
? 'true' ? 'true'
: 'false' : 'false'
} }
@ -465,9 +425,7 @@ const ListViewTable = ({
rowIndex, rowIndex,
}) })
} }
onMouseDown={(e: any) => onMouseDown={(e: any) => handleSelectMouseDown(e, rowData)}
handleSelectMouseDown(e, rowData)
}
onMouseEnter={() => handleSelectMouseEnter(rowData)} onMouseEnter={() => handleSelectMouseEnter(rowData)}
onMouseUp={() => handleSelectMouseUp()} onMouseUp={() => handleSelectMouseUp()}
dragover={ dragover={
@ -514,10 +472,7 @@ const ListViewTable = ({
`${cacheImages.cacheType}_${ `${cacheImages.cacheType}_${
rowData[cacheImages.cacheIdProperty] rowData[cacheImages.cacheIdProperty]
}.jpg`, }.jpg`,
rowData.image.replace( rowData.image.replace(/size=\d+/, 'size=500')
/size=\d+/,
'size=500'
)
); );
} }
}} }}
@ -540,11 +495,9 @@ const ListViewTable = ({
> >
<CombinedTitleTextWrapper <CombinedTitleTextWrapper
playing={ playing={
(rowData.uniqueId === (rowData.uniqueId === playQueue?.currentSongUniqueId &&
playQueue?.currentSongUniqueId &&
nowPlaying) || nowPlaying) ||
(!nowPlaying && (!nowPlaying && rowData.id === playQueue?.currentSongId)
rowData.id === playQueue?.currentSongId)
? 'true' ? 'true'
: 'false' : 'false'
} }
@ -584,9 +537,7 @@ const ListViewTable = ({
appearance="link" appearance="link"
onClick={() => { onClick={() => {
if (rowData.artistId && !isModal) { if (rowData.artistId && !isModal) {
history.push( history.push(`/library/artist/${rowData.artistId}`);
`/library/artist/${rowData.artistId}`
);
} else if (rowData.artistId && isModal) { } else if (rowData.artistId && isModal) {
dispatch( dispatch(
addModalPage({ addModalPage({
@ -600,11 +551,9 @@ const ListViewTable = ({
fontSize: `${fontSize}px`, fontSize: `${fontSize}px`,
}} }}
playing={ playing={
(rowData.uniqueId === (rowData.uniqueId === playQueue?.currentSongUniqueId &&
playQueue?.currentSongUniqueId &&
nowPlaying) || nowPlaying) ||
(!nowPlaying && (!nowPlaying && rowData.id === playQueue?.currentSongId)
rowData.id === playQueue?.currentSongId)
? 'true' ? 'true'
: 'false' : 'false'
} }
@ -626,16 +575,12 @@ const ListViewTable = ({
return ( return (
<TableCellWrapper <TableCellWrapper
rowselected={ rowselected={
multiSelect?.selected.find( multiSelect?.selected.find((e: any) => e.uniqueId === rowData.uniqueId)
(e: any) => e.uniqueId === rowData.uniqueId
)
? 'true' ? 'true'
: 'false' : 'false'
} }
height={rowHeight} height={rowHeight}
onMouseDown={(e: any) => onMouseDown={(e: any) => handleSelectMouseDown(e, rowData)}
handleSelectMouseDown(e, rowData)
}
onMouseEnter={() => handleSelectMouseEnter(rowData)} onMouseEnter={() => handleSelectMouseEnter(rowData)}
onMouseUp={() => handleSelectMouseUp()} onMouseUp={() => handleSelectMouseUp()}
dragover={ dragover={
@ -683,26 +628,19 @@ const ListViewTable = ({
return ( return (
<TableCellWrapper <TableCellWrapper
playing={ playing={
(rowData.uniqueId === playQueue?.currentSongUniqueId && (rowData.uniqueId === playQueue?.currentSongUniqueId && nowPlaying) ||
nowPlaying) ||
(!nowPlaying && rowData.id === playQueue?.currentSongId) (!nowPlaying && rowData.id === playQueue?.currentSongId)
? 'true' ? 'true'
: 'false' : 'false'
} }
rowselected={ rowselected={
multiSelect?.selected.find( multiSelect?.selected.find((e: any) => e.uniqueId === rowData.uniqueId)
(e: any) => e.uniqueId === rowData.uniqueId
)
? 'true' ? 'true'
: 'false' : 'false'
} }
height={rowHeight} height={rowHeight}
onClick={(e: any) => { onClick={(e: any) => {
if ( if (!column.dataKey?.match(/starred|songCount|duration|userRating/)) {
!column.dataKey?.match(
/starred|songCount|duration|userRating/
)
) {
handleRowClick(e, { handleRowClick(e, {
...rowData, ...rowData,
rowIndex, rowIndex,
@ -710,20 +648,14 @@ const ListViewTable = ({
} }
}} }}
onDoubleClick={() => { onDoubleClick={() => {
if ( if (!column.dataKey?.match(/starred|songCount|duration|userRating/)) {
!column.dataKey?.match(
/starred|songCount|duration|userRating/
)
) {
handleRowDoubleClick({ handleRowDoubleClick({
...rowData, ...rowData,
rowIndex, rowIndex,
}); });
} }
}} }}
onMouseDown={(e: any) => onMouseDown={(e: any) => handleSelectMouseDown(e, rowData)}
handleSelectMouseDown(e, rowData)
}
onMouseEnter={() => handleSelectMouseEnter(rowData)} onMouseEnter={() => handleSelectMouseEnter(rowData)}
onMouseUp={() => handleSelectMouseUp()} onMouseUp={() => handleSelectMouseUp()}
dragover={ dragover={
@ -751,9 +683,7 @@ const ListViewTable = ({
onClick={() => { onClick={() => {
if (column.dataKey === 'album') { if (column.dataKey === 'album') {
if (rowData.albumId && !isModal) { if (rowData.albumId && !isModal) {
history.push( history.push(`/library/album/${rowData.albumId}`);
`/library/album/${rowData.albumId}`
);
} else if (rowData.albumId && isModal) { } else if (rowData.albumId && isModal) {
dispatch( dispatch(
addModalPage({ addModalPage({
@ -764,9 +694,7 @@ const ListViewTable = ({
} }
} else if (column.dataKey === 'artist') { } else if (column.dataKey === 'artist') {
if (rowData.artistId && !isModal) { if (rowData.artistId && !isModal) {
history.push( history.push(`/library/artist/${rowData.artistId}`);
`/library/artist/${rowData.artistId}`
);
} else if (rowData.artistId && isModal) { } else if (rowData.artistId && isModal) {
dispatch( dispatch(
addModalPage({ addModalPage({
@ -778,11 +706,8 @@ const ListViewTable = ({
} }
}} }}
playing={ playing={
(rowData.uniqueId === (rowData.uniqueId === playQueue?.currentSongUniqueId && nowPlaying) ||
playQueue?.currentSongUniqueId && (!nowPlaying && rowData.id === playQueue?.currentSongId)
nowPlaying) ||
(!nowPlaying &&
rowData.id === playQueue?.currentSongId)
? 'true' ? 'true'
: 'false' : 'false'
} }
@ -794,8 +719,7 @@ const ListViewTable = ({
</RsuiteLinkButton> </RsuiteLinkButton>
) : column.dataKey === 'duration' ? ( ) : column.dataKey === 'duration' ? (
formatSongDuration(rowData[column.dataKey]) formatSongDuration(rowData[column.dataKey])
) : column.dataKey === 'changed' || ) : column.dataKey === 'changed' || column.dataKey === 'created' ? (
column.dataKey === 'created' ? (
formatDate(rowData[column.dataKey]) formatDate(rowData[column.dataKey])
) : column.dataKey === 'starred' ? ( ) : column.dataKey === 'starred' ? (
<StyledIconToggle <StyledIconToggle
@ -810,9 +734,7 @@ const ListViewTable = ({
<StyledRate <StyledRate
size="sm" size="sm"
readOnly={false} readOnly={false}
defaultValue={ defaultValue={rowData?.userRating ? rowData.userRating : 0}
rowData?.userRating ? rowData.userRating : 0
}
onChange={(e: any) => handleRating(rowData, e)} onChange={(e: any) => handleRating(rowData, e)}
/> />
) : column.dataKey === 'bitRate' ? ( ) : column.dataKey === 'bitRate' ? (

8
src/components/viewtypes/ListViewType.tsx

@ -1,13 +1,7 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
// Resize derived from @nimrod-cohen https://gitter.im/rsuite/rsuite?at=5e1cd3f165540a529a0f5deb // Resize derived from @nimrod-cohen https://gitter.im/rsuite/rsuite?at=5e1cd3f165540a529a0f5deb
import React, { import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
useState,
useEffect,
useRef,
forwardRef,
useImperativeHandle,
} from 'react';
import { DOMHelper } from 'rsuite'; import { DOMHelper } from 'rsuite';
import { useAppSelector } from '../../redux/hooks'; import { useAppSelector } from '../../redux/hooks';
import PageLoader from '../loader/PageLoader'; import PageLoader from '../loader/PageLoader';

6
src/components/viewtypes/ViewTypeButtons.tsx

@ -3,11 +3,7 @@ import { ButtonToolbar, ButtonGroup, Icon } from 'rsuite';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { StyledIconButton } from '../shared/styled'; import { StyledIconButton } from '../shared/styled';
const ViewTypeButtons = ({ const ViewTypeButtons = ({ handleListClick, handleGridClick, viewTypeSetting }: any) => {
handleListClick,
handleGridClick,
viewTypeSetting,
}: any) => {
return ( return (
<ButtonToolbar> <ButtonToolbar>
<ButtonGroup> <ButtonGroup>

10
src/components/viewtypes/styled.tsx

@ -34,13 +34,10 @@ export const TableCellWrapper = styled.div<{
}>` }>`
background: ${(props) => background: ${(props) =>
props.rowselected === 'true' ? props.theme.primary.rowSelected : undefined}; props.rowselected === 'true' ? props.theme.primary.rowSelected : undefined};
color: ${(props) => color: ${(props) => (props.playing === 'true' ? props.theme.primary.main : undefined)};
props.playing === 'true' ? props.theme.primary.main : undefined};
line-height: ${(props) => (props.height ? `${props.height}px` : undefined)}; line-height: ${(props) => (props.height ? `${props.height}px` : undefined)};
box-shadow: ${(props) => box-shadow: ${(props) =>
props.dragover === 'true' props.dragover === 'true' ? `inset 0px 5px 0px -3px ${props.theme.primary.main}` : undefined};
? `inset 0px 5px 0px -3px ${props.theme.primary.main}`
: undefined};
cursor: ${(props) => cursor: ${(props) =>
props.dragover === 'true' props.dragover === 'true'
? 'grabbing' ? 'grabbing'
@ -52,8 +49,7 @@ export const TableCellWrapper = styled.div<{
`; `;
export const CombinedTitleTextWrapper = styled.span<{ playing: string }>` export const CombinedTitleTextWrapper = styled.span<{ playing: string }>`
color: ${(props) => color: ${(props) => (props.playing === 'true' ? props.theme.primary.main : undefined)};
props.playing === 'true' ? props.theme.primary.main : undefined};
`; `;
export const StyledTableHeaderCell = styled(Table.HeaderCell)` export const StyledTableHeaderCell = styled(Table.HeaderCell)`

10
src/hooks/useSearchQuery.ts

@ -1,11 +1,7 @@
import { useState, useEffect, SetStateAction } from 'react'; import { useState, useEffect, SetStateAction } from 'react';
import _ from 'lodash'; import _ from 'lodash';
const useSearchQuery = ( const useSearchQuery = (searchQuery: string, data: any[], filterProperties: string[]) => {
searchQuery: string,
data: any[],
filterProperties: string[]
) => {
const [filteredData, setFilteredData] = useState<any[]>([]); const [filteredData, setFilteredData] = useState<any[]>([]);
const [filterProps] = useState(filterProperties); const [filterProps] = useState(filterProperties);
@ -15,9 +11,7 @@ const useSearchQuery = (
const matches: SetStateAction<any[]> = []; const matches: SetStateAction<any[]> = [];
filterProps.map((prop: string) => { filterProps.map((prop: string) => {
const filteredDataByProp = data.filter((entry: any) => { const filteredDataByProp = data.filter((entry: any) => {
return entry[prop] return entry[prop]?.toLowerCase().includes(searchQuery.toLowerCase());
?.toLowerCase()
.includes(searchQuery.toLowerCase());
}); });
return filteredDataByProp.map((entry) => matches.push(entry)); return filteredDataByProp.map((entry) => matches.push(entry));

4
src/index.html

@ -35,9 +35,7 @@
if (scripts.length) { if (scripts.length) {
document.write( document.write(
scripts scripts.map((script) => `<script defer src="${script}"><\/script>`).join('')
.map((script) => `<script defer src="${script}"><\/script>`)
.join('')
); );
} }
</script> </script>

53
src/main.dev.js

@ -17,11 +17,7 @@ import electronLocalshortcut from 'electron-localshortcut';
import { autoUpdater } from 'electron-updater'; import { autoUpdater } from 'electron-updater';
import log from 'electron-log'; import log from 'electron-log';
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from '@reduxjs/toolkit';
import { import { forwardToRenderer, triggerAlias, replayActionMain } from 'electron-redux';
forwardToRenderer,
triggerAlias,
replayActionMain,
} from 'electron-redux';
import playerReducer, { resetPlayer, setStatus } from './redux/playerSlice'; import playerReducer, { resetPlayer, setStatus } from './redux/playerSlice';
import playQueueReducer, { import playQueueReducer, {
decrementCurrentIndex, decrementCurrentIndex,
@ -58,10 +54,7 @@ if (process.env.NODE_ENV === 'production') {
sourceMapSupport.install(); sourceMapSupport.install();
} }
if ( if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
require('electron-debug')(); require('electron-debug')();
} }
@ -79,10 +72,7 @@ const installExtensions = async () => {
}; };
const createWindow = async () => { const createWindow = async () => {
if ( if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
await installExtensions(); await installExtensions();
} }
@ -114,9 +104,7 @@ const createWindow = async () => {
if (settings.getSync('globalMediaHotkeys')) { if (settings.getSync('globalMediaHotkeys')) {
globalShortcut.register('MediaStop', () => { globalShortcut.register('MediaStop', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
store.dispatch(clearPlayQueue()); store.dispatch(clearPlayQueue());
@ -127,9 +115,7 @@ const createWindow = async () => {
globalShortcut.register('MediaPlayPause', () => { globalShortcut.register('MediaPlayPause', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
if (storeValues.player.status === 'PAUSED') { if (storeValues.player.status === 'PAUSED') {
@ -142,9 +128,7 @@ const createWindow = async () => {
globalShortcut.register('MediaNextTrack', () => { globalShortcut.register('MediaNextTrack', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
store.dispatch(resetPlayer()); store.dispatch(resetPlayer());
store.dispatch(incrementCurrentIndex('usingHotkey')); store.dispatch(incrementCurrentIndex('usingHotkey'));
@ -154,9 +138,7 @@ const createWindow = async () => {
globalShortcut.register('MediaPreviousTrack', () => { globalShortcut.register('MediaPreviousTrack', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
store.dispatch(resetPlayer()); store.dispatch(resetPlayer());
store.dispatch(decrementCurrentIndex('usingHotkey')); store.dispatch(decrementCurrentIndex('usingHotkey'));
@ -167,9 +149,7 @@ const createWindow = async () => {
} else { } else {
electronLocalshortcut.register(mainWindow, 'MediaStop', () => { electronLocalshortcut.register(mainWindow, 'MediaStop', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
store.dispatch(clearPlayQueue()); store.dispatch(clearPlayQueue());
@ -180,9 +160,7 @@ const createWindow = async () => {
electronLocalshortcut.register(mainWindow, 'MediaPlayPause', () => { electronLocalshortcut.register(mainWindow, 'MediaPlayPause', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
if (storeValues.player.status === 'PAUSED') { if (storeValues.player.status === 'PAUSED') {
@ -195,9 +173,7 @@ const createWindow = async () => {
electronLocalshortcut.register(mainWindow, 'MediaNextTrack', () => { electronLocalshortcut.register(mainWindow, 'MediaNextTrack', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
store.dispatch(resetPlayer()); store.dispatch(resetPlayer());
store.dispatch(incrementCurrentIndex('usingHotkey')); store.dispatch(incrementCurrentIndex('usingHotkey'));
@ -207,9 +183,7 @@ const createWindow = async () => {
electronLocalshortcut.register(mainWindow, 'MediaPreviousTrack', () => { electronLocalshortcut.register(mainWindow, 'MediaPreviousTrack', () => {
const storeValues = store.getState(); const storeValues = store.getState();
const currentEntryList = storeValues.playQueue.shuffle const currentEntryList = storeValues.playQueue.shuffle ? 'shuffledEntry' : 'entry';
? 'shuffledEntry'
: 'entry';
if (storeValues.playQueue[currentEntryList].length > 0) { if (storeValues.playQueue[currentEntryList].length > 0) {
store.dispatch(resetPlayer()); store.dispatch(resetPlayer());
store.dispatch(decrementCurrentIndex('usingHotkey')); store.dispatch(decrementCurrentIndex('usingHotkey'));
@ -266,10 +240,7 @@ app.on('window-all-closed', () => {
} }
}); });
app.commandLine.appendSwitch( app.commandLine.appendSwitch('disable-features', 'HardwareMediaKeyHandling,MediaSessionService');
'disable-features',
'HardwareMediaKeyHandling,MediaSessionService'
);
app.whenReady().then(createWindow).catch(console.log); app.whenReady().then(createWindow).catch(console.log);

39
src/menu.ts

@ -1,10 +1,4 @@
import { import { app, Menu, shell, BrowserWindow, MenuItemConstructorOptions } from 'electron';
app,
Menu,
shell,
BrowserWindow,
MenuItemConstructorOptions,
} from 'electron';
interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions { interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
selector?: string; selector?: string;
@ -19,17 +13,12 @@ export default class MenuBuilder {
} }
buildMenu(): Menu { buildMenu(): Menu {
if ( if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
process.env.NODE_ENV === 'development' ||
process.env.DEBUG_PROD === 'true'
) {
this.setupDevelopmentEnvironment(); this.setupDevelopmentEnvironment();
} }
const template = const template =
process.platform === 'darwin' process.platform === 'darwin' ? this.buildDarwinTemplate() : this.buildDefaultTemplate();
? this.buildDarwinTemplate()
: this.buildDefaultTemplate();
const menu = Menu.buildFromTemplate(template); const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu); Menu.setApplicationMenu(menu);
@ -163,9 +152,7 @@ export default class MenuBuilder {
{ {
label: 'Documentation', label: 'Documentation',
click() { click() {
shell.openExternal( shell.openExternal('https://github.com/electron/electron/tree/master/docs#readme');
'https://github.com/electron/electron/tree/master/docs#readme'
);
}, },
}, },
{ {
@ -184,8 +171,7 @@ export default class MenuBuilder {
}; };
const subMenuView = const subMenuView =
process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
process.env.DEBUG_PROD === 'true'
? subMenuViewDev ? subMenuViewDev
: subMenuViewProd; : subMenuViewProd;
@ -213,8 +199,7 @@ export default class MenuBuilder {
{ {
label: '&View', label: '&View',
submenu: submenu:
process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
process.env.DEBUG_PROD === 'true'
? [ ? [
{ {
label: '&Reload', label: '&Reload',
@ -227,9 +212,7 @@ export default class MenuBuilder {
label: 'Toggle &Full Screen', label: 'Toggle &Full Screen',
accelerator: 'F11', accelerator: 'F11',
click: () => { click: () => {
this.mainWindow.setFullScreen( this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
!this.mainWindow.isFullScreen()
);
}, },
}, },
{ {
@ -245,9 +228,7 @@ export default class MenuBuilder {
label: 'Toggle &Full Screen', label: 'Toggle &Full Screen',
accelerator: 'F11', accelerator: 'F11',
click: () => { click: () => {
this.mainWindow.setFullScreen( this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
!this.mainWindow.isFullScreen()
);
}, },
}, },
], ],
@ -264,9 +245,7 @@ export default class MenuBuilder {
{ {
label: 'Documentation', label: 'Documentation',
click() { click() {
shell.openExternal( shell.openExternal('https://github.com/electron/electron/tree/master/docs#readme');
'https://github.com/electron/electron/tree/master/docs#readme'
);
}, },
}, },
{ {

11
src/redux/miscSlice.ts

@ -2,8 +2,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import settings from 'electron-settings'; import settings from 'electron-settings';
import { mockSettings } from '../shared/mockSettings'; import { mockSettings } from '../shared/mockSettings';
const parsedSettings = const parsedSettings = process.env.NODE_ENV === 'test' ? mockSettings : settings.getSync();
process.env.NODE_ENV === 'test' ? mockSettings : settings.getSync();
export interface ModalPage { export interface ModalPage {
pageType: string; pageType: string;
@ -83,9 +82,7 @@ const miscSlice = createSlice({
}, },
removeProcessingPlaylist: (state, action: PayloadAction<string>) => { removeProcessingPlaylist: (state, action: PayloadAction<string>) => {
const filtered = state.isProcessingPlaylist.filter( const filtered = state.isProcessingPlaylist.filter((id: string) => id !== action.payload);
(id: string) => id !== action.payload
);
state.isProcessingPlaylist = filtered; state.isProcessingPlaylist = filtered;
}, },
@ -109,9 +106,7 @@ const miscSlice = createSlice({
if ( if (
state.modalPages[ state.modalPages[
state.modal.currentPageIndex === undefined state.modal.currentPageIndex === undefined ? 0 : state.modal.currentPageIndex
? 0
: state.modal.currentPageIndex
]?.id !== action.payload.id ]?.id !== action.payload.id
) { ) {
state.modalPages.push(action.payload); state.modalPages.push(action.payload);

16
src/redux/multiSelectSlice.ts

@ -72,9 +72,7 @@ const multiSelectSlice = createSlice({
lastRangeSelected: {}, lastRangeSelected: {},
}; };
if ( if (state.selected.find((item) => item.uniqueId === action.payload.uniqueId)) {
state.selected.find((item) => item.uniqueId === action.payload.uniqueId)
) {
state.selected = []; state.selected = [];
} else { } else {
state.selected = []; state.selected = [];
@ -111,9 +109,7 @@ const multiSelectSlice = createSlice({
}, },
toggleSelected: (state, action: PayloadAction<any>) => { toggleSelected: (state, action: PayloadAction<any>) => {
if ( if (state.selected.find((item) => item.uniqueId === action.payload.uniqueId)) {
state.selected.find((item) => item.uniqueId === action.payload.uniqueId)
) {
const indexOfItem = state.selected.findIndex( const indexOfItem = state.selected.findIndex(
(item) => item.uniqueId === action.payload.uniqueId (item) => item.uniqueId === action.payload.uniqueId
); );
@ -128,17 +124,13 @@ const multiSelectSlice = createSlice({
}, },
toggleRangeSelected: (state, action: PayloadAction<any[]>) => { toggleRangeSelected: (state, action: PayloadAction<any[]>) => {
if ( if (state.lastSelected.uniqueId === state.lastRangeSelected.lastSelected.uniqueId) {
state.lastSelected.uniqueId ===
state.lastRangeSelected.lastSelected.uniqueId
) {
const beginningIndex = action.payload.findIndex( const beginningIndex = action.payload.findIndex(
(e) => e.uniqueId === state.lastSelected.uniqueId (e) => e.uniqueId === state.lastSelected.uniqueId
); );
const endingIndex = action.payload.findIndex( const endingIndex = action.payload.findIndex(
(e) => (e) => e.uniqueId === state.lastRangeSelected.lastRangeSelected.uniqueId
e.uniqueId === state.lastRangeSelected.lastRangeSelected.uniqueId
); );
// Handle both selection directions // Handle both selection directions

146
src/redux/playQueueSlice.ts

@ -6,8 +6,7 @@ import arrayMove from 'array-move';
import { areConsecutive, consecutiveRanges } from '../shared/utils'; import { areConsecutive, consecutiveRanges } from '../shared/utils';
import { mockSettings } from '../shared/mockSettings'; import { mockSettings } from '../shared/mockSettings';
const parsedSettings = const parsedSettings = process.env.NODE_ENV === 'test' ? mockSettings : settings.getSync();
process.env.NODE_ENV === 'test' ? mockSettings : settings.getSync();
export interface Entry { export interface Entry {
id: string; id: string;
@ -160,17 +159,9 @@ const removeItem = (array: any, index: any) => {
}; };
const entrySelect = (state: PlayQueue) => const entrySelect = (state: PlayQueue) =>
state.sortedEntry.length > 0 state.sortedEntry.length > 0 ? 'sortedEntry' : state.shuffle ? 'shuffledEntry' : 'entry';
? 'sortedEntry'
: state.shuffle const getNextPlayerIndex = (length: number, repeat: string, currentIndex: number) => {
? 'shuffledEntry'
: 'entry';
const getNextPlayerIndex = (
length: number,
repeat: string,
currentIndex: number
) => {
if (length >= 2 && repeat !== 'one') { if (length >= 2 && repeat !== 'one') {
if (currentIndex + 1 === length) { if (currentIndex + 1 === length) {
return 0; return 0;
@ -187,10 +178,7 @@ export const getCurrentEntryIndex = (entries: any[], currentSongId: string) => {
return entries.findIndex((entry: any) => entry.id === currentSongId); return entries.findIndex((entry: any) => entry.id === currentSongId);
}; };
export const getCurrentEntryIndexByUID = ( export const getCurrentEntryIndexByUID = (entries: any[], currentSongId: string) => {
entries: any[],
currentSongId: string
) => {
return entries.findIndex((entry: any) => entry.uniqueId === currentSongId); return entries.findIndex((entry: any) => entry.uniqueId === currentSongId);
}; };
@ -204,10 +192,7 @@ const playQueueSlice = createSlice({
name: 'nowPlaying', name: 'nowPlaying',
initialState, initialState,
reducers: { reducers: {
setPlayerSrc: ( setPlayerSrc: (state, action: PayloadAction<{ player: number; src: string }>) => {
state,
action: PayloadAction<{ player: number; src: string }>
) => {
if (action.payload.player === 1) { if (action.payload.player === 1) {
state.player1.src = action.payload.src; state.player1.src = action.payload.src;
} else { } else {
@ -230,10 +215,7 @@ const playQueueSlice = createSlice({
state.currentIndex = newCurrentSongIndex; state.currentIndex = newCurrentSongIndex;
}, },
setSort: ( setSort: (state, action: PayloadAction<{ sortColumn?: string; sortType: 'asc' | 'desc' }>) => {
state,
action: PayloadAction<{ sortColumn?: string; sortType: 'asc' | 'desc' }>
) => {
state.sortColumn = action.payload.sortColumn; state.sortColumn = action.payload.sortColumn;
state.sortType = action.payload.sortType; state.sortType = action.payload.sortType;
}, },
@ -259,9 +241,7 @@ const playQueueSlice = createSlice({
const currentEntry = entrySelect(state); const currentEntry = entrySelect(state);
const newCurrentSongIndex = getCurrentEntryIndexByUID( const newCurrentSongIndex = getCurrentEntryIndexByUID(
action.payload.columnDataKey !== '' action.payload.columnDataKey !== '' ? state.sortedEntry : state[currentEntry],
? state.sortedEntry
: state[currentEntry],
state.currentSongUniqueId state.currentSongUniqueId
); );
@ -284,10 +264,7 @@ const playQueueSlice = createSlice({
state.currentSongUniqueId = state[currentEntry][0].uniqueId; state.currentSongUniqueId = state[currentEntry][0].uniqueId;
}, },
setPlaybackSetting: ( setPlaybackSetting: (state, action: PayloadAction<{ setting: string; value: any }>) => {
state,
action: PayloadAction<{ setting: string; value: any }>
) => {
switch (action.payload.setting) { switch (action.payload.setting) {
case 'fadeDuration': case 'fadeDuration':
state.fadeDuration = action.payload.value; state.fadeDuration = action.payload.value;
@ -325,15 +302,11 @@ const playQueueSlice = createSlice({
switch (action.payload.player) { switch (action.payload.player) {
case 1: case 1:
state.player1.fadeData.volumeData.push(action.payload.volume || 0); state.player1.fadeData.volumeData.push(action.payload.volume || 0);
state.player1.fadeData.timeData.push( state.player1.fadeData.timeData.push(action.payload.time?.toFixed(2) || '0');
action.payload.time?.toFixed(2) || '0'
);
break; break;
case 2: case 2:
state.player2.fadeData.volumeData.push(action.payload.volume || 0); state.player2.fadeData.volumeData.push(action.payload.volume || 0);
state.player2.fadeData.timeData.push( state.player2.fadeData.timeData.push(action.payload.time?.toFixed(2) || '0');
action.payload.time?.toFixed(2) || '0'
);
break; break;
default: default:
break; break;
@ -389,10 +362,7 @@ const playQueueSlice = createSlice({
We want to swap the currentIndex over to the currently playing track since its row index We want to swap the currentIndex over to the currently playing track since its row index
will change */ will change */
const currentEntryIndex = getCurrentEntryIndex( const currentEntryIndex = getCurrentEntryIndex(state.entry, state.currentSongId);
state.entry,
state.currentSongId
);
/* Account for the currentPlayer and set the player indexes accordingly. Unfortunately /* Account for the currentPlayer and set the player indexes accordingly. Unfortunately
since we're updating the indexes here, the transition won't be seamless and the currently since we're updating the indexes here, the transition won't be seamless and the currently
@ -401,19 +371,11 @@ const playQueueSlice = createSlice({
state.player1.index = state.player1.index =
state.currentPlayer === 1 state.currentPlayer === 1
? currentEntryIndex ? currentEntryIndex
: getNextPlayerIndex( : getNextPlayerIndex(state.entry.length, state.repeat, state.currentIndex);
state.entry.length,
state.repeat,
state.currentIndex
);
state.player2.index = state.player2.index =
state.currentPlayer === 2 state.currentPlayer === 2
? currentEntryIndex ? currentEntryIndex
: getNextPlayerIndex( : getNextPlayerIndex(state.entry.length, state.repeat, state.currentIndex);
state.entry.length,
state.repeat,
state.currentIndex
);
// Free up memory by clearing out the shuffled entries // Free up memory by clearing out the shuffled entries
state.shuffledEntry = []; state.shuffledEntry = [];
@ -544,8 +506,7 @@ const playQueueSlice = createSlice({
handleGaplessPlayback(state); handleGaplessPlayback(state);
state.current = { ...state[currentEntry][state.currentIndex] }; state.current = { ...state[currentEntry][state.currentIndex] };
state.currentSongId = state[currentEntry][state.currentIndex].id; state.currentSongId = state[currentEntry][state.currentIndex].id;
state.currentSongUniqueId = state.currentSongUniqueId = state[currentEntry][state.currentIndex].uniqueId;
state[currentEntry][state.currentIndex].uniqueId;
}, },
incrementPlayerIndex: (state, action: PayloadAction<number>) => { incrementPlayerIndex: (state, action: PayloadAction<number>) => {
@ -556,10 +517,7 @@ const playQueueSlice = createSlice({
without changing the index of either player */ without changing the index of either player */
if (state[currentEntry].length > 2 && state.repeat !== 'one') { if (state[currentEntry].length > 2 && state.repeat !== 'one') {
if (action.payload === 1) { if (action.payload === 1) {
if ( if (state.player1.index + 1 === state[currentEntry].length && state.repeat === 'none') {
state.player1.index + 1 === state[currentEntry].length &&
state.repeat === 'none'
) {
// Reset the player on the end of the playlist if no repeat // Reset the player on the end of the playlist if no repeat
resetPlayerDefaults(state); resetPlayerDefaults(state);
handleGaplessPlayback(state); handleGaplessPlayback(state);
@ -576,10 +534,7 @@ const playQueueSlice = createSlice({
} }
state.currentPlayer = 2; state.currentPlayer = 2;
} else { } else {
if ( if (state.player2.index + 1 === state[currentEntry].length && state.repeat === 'none') {
state.player2.index + 1 === state[currentEntry].length &&
state.repeat === 'none'
) {
// Reset the player on the end of the playlist if no repeat // Reset the player on the end of the playlist if no repeat
resetPlayerDefaults(state); resetPlayerDefaults(state);
handleGaplessPlayback(state); handleGaplessPlayback(state);
@ -602,10 +557,7 @@ const playQueueSlice = createSlice({
setPlayerIndex: (state, action: PayloadAction<Entry>) => { setPlayerIndex: (state, action: PayloadAction<Entry>) => {
const currentEntry = entrySelect(state); const currentEntry = entrySelect(state);
const findIndex = getCurrentEntryIndexByUID( const findIndex = getCurrentEntryIndexByUID(state[currentEntry], action.payload.uniqueId);
state[currentEntry],
action.payload.uniqueId
);
state.isFading = false; state.isFading = false;
state.player1.index = findIndex; state.player1.index = findIndex;
@ -622,10 +574,7 @@ const playQueueSlice = createSlice({
state.currentSongUniqueId = action.payload.uniqueId; state.currentSongUniqueId = action.payload.uniqueId;
}, },
setPlayerVolume: ( setPlayerVolume: (state, action: PayloadAction<{ player: number; volume: number }>) => {
state,
action: PayloadAction<{ player: number; volume: number }>
) => {
if (action.payload.player === 1) { if (action.payload.player === 1) {
state.player1.volume = action.payload.volume; state.player1.volume = action.payload.volume;
} else { } else {
@ -658,8 +607,7 @@ const playQueueSlice = createSlice({
handleGaplessPlayback(state); handleGaplessPlayback(state);
state.current = { ...state[currentEntry][state.currentIndex] }; state.current = { ...state[currentEntry][state.currentIndex] };
state.currentSongId = state[currentEntry][state.currentIndex].id; state.currentSongId = state[currentEntry][state.currentIndex].id;
state.currentSongUniqueId = state.currentSongUniqueId = state[currentEntry][state.currentIndex].uniqueId;
state[currentEntry][state.currentIndex].uniqueId;
} }
}, },
@ -689,10 +637,7 @@ const playQueueSlice = createSlice({
setCurrentIndex: (state, action: PayloadAction<Entry>) => { setCurrentIndex: (state, action: PayloadAction<Entry>) => {
const currentEntry = entrySelect(state); const currentEntry = entrySelect(state);
const findIndex = getCurrentEntryIndexByUID( const findIndex = getCurrentEntryIndexByUID(state[currentEntry], action.payload.uniqueId);
state[currentEntry],
action.payload.uniqueId
);
state.currentIndex = findIndex; state.currentIndex = findIndex;
state.current = { ...action.payload }; state.current = { ...action.payload };
@ -798,15 +743,10 @@ const playQueueSlice = createSlice({
} }
}, },
removeFromPlayQueue: ( removeFromPlayQueue: (state, action: PayloadAction<{ entries: Entry[] }>) => {
state,
action: PayloadAction<{ entries: Entry[] }>
) => {
const uniqueIds = _.map(action.payload.entries, 'uniqueId'); const uniqueIds = _.map(action.payload.entries, 'uniqueId');
state.entry = state.entry.filter( state.entry = state.entry.filter((entry) => !uniqueIds.includes(entry.uniqueId));
(entry) => !uniqueIds.includes(entry.uniqueId)
);
state.shuffledEntry = (state.shuffledEntry || []).filter( state.shuffledEntry = (state.shuffledEntry || []).filter(
(entry) => !uniqueIds.includes(entry.uniqueId) (entry) => !uniqueIds.includes(entry.uniqueId)
@ -847,11 +787,7 @@ const playQueueSlice = createSlice({
// We'll recalculate the currentSongIndex just in case the existing index was modified // We'll recalculate the currentSongIndex just in case the existing index was modified
// due to removing row entries that are before the current song // due to removing row entries that are before the current song
const newCurrentSongIndex = getCurrentEntryIndexByUID( const newCurrentSongIndex = getCurrentEntryIndexByUID(
state.sortColumn state.sortColumn ? state.sortedEntry : state.shuffle ? state.shuffledEntry : state.entry,
? state.sortedEntry
: state.shuffle
? state.shuffledEntry
: state.entry,
state.currentSongUniqueId state.currentSongUniqueId
); );
@ -883,10 +819,7 @@ const playQueueSlice = createSlice({
state.isFading = action.payload; state.isFading = action.payload;
}, },
moveToIndex: ( moveToIndex: (state, action: PayloadAction<{ entries: Entry[]; moveBeforeId: string }>) => {
state,
action: PayloadAction<{ entries: Entry[]; moveBeforeId: string }>
) => {
const currentEntry = entrySelect(state); const currentEntry = entrySelect(state);
const tempQueue = state[currentEntry].slice(); const tempQueue = state[currentEntry].slice();
const uniqueIds = _.map(action.payload.entries, 'uniqueId'); const uniqueIds = _.map(action.payload.entries, 'uniqueId');
@ -898,10 +831,7 @@ const playQueueSlice = createSlice({
/* Used if dragging onto the first selected row. We'll need to calculate the number of selected rows above the first selected row /* Used if dragging onto the first selected row. We'll need to calculate the number of selected rows above the first selected row
so we can subtract it from the spliceIndexPre value when moving it into the newQueue, which has all selected entries removed */ so we can subtract it from the spliceIndexPre value when moving it into the newQueue, which has all selected entries removed */
const spliceIndexPre = getCurrentEntryIndexByUID( const spliceIndexPre = getCurrentEntryIndexByUID(tempQueue, action.payload.moveBeforeId);
tempQueue,
action.payload.moveBeforeId
);
const queueAbovePre = tempQueue.slice(0, spliceIndexPre); const queueAbovePre = tempQueue.slice(0, spliceIndexPre);
const selectedAbovePre = queueAbovePre.filter((entry: Entry) => { const selectedAbovePre = queueAbovePre.filter((entry: Entry) => {
@ -909,10 +839,7 @@ const playQueueSlice = createSlice({
}); });
// Used if dragging onto a non-selected row // Used if dragging onto a non-selected row
const spliceIndexPost = getCurrentEntryIndexByUID( const spliceIndexPost = getCurrentEntryIndexByUID(newQueue, action.payload.moveBeforeId);
newQueue,
action.payload.moveBeforeId
);
/* Used if dragging onto consecutive selected rows /* Used if dragging onto consecutive selected rows
If the moveBeforeId index is selected, then we find the first consecutive selected index to move to */ If the moveBeforeId index is selected, then we find the first consecutive selected index to move to */
@ -948,9 +875,7 @@ const playQueueSlice = createSlice({
}); });
// Sort the entries by their rowIndex so that we can re-add them in the proper order // Sort the entries by their rowIndex so that we can re-add them in the proper order
const sortedEntries = updatedEntries.sort( const sortedEntries = updatedEntries.sort((a, b) => a.rowIndex - b.rowIndex);
(a, b) => a.rowIndex - b.rowIndex
);
// Splice the entries into the new queue array // Splice the entries into the new queue array
newQueue.splice(spliceIndex, 0, ...sortedEntries); newQueue.splice(spliceIndex, 0, ...sortedEntries);
@ -960,10 +885,7 @@ const playQueueSlice = createSlice({
// We'll need to fix the current player index after swapping the queue order // We'll need to fix the current player index after swapping the queue order
// This will be used in conjunction with fixPlayer2Index // This will be used in conjunction with fixPlayer2Index
const newCurrentSongIndex = getCurrentEntryIndexByUID( const newCurrentSongIndex = getCurrentEntryIndexByUID(newQueue, state.currentSongUniqueId);
newQueue,
state.currentSongUniqueId
);
if (state.currentPlayer === 1) { if (state.currentPlayer === 1) {
state.player1.index = newCurrentSongIndex; state.player1.index = newCurrentSongIndex;
@ -1008,10 +930,7 @@ const playQueueSlice = createSlice({
// We'll need to fix the current player index after swapping the queue order // We'll need to fix the current player index after swapping the queue order
// This will be used in conjunction with fixPlayer2Index // This will be used in conjunction with fixPlayer2Index
const newCurrentSongIndex = getCurrentEntryIndexByUID( const newCurrentSongIndex = getCurrentEntryIndexByUID(tempQueue, state.currentSongUniqueId);
tempQueue,
state.currentSongUniqueId
);
if (state.currentPlayer === 1) { if (state.currentPlayer === 1) {
state.player1.index = newCurrentSongIndex; state.player1.index = newCurrentSongIndex;
@ -1056,10 +975,7 @@ const playQueueSlice = createSlice({
// We'll need to fix the current player index after swapping the queue order // We'll need to fix the current player index after swapping the queue order
// This will be used in conjunction with fixPlayer2Index // This will be used in conjunction with fixPlayer2Index
const newCurrentSongIndex = getCurrentEntryIndexByUID( const newCurrentSongIndex = getCurrentEntryIndexByUID(tempQueue, state.currentSongUniqueId);
tempQueue,
state.currentSongUniqueId
);
if (state.currentPlayer === 1) { if (state.currentPlayer === 1) {
state.player1.index = newCurrentSongIndex; state.player1.index = newCurrentSongIndex;

44
src/shared/utils.ts

@ -89,10 +89,7 @@ export const shuffle = (array: any[]) => {
currentIndex -= 1; currentIndex -= 1;
// And swap it with the current element. // And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [ [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
array[randomIndex],
array[currentIndex],
];
} }
return array; return array;
@ -219,11 +216,7 @@ export const consecutiveRanges = (a: number[]) => {
return list; return list;
}; };
export const moveToIndex = ( export const moveToIndex = (entryData: any, selectedEntries: any, moveBeforeId: string) => {
entryData: any,
selectedEntries: any,
moveBeforeId: string
) => {
const uniqueIds: any[] = []; const uniqueIds: any[] = [];
selectedEntries.map((entry: any) => uniqueIds.push(entry.uniqueId)); selectedEntries.map((entry: any) => uniqueIds.push(entry.uniqueId));
@ -234,19 +227,13 @@ export const moveToIndex = (
// Used if dragging onto the first selected row. We'll need to calculate the number of selected rows above the first selected row // Used if dragging onto the first selected row. We'll need to calculate the number of selected rows above the first selected row
// so we can subtract it from the spliceIndexPre value when moving it into the newList, which has all selected entries removed // so we can subtract it from the spliceIndexPre value when moving it into the newList, which has all selected entries removed
const spliceIndexPre = entryData.findIndex( const spliceIndexPre = entryData.findIndex((entry: any) => entry.uniqueId === moveBeforeId);
(entry: any) => entry.uniqueId === moveBeforeId
);
const queueAbovePre = entryData.slice(0, spliceIndexPre); const queueAbovePre = entryData.slice(0, spliceIndexPre);
const selectedAbovePre = queueAbovePre.filter((entry: any) => const selectedAbovePre = queueAbovePre.filter((entry: any) => uniqueIds.includes(entry.uniqueId));
uniqueIds.includes(entry.uniqueId)
);
// Used if dragging onto a non-selected row // Used if dragging onto a non-selected row
const spliceIndexPost = newList.findIndex( const spliceIndexPost = newList.findIndex((entry: any) => entry.uniqueId === moveBeforeId);
(entry: any) => entry.uniqueId === moveBeforeId
);
// Used if dragging onto consecutive selected rows // Used if dragging onto consecutive selected rows
// If the moveBeforeId index is selected, then we find the first consecutive selected index to move to // If the moveBeforeId index is selected, then we find the first consecutive selected index to move to
@ -275,16 +262,12 @@ export const moveToIndex = (
// Get the updated entry rowIndexes since dragging an entry multiple times will change the existing selected rowIndex // Get the updated entry rowIndexes since dragging an entry multiple times will change the existing selected rowIndex
const updatedEntries = selectedEntries.map((entry: any) => { const updatedEntries = selectedEntries.map((entry: any) => {
const findIndex = entryData.findIndex( const findIndex = entryData.findIndex((item: any) => item.uniqueId === entry.uniqueId);
(item: any) => item.uniqueId === entry.uniqueId
);
return { ...entry, rowIndex: findIndex }; return { ...entry, rowIndex: findIndex };
}); });
// Sort the entries by their rowIndex so that we can re-add them in the proper order // Sort the entries by their rowIndex so that we can re-add them in the proper order
const sortedEntries = updatedEntries.sort( const sortedEntries = updatedEntries.sort((a: any, b: any) => a.rowIndex - b.rowIndex);
(a: any, b: any) => a.rowIndex - b.rowIndex
);
// Splice the entries into the new queue array // Splice the entries into the new queue array
newList.splice(spliceIndex, 0, ...sortedEntries); newList.splice(spliceIndex, 0, ...sortedEntries);
@ -293,21 +276,14 @@ export const moveToIndex = (
return newList; return newList;
}; };
export const getUpdatedEntryRowIndex = ( export const getUpdatedEntryRowIndex = (selectedEntries: any, entryData: any) => {
selectedEntries: any,
entryData: any
) => {
const updatedEntries = selectedEntries.map((entry: any) => { const updatedEntries = selectedEntries.map((entry: any) => {
const findIndex = entryData.findIndex( const findIndex = entryData.findIndex((item: any) => item.uniqueId === entry.uniqueId);
(item: any) => item.uniqueId === entry.uniqueId
);
return { ...entry, rowIndex: findIndex }; return { ...entry, rowIndex: findIndex };
}); });
// Sort the entries by their rowIndex so that we can re-add them in the proper order // Sort the entries by their rowIndex so that we can re-add them in the proper order
const sortedEntries = updatedEntries.sort( const sortedEntries = updatedEntries.sort((a: any, b: any) => a.rowIndex - b.rowIndex);
(a: any, b: any) => a.rowIndex - b.rowIndex
);
return sortedEntries; return sortedEntries;
}; };

7
yarn.lock

@ -13004,12 +13004,7 @@ yargs-parser@^18.1.2:
camelcase "^5.0.0" camelcase "^5.0.0"
decamelize "^1.2.0" decamelize "^1.2.0"
yargs-parser@^20.2.2: yargs-parser@^20.2.2, yargs-parser@^20.2.3:
version "20.2.4"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
yargs-parser@^20.2.3:
version "20.2.9" version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==

Loading…
Cancel
Save