Add ratings support (#21)

* Update rating types for multiserver support

* Add rating mutation

* Add rating support to table views

* Add rating support on playerbar

* Add hovercard component

* Handle rating from context menu

- Improve context menu components
- Allow left / right icons
- Allow nested menus

* Add selected item count

* Fix context menu auto direction

* Add transition and move portal for context menu

* Re-use context menu for all item dropdowns

* Add ratings to detail pages / double click to clear

* Bump react-query package
This commit is contained in:
Jeff 2023-02-05 05:19:01 -08:00 committed by GitHub
parent f50ec5cf31
commit 22fec8f9d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 1189 additions and 503 deletions

View file

@ -1,16 +1,16 @@
import React from 'react';
import { Center, Group } from '@mantine/core';
import { openContextModal } from '@mantine/modals';
import { motion, AnimatePresence, LayoutGroup } from 'framer-motion';
import { RiArrowUpSLine, RiDiscLine, RiMore2Fill } from 'react-icons/ri';
import { generatePath, Link } from 'react-router-dom';
import styled from 'styled-components';
import { Button, DropdownMenu, Text } from '/@/renderer/components';
import { Button, Text } from '/@/renderer/components';
import { AppRoute } from '/@/renderer/router/routes';
import { useAppStoreActions, useAppStore, useCurrentSong } from '/@/renderer/store';
import { fadeIn } from '/@/renderer/styles';
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
import { LibraryItem } from '/@/renderer/api/types';
import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { useHandleGeneralContextMenu } from '/@/renderer/features/context-menu/hooks/use-handle-context-menu';
const LeftControlsContainer = styled.div`
display: flex;
@ -82,41 +82,10 @@ export const LeftControls = () => {
const isSongDefined = Boolean(currentSong?.id);
const openAddToPlaylistModal = () => {
openContextModal({
innerProps: {
songId: [currentSong?.id],
},
modal: 'addToPlaylist',
size: 'md',
title: 'Add to playlist',
});
};
const addToFavoritesMutation = useCreateFavorite();
const removeFromFavoritesMutation = useDeleteFavorite();
const handleAddToFavorites = () => {
if (!isSongDefined || !currentSong) return;
addToFavoritesMutation.mutate({
query: {
id: [currentSong.id],
type: LibraryItem.SONG,
},
});
};
const handleRemoveFromFavorites = () => {
if (!isSongDefined || !currentSong) return;
removeFromFavoritesMutation.mutate({
query: {
id: [currentSong.id],
type: LibraryItem.SONG,
},
});
};
const handleGeneralContextMenu = useHandleGeneralContextMenu(
LibraryItem.SONG,
SONG_CONTEXT_MENU_ITEMS,
);
return (
<LeftControlsContainer>
@ -196,28 +165,13 @@ export const LeftControls = () => {
{title || '—'}
</Text>
{isSongDefined && (
<DropdownMenu>
<DropdownMenu.Target>
<Button
compact
variant="subtle"
>
<RiMore2Fill size="1.2rem" />
</Button>
</DropdownMenu.Target>
<DropdownMenu.Dropdown>
<DropdownMenu.Item onClick={openAddToPlaylistModal}>
Add to playlist
</DropdownMenu.Item>
<DropdownMenu.Divider />
<DropdownMenu.Item onClick={handleAddToFavorites}>
Add to favorites
</DropdownMenu.Item>
<DropdownMenu.Item onClick={handleRemoveFromFavorites}>
Remove from favorites
</DropdownMenu.Item>
</DropdownMenu.Dropdown>
</DropdownMenu>
<Button
compact
variant="subtle"
onClick={(e) => handleGeneralContextMenu(e, [currentSong!])}
>
<RiMore2Fill size="1.2rem" />
</Button>
)}
</Group>
</LineItem>

View file

@ -1,3 +1,4 @@
import { MouseEvent } from 'react';
import { Flex, Group } from '@mantine/core';
import { HiOutlineQueueList } from 'react-icons/hi2';
import {
@ -19,7 +20,7 @@ import {
import { useRightControls } from '../hooks/use-right-controls';
import { PlayerButton } from './player-button';
import { LibraryItem, ServerType } from '/@/renderer/api/types';
import { useCreateFavorite, useDeleteFavorite } from '/@/renderer/features/shared';
import { useCreateFavorite, useDeleteFavorite, useUpdateRating } from '/@/renderer/features/shared';
import { Rating } from '/@/renderer/components';
import { PlayerbarSlider } from '/@/renderer/features/player/components/playerbar-slider';
@ -32,6 +33,7 @@ export const RightControls = () => {
const { rightExpanded: isQueueExpanded } = useSidebarStore();
const { handleVolumeSlider, handleVolumeWheel, handleMute } = useRightControls();
const updateRatingMutation = useUpdateRating();
const addToFavoritesMutation = useCreateFavorite();
const removeFromFavoritesMutation = useDeleteFavorite();
const setFavorite = useSetQueueFavorite();
@ -54,6 +56,30 @@ export const RightControls = () => {
);
};
const handleUpdateRating = (rating: number) => {
if (!currentSong) return;
updateRatingMutation.mutate({
_serverId: currentSong?.serverId,
query: {
item: [currentSong],
rating,
},
});
};
const handleClearRating = (_e: MouseEvent<HTMLDivElement>, rating?: number) => {
if (!currentSong || !rating) return;
updateRatingMutation.mutate({
_serverId: currentSong?.serverId,
query: {
item: [currentSong],
rating: 0,
},
});
};
const handleRemoveFromFavorites = () => {
if (!currentSong) return;
@ -96,9 +122,10 @@ export const RightControls = () => {
<Group h="calc(100% / 3)">
{showRating && (
<Rating
readOnly
size="sm"
value={currentSong?.userRating ?? 0}
value={currentSong?.userRating || 0}
onChange={handleUpdateRating}
onClick={handleClearRating}
/>
)}
</Group>