Browse Source

Move list-view favorite handler outside component

master
jeffvli 3 years ago
parent
commit
b7038c75d4
  1. 2
      src/api/api.ts
  2. 27
      src/components/library/AlbumList.tsx
  3. 35
      src/components/library/AlbumView.tsx
  4. 28
      src/components/library/ArtistList.tsx
  5. 32
      src/components/library/ArtistView.tsx
  6. 13
      src/components/player/NowPlayingMiniView.tsx
  7. 14
      src/components/player/NowPlayingView.tsx
  8. 41
      src/components/playlist/PlaylistView.tsx
  9. 32
      src/components/starred/StarredView.tsx
  10. 32
      src/components/viewtypes/ListViewTable.tsx
  11. 2
      src/components/viewtypes/ListViewType.tsx
  12. 22
      src/redux/playlistSlice.ts

2
src/api/api.ts

@ -226,7 +226,7 @@ export const getStarred = async () => {
albumCount: entry.albumCount || undefined,
coverArt: getCoverArtUrl(entry),
image: getCoverArtUrl(entry),
starred: entry.starred || undefined,
starred: entry.starred || Date.now(), // Airsonic does not return the starred date
type: 'artist',
index,
uniqueId: nanoid(),

27
src/components/library/AlbumList.tsx

@ -9,7 +9,7 @@ import ListViewType from '../viewtypes/ListViewType';
import useSearchQuery from '../../hooks/useSearchQuery';
import GenericPageHeader from '../layout/GenericPageHeader';
import GenericPage from '../layout/GenericPage';
import { getAlbumsDirect, getAllAlbums, getGenres } from '../../api/api';
import { getAlbumsDirect, getAllAlbums, getGenres, star, unstar } from '../../api/api';
import PageLoader from '../loader/PageLoader';
import { useAppDispatch } from '../../redux/hooks';
import {
@ -107,6 +107,30 @@ const AlbumList = () => {
setIsRefreshing(false);
};
const handleRowFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, 'album');
queryClient.setQueryData(['albumList', offset, sortBy], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData[index].starred = Date.now();
});
return oldData;
});
} else {
await unstar(rowData.id, 'album');
queryClient.setQueryData(['albumList', offset, sortBy], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData[index].starred = undefined;
});
return oldData;
});
}
};
return (
<GenericPage
hideDivider
@ -165,6 +189,7 @@ const AlbumList = () => {
listType="album"
virtualized
disabledContextMenuOptions={['moveSelectedTo', 'removeFromCurrent', 'deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
)}
{!isLoading && !isError && viewType === 'grid' && (

35
src/components/library/AlbumView.tsx

@ -1,4 +1,5 @@
import React, { useState } from 'react';
import _ from 'lodash';
import settings from 'electron-settings';
import { ButtonToolbar, Tag } from 'rsuite';
import { useQuery, useQueryClient } from 'react-query';
@ -11,6 +12,7 @@ import {
fixPlayer2Index,
setPlayQueue,
setPlayQueueByRowClick,
setStar,
} from '../../redux/playQueueSlice';
import {
toggleSelected,
@ -107,13 +109,37 @@ const AlbumView = ({ ...rest }: any) => {
const handleFavorite = async () => {
if (!data.starred) {
await star(data.id, 'album');
queryClient.setQueryData(['album', id], { ...data, starred: Date.now() });
} else {
await unstar(data.id, 'album');
queryClient.setQueryData(['album', id], { ...data, starred: undefined });
}
};
const handleRowFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, 'music');
dispatch(setStar({ id: rowData.id, type: 'star' }));
queryClient.setQueryData(['album', id], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.song, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData.song[index].starred = Date.now();
});
return oldData;
});
} else {
await unstar(rowData.id, 'music');
dispatch(setStar({ id: rowData.id, type: 'unstar' }));
queryClient.setQueryData(['album', id], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.song, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData.song[index].starred = undefined;
});
return oldData;
});
}
await queryClient.refetchQueries(['album', id], {
active: true,
exact: true,
});
};
if (isLoading) {
@ -203,6 +229,7 @@ const AlbumView = ({ ...rest }: any) => {
listType="music"
isModal={rest.isModal}
disabledContextMenuOptions={['removeFromCurrent', 'moveSelectedTo', 'deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
</GenericPage>
);

28
src/components/library/ArtistList.tsx

@ -1,9 +1,10 @@
import React, { useState } from 'react';
import _ from 'lodash';
import settings from 'electron-settings';
import { useQuery, useQueryClient } from 'react-query';
import { useHistory } from 'react-router';
import { ButtonToolbar } from 'rsuite';
import { getArtists } from '../../api/api';
import { getArtists, star, unstar } from '../../api/api';
import useSearchQuery from '../../hooks/useSearchQuery';
import GenericPage from '../layout/GenericPage';
import GenericPageHeader from '../layout/GenericPageHeader';
@ -67,6 +68,30 @@ const ArtistList = () => {
setIsRefreshing(false);
};
const handleRowFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, 'artist');
queryClient.setQueryData(['artistList'], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData[index].starred = Date.now();
});
return oldData;
});
} else {
await unstar(rowData.id, 'artist');
queryClient.setQueryData(['artistList'], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData[index].starred = undefined;
});
return oldData;
});
}
};
return (
<GenericPage
hideDivider
@ -107,6 +132,7 @@ const ArtistList = () => {
listType="artist"
virtualized
disabledContextMenuOptions={['moveSelectedTo', 'removeFromCurrent', 'deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
)}
{!isLoading && !isError && viewType === 'grid' && (

32
src/components/library/ArtistView.tsx

@ -1,5 +1,6 @@
/* eslint-disable import/no-cycle */
import React, { useState } from 'react';
import _ from 'lodash';
import settings from 'electron-settings';
import { ButtonToolbar, Tag, Whisper, Button, Popover, TagGroup } from 'rsuite';
import { useQuery, useQueryClient } from 'react-query';
@ -80,13 +81,11 @@ const ArtistView = ({ ...rest }: any) => {
const handleFavorite = async () => {
if (!data.starred) {
await star(data.id, 'artist');
queryClient.setQueryData(['artist', artistId], { ...data, starred: Date.now() });
} else {
await unstar(data.id, 'artist');
queryClient.setQueryData(['artist', artistId], { ...data, starred: undefined });
}
await queryClient.refetchQueries(['artist', artistId], {
active: true,
exact: true,
});
};
const handlePlay = async () => {
@ -113,6 +112,30 @@ const ArtistView = ({ ...rest }: any) => {
);
}
const handleRowFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, 'album');
queryClient.setQueryData(['artist', artistId], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData.album[index].starred = Date.now();
});
return oldData;
});
} else {
await unstar(rowData.id, 'album');
queryClient.setQueryData(['artist', artistId], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData.album[index].starred = undefined;
});
return oldData;
});
}
};
return (
<GenericPage
hideDivider
@ -211,6 +234,7 @@ const ArtistView = ({ ...rest }: any) => {
listType="album"
isModal={rest.isModal}
disabledContextMenuOptions={['removeFromCurrent', 'moveSelectedTo', 'deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
)}

13
src/components/player/NowPlayingMiniView.tsx

@ -22,6 +22,7 @@ import {
moveToIndex,
setPlaybackSetting,
removeFromPlayQueue,
setStar,
} from '../../redux/playQueueSlice';
import { resetPlayer, setStatus } from '../../redux/playerSlice';
import ListViewType from '../viewtypes/ListViewType';
@ -30,6 +31,7 @@ import { StyledCheckbox, StyledIconButton } from '../shared/styled';
import { MiniViewContainer } from './styled';
import { DeselectAllButton, MoveDownButton, MoveUpButton } from '../selectionbar/SelectionButtons';
import { getCurrentEntryList } from '../../shared/utils';
import { star, unstar } from '../../api/api';
const NowPlayingMiniView = () => {
const tableRef = useRef<any>();
@ -122,6 +124,16 @@ const NowPlayingMiniView = () => {
}
};
const handleRowFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, 'playlist');
dispatch(setStar({ id: rowData.id, type: 'star' }));
} else {
await unstar(rowData.id, 'playlist');
dispatch(setStar({ id: rowData.id, type: 'unstar' }));
}
};
return (
<>
{playQueue.displayQueue && (
@ -230,6 +242,7 @@ const NowPlayingMiniView = () => {
nowPlaying
dnd
disabledContextMenuOptions={['deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
</GenericPage>
</MiniViewContainer>

14
src/components/player/NowPlayingView.tsx

@ -17,6 +17,7 @@ import {
removeFromPlayQueue,
setPlayQueue,
appendPlayQueue,
setStar,
} from '../../redux/playQueueSlice';
import {
toggleSelected,
@ -40,7 +41,7 @@ import {
StyledPopover,
} from '../shared/styled';
import { errorMessages, getCurrentEntryList, isFailedResponse } from '../../shared/utils';
import { getGenres, getRandomSongs } from '../../api/api';
import { getGenres, getRandomSongs, star, unstar } from '../../api/api';
import { notifyToast } from '../shared/toast';
const NowPlayingView = () => {
@ -194,6 +195,16 @@ const NowPlayingView = () => {
return addRandomTriggerRef.current.close();
};
const handleRowFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, 'playlist');
dispatch(setStar({ id: rowData.id, type: 'star' }));
} else {
await unstar(rowData.id, 'playlist');
dispatch(setStar({ id: rowData.id, type: 'unstar' }));
}
};
return (
<GenericPage
hideDivider
@ -376,6 +387,7 @@ const NowPlayingView = () => {
nowPlaying
dnd
disabledContextMenuOptions={['deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
)}
</GenericPage>

41
src/components/playlist/PlaylistView.tsx

@ -22,6 +22,8 @@ import {
updatePlaylistSongsLg,
updatePlaylistSongs,
updatePlaylist,
star,
unstar,
} from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import {
@ -29,6 +31,7 @@ import {
setPlayQueueByRowClick,
setPlayQueue,
appendPlayQueue,
setStar,
} from '../../redux/playQueueSlice';
import {
toggleSelected,
@ -52,7 +55,12 @@ import { setStatus } from '../../redux/playerSlice';
import { notifyToast } from '../shared/toast';
import { addProcessingPlaylist, removeProcessingPlaylist } from '../../redux/miscSlice';
import { StyledButton, StyledCheckbox, StyledInputGroup } from '../shared/styled';
import { moveToIndex, removeFromPlaylist, setPlaylistData } from '../../redux/playlistSlice';
import {
moveToIndex,
removeFromPlaylist,
setPlaylistData,
setPlaylistStar,
} from '../../redux/playlistSlice';
interface PlaylistParams {
id: string;
@ -286,6 +294,36 @@ const PlaylistView = ({ ...rest }) => {
}
};
const handleRowFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, 'music');
dispatch(setStar({ id: rowData.id, type: 'star' }));
dispatch(setPlaylistStar({ id: rowData.id, type: 'star' }));
queryClient.setQueryData(['playlist', playlistId], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData.song, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData.song[index].starred = Date.now();
});
return oldData;
});
} else {
await unstar(rowData.id, 'music');
dispatch(setStar({ id: rowData.id, type: 'unstar' }));
dispatch(setPlaylistStar({ id: rowData.id, type: 'unstar' }));
queryClient.setQueryData(['playlist', playlistId], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData.song, { id: rowData.id }));
starredIndices.forEach((index) => {
oldData.song[index].starred = undefined;
});
return oldData;
});
}
};
if (isLoading) {
return <PageLoader />;
}
@ -440,6 +478,7 @@ const PlaylistView = ({ ...rest }) => {
dnd
isModal={rest.isModal}
disabledContextMenuOptions={['deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
</GenericPage>
);

32
src/components/starred/StarredView.tsx

@ -1,18 +1,18 @@
import React, { useState } from 'react';
import { useHistory } from 'react-router';
import { useQuery } from 'react-query';
import { useQuery, useQueryClient } from 'react-query';
import { Nav } from 'rsuite';
import settings from 'electron-settings';
import useSearchQuery from '../../hooks/useSearchQuery';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { fixPlayer2Index, setPlayQueueByRowClick } from '../../redux/playQueueSlice';
import { fixPlayer2Index, setPlayQueueByRowClick, setStar } from '../../redux/playQueueSlice';
import {
clearSelected,
toggleSelected,
toggleRangeSelected,
setRangeSelected,
} from '../../redux/multiSelectSlice';
import { getStarred } from '../../api/api';
import { getStarred, unstar } from '../../api/api';
import GenericPage from '../layout/GenericPage';
import GenericPageHeader from '../layout/GenericPageHeader';
import PageLoader from '../loader/PageLoader';
@ -26,6 +26,7 @@ const StarredView = () => {
const history = useHistory();
const dispatch = useAppDispatch();
const query = useRouterQuery();
const queryClient = useQueryClient();
const multiSelect = useAppSelector((state) => state.multiSelect);
const [page, setPage] = useState(query.get('page') || 'tracks');
const [viewType, setViewType] = useState(settings.getSync('albumViewType') || 'list');
@ -91,6 +92,28 @@ const StarredView = () => {
}
};
const handleRowFavorite = async (rowData: any) => {
await unstar(rowData.id, 'music');
dispatch(setStar({ id: rowData.id, type: 'unstar' }));
await queryClient.refetchQueries(['starred'], {
active: true,
});
};
const handleRowFavoriteAlbum = async (rowData: any) => {
await unstar(rowData.id, 'album');
await queryClient.refetchQueries(['starred'], {
active: true,
});
};
const handleRowFavoriteArtist = async (rowData: any) => {
await unstar(rowData.id, 'artist');
await queryClient.refetchQueries(['starred'], {
active: true,
});
};
if (isError) {
return <span>Error: {error.message}</span>;
}
@ -139,6 +162,7 @@ const StarredView = () => {
listType="music"
virtualized
disabledContextMenuOptions={['removeFromCurrent', 'moveSelectedTo', 'deletePlaylist']}
handleFavorite={handleRowFavorite}
/>
)}
{page === 'albums' && (
@ -163,6 +187,7 @@ const StarredView = () => {
'moveSelectedTo',
'deletePlaylist',
]}
handleFavorite={handleRowFavoriteAlbum}
/>
)}
{viewType === 'grid' && (
@ -209,6 +234,7 @@ const StarredView = () => {
'addToPlaylist',
'deletePlaylist',
]}
handleFavorite={handleRowFavoriteArtist}
/>
)}
{viewType === 'grid' && (

32
src/components/viewtypes/ListViewTable.tsx

@ -6,7 +6,6 @@ import _ from 'lodash';
import path from 'path';
import settings from 'electron-settings';
import styled from 'styled-components';
import { useQueryClient } from 'react-query';
import { useHotkeys } from 'react-hotkeys-hook';
import { nanoid } from 'nanoid';
import { Table, Grid, Row, Col } from 'rsuite';
@ -20,9 +19,9 @@ import {
} from './styled';
import { formatSongDuration, isCached, getImageCachePath, formatDate } from '../../shared/utils';
import cacheImage from '../shared/cacheImage';
import { setRating, star, unstar } from '../../api/api';
import { setRating } from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { fixPlayer2Index, setSort, setStar, sortPlayQueue } from '../../redux/playQueueSlice';
import { fixPlayer2Index, setSort, sortPlayQueue } from '../../redux/playQueueSlice';
import { StyledIconToggle, StyledRate } from '../shared/styled';
import { addModalPage, setContextMenu } from '../../redux/miscSlice';
import {
@ -73,11 +72,11 @@ const ListViewTable = ({
miniView,
dnd,
disabledContextMenuOptions,
handleFavorite,
}: any) => {
const history = useHistory();
const dispatch = useAppDispatch();
const misc = useAppSelector((state) => state.misc);
const queryClient = useQueryClient();
const [cachePath] = useState(path.join(getImageCachePath(), '/'));
const [sortColumn, setSortColumn] = useState<any>();
const [sortType, setSortType] = useState<any>();
@ -98,31 +97,6 @@ const ListViewTable = ({
[multiSelect.selected, data]
);
const handleFavorite = async (rowData: any) => {
if (!rowData.starred) {
await star(rowData.id, listType);
dispatch(setStar({ id: rowData.id, type: 'star' }));
} else {
await unstar(rowData.id, listType);
dispatch(setStar({ id: rowData.id, type: 'unstar' }));
}
await queryClient.refetchQueries(['starred'], {
active: true,
});
await queryClient.refetchQueries(['album'], {
active: true,
});
await queryClient.refetchQueries(['albumList'], {
active: true,
});
await queryClient.refetchQueries(['playlist'], {
active: true,
});
await queryClient.refetchQueries(['search'], {
active: true,
});
};
const handleRating = (rowData: any, e: number) => {
setRating(rowData.id, e);
};

2
src/components/viewtypes/ListViewType.tsx

@ -31,6 +31,7 @@ const ListViewType = (
dnd,
miniView,
disabledContextMenuOptions,
handleFavorite,
...rest
}: any,
ref: any
@ -329,6 +330,7 @@ const ListViewType = (
dnd={dnd}
miniView={miniView}
disabledContextMenuOptions={disabledContextMenuOptions}
handleFavorite={handleFavorite}
// onScroll={(e) => setScrollY(tableRef.current.scrollY)}
/>
)}

22
src/redux/playlistSlice.ts

@ -56,6 +56,27 @@ const playlistSlice = createSlice({
moveToBottom: (state, action: PayloadAction<{ selectedEntries: Entry[] }>) => {
state.entry = moveSelectedToBottom(state.entry, action.payload.selectedEntries);
},
setPlaylistStar: (state, action: PayloadAction<{ id: string; type: string }>) => {
// Since the playqueue can have multiples of the same song, we need to find
// all the indices of the starred/unstarred song.
const findIndices = state.entry
.map((entry, index) => (entry.id === action.payload.id ? index : ''))
.filter(String);
if (action.payload.type === 'unstar') {
findIndices?.map((rowIndex: any) => {
state.entry[rowIndex].starred = undefined;
return rowIndex;
});
} else {
findIndices?.map((rowIndex: any) => {
state.entry[rowIndex].starred = String(Date.now());
return rowIndex;
});
}
},
},
});
@ -67,5 +88,6 @@ export const {
moveDown,
moveToBottom,
moveToTop,
setPlaylistStar,
} = playlistSlice.actions;
export default playlistSlice.reducer;

Loading…
Cancel
Save