Browse Source

add context menu star functionality

- resize buttons
- revert back to default api for star/playlist due to 200 responses
- add sleep util function
master
jeffvli 3 years ago
parent
commit
ce74feca69
  1. 34
      src/api/api.ts
  2. 84
      src/components/shared/ContextMenu.tsx
  3. 10
      src/components/shared/styled.ts
  4. 23
      src/shared/utils.ts

34
src/api/api.ts

@ -46,13 +46,30 @@ api.interceptors.response.use(
axiosRetry(api, {
retries: 3,
retryDelay: (retryCount) => {
return retryCount * 1000;
},
});
export const playlistApi = axios.create({
export const autoFailApi = axios.create({
baseURL: API_BASE_URL,
validateStatus: () => {
return false;
},
});
autoFailApi.interceptors.request.use((config) => {
config.params = config.params || {};
config.params.u = auth.username;
config.params.s = auth.salt;
config.params.t = auth.hash;
config.params.v = '1.15.0';
config.params.c = 'sonixd';
config.params.f = 'json';
return config;
});
playlistApi.interceptors.response.use(
autoFailApi.interceptors.response.use(
(res) => {
// Return the subsonic response directly
res.data = res.data['subsonic-response'];
@ -63,10 +80,13 @@ playlistApi.interceptors.response.use(
}
);
axiosRetry(playlistApi, {
retries: 3,
axiosRetry(autoFailApi, {
retries: 5,
retryCondition: (e: any) => {
return e.status !== 'ok';
return e.response.data['subsonic-response'].status !== 'ok';
},
retryDelay: (retryCount) => {
return retryCount * 1000;
},
});
@ -501,7 +521,7 @@ export const clearPlaylist = async (id: string, entryCount: number) => {
}
data = (
await playlistApi.get(`/updatePlaylist`, {
await api.get(`/updatePlaylist`, {
params,
})
).data;
@ -531,7 +551,7 @@ export const populatePlaylist = async (id: string, entry: any[]) => {
}
data = (
await playlistApi.get(`/updatePlaylist`, {
await api.get(`/updatePlaylist`, {
params,
})
).data;

84
src/components/shared/ContextMenu.tsx

@ -1,16 +1,17 @@
/* eslint-disable no-await-in-loop */
import React, { useRef, useState } from 'react';
import { useQuery } from 'react-query';
import { useQuery, useQueryClient } from 'react-query';
import { useHistory } from 'react-router';
import { Popover, Whisper } from 'rsuite';
import { getPlaylists, populatePlaylist } from '../../api/api';
import { getPlaylists, populatePlaylist, star, unstar } from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import {
addProcessingPlaylist,
removeProcessingPlaylist,
setContextMenu,
} from '../../redux/miscSlice';
import { setStar } from '../../redux/playQueueSlice';
import {
ContextMenuTitle,
ContextMenuDivider,
ContextMenuWindow,
StyledContextMenuButton,
@ -18,10 +19,11 @@ import {
StyledButton,
} from './styled';
import { notifyToast } from './toast';
import { sleep } from '../../shared/utils';
export const ContextMenuButton = ({ children, ...rest }: any) => {
return (
<StyledContextMenuButton {...rest} appearance="subtle" size="xs" block>
<StyledContextMenuButton {...rest} appearance="subtle" size="sm" block>
{children}
</StyledContextMenuButton>
);
@ -53,6 +55,7 @@ export const ContextMenu = ({
export const GlobalContextMenu = () => {
const history = useHistory();
const dispatch = useAppDispatch();
const queryClient = useQueryClient();
const misc = useAppSelector((state) => state.misc);
const multiSelect = useAppSelector((state) => state.multiSelect);
const playlistTriggerRef = useRef<any>();
@ -111,6 +114,54 @@ export const GlobalContextMenu = () => {
dispatch(removeProcessingPlaylist(localSelectedPlaylistId));
};
const refetchAfterFavorite = async () => {
await queryClient.refetchQueries(['starred'], {
active: true,
});
await queryClient.refetchQueries(['album'], {
active: true,
});
await queryClient.refetchQueries(['albumList'], {
active: true,
});
await queryClient.refetchQueries(['playlist'], {
active: true,
});
};
const handleFavorite = async (ordered: boolean) => {
dispatch(setContextMenu({ show: false }));
const sortedEntries = [...multiSelect.selected].sort(
(a: any, b: any) => a.rowIndex - b.rowIndex
);
for (let i = 0; i < sortedEntries.length; i += 1) {
await star(sortedEntries[i].id, sortedEntries[i].type);
dispatch(setStar({ id: sortedEntries[i].id, type: 'star' }));
if (ordered) {
await sleep(350);
}
}
await refetchAfterFavorite();
};
const handleUnfavorite = async () => {
dispatch(setContextMenu({ show: false }));
const starredEntries = multiSelect.selected.filter(
(entry: any) => entry.starred
);
for (let i = 0; i < starredEntries.length; i += 1) {
await unstar(starredEntries[i].id, starredEntries[i].type);
dispatch(setStar({ id: starredEntries[i].id, type: 'unstar' }));
}
await refetchAfterFavorite();
};
return (
<>
{misc.contextMenu.show && misc.contextMenu.type === 'nowPlaying' && (
@ -118,14 +169,13 @@ export const GlobalContextMenu = () => {
<ContextMenu
xPos={misc.contextMenu.xPos}
yPos={misc.contextMenu.yPos}
width={140}
numOfButtons={5}
width={190}
numOfButtons={7}
numOfDividers={3}
hasTitle
>
<ContextMenuTitle>
<ContextMenuButton>
Selected: {multiSelect.selected.length}
</ContextMenuTitle>
</ContextMenuButton>
<ContextMenuDivider />
<ContextMenuButton>Add to queue</ContextMenuButton>
<ContextMenuButton>Remove from current</ContextMenuButton>
@ -134,16 +184,17 @@ export const GlobalContextMenu = () => {
<Whisper
ref={playlistTriggerRef}
enterable
placement="autoHorizontal"
placement="autoHorizontalStart"
trigger="none"
speaker={
<Popover>
<StyledInputPicker
data={playlists}
placement="autoVerticalStart"
virtualized
style={{ width: '150px' }}
labelKey="name"
valueKey="id"
width={200}
onChange={(e: any) => setSelectedPlaylistId(e)}
/>
<StyledButton
@ -173,8 +224,15 @@ export const GlobalContextMenu = () => {
</Whisper>
<ContextMenuDivider />
<ContextMenuButton>Add to favorites</ContextMenuButton>
<ContextMenuButton>Remove from favorites</ContextMenuButton>
<ContextMenuButton onClick={() => handleFavorite(false)}>
Add to favorites
</ContextMenuButton>
<ContextMenuButton onClick={() => handleFavorite(true)}>
Add to favorites (ordered)
</ContextMenuButton>
<ContextMenuButton onClick={handleUnfavorite}>
Remove from favorites
</ContextMenuButton>
</ContextMenu>
</>
)}

10
src/components/shared/styled.ts

@ -170,8 +170,8 @@ export const ContextMenuWindow = styled.div<{
left: ${(props) => `${props.xPos}px`};
height: ${(props) =>
`${
props.numOfButtons * 30.5 +
props.numOfDividers * 7 +
props.numOfButtons * 30 +
props.numOfDividers * 2 +
(props.hasTitle ? 16 : 0)
}px`};
width: ${(props) => `${props.width}px`};
@ -186,15 +186,15 @@ export const ContextMenuWindow = styled.div<{
export const StyledContextMenuButton = styled(Button)`
text-align: left;
margin: 0px !important;
`;
export const ContextMenuDivider = styled.hr`
margin: 5px 0 5px 0;
margin: 0px;
`;
export const ContextMenuTitle = styled.div`
color: ${(props) => props.theme.primary.text};
margin-left: 5px;
margin-top: 5px;
margin: 5px 0 5px 5px;
user-select: none;
`;

23
src/shared/utils.ts

@ -265,3 +265,26 @@ export const moveToIndex = (
// Finally, return the modified list
return newList;
};
export const getUpdatedEntryRowIndex = (
selectedEntries: any,
entryData: any
) => {
const updatedEntries = selectedEntries.map((entry: any) => {
const findIndex = entryData.findIndex(
(item: any) => item.uniqueId === entry.uniqueId
);
return { ...entry, rowIndex: findIndex };
});
// Sort the entries by their rowIndex so that we can re-add them in the proper order
const sortedEntries = updatedEntries.sort(
(a: any, b: any) => a.rowIndex - b.rowIndex
);
return sortedEntries;
};
export const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};

Loading…
Cancel
Save