Skip to content

Commit

Permalink
Add ratings support (#21)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jeffvli committed Feb 5, 2023
1 parent f50ec5c commit 22fec8f
Show file tree
Hide file tree
Showing 27 changed files with 1,184 additions and 498 deletions.
46 changes: 23 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@
"@mantine/modals": "^6.0.0-alpha.2",
"@mantine/notifications": "^6.0.0-alpha.2",
"@mantine/utils": "^6.0.0-alpha.2",
"@tanstack/react-query": "^4.16.1",
"@tanstack/react-query-devtools": "^4.16.1",
"@tanstack/react-query": "^4.24.4",
"@tanstack/react-query-devtools": "^4.24.4",
"@tanstack/react-virtual": "^3.0.0-beta.39",
"dayjs": "^1.11.6",
"electron-debug": "^3.2.0",
Expand Down
10 changes: 4 additions & 6 deletions src/renderer/api/subsonic.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,9 @@ const updateRating = async (args: RatingArgs): Promise<RatingResponse> => {
const { server, query, signal } = args;
const defaultParams = getDefaultParams(server);

for (const id of query.id) {
const itemIds = query.item.map((item) => item.id);

for (const id of itemIds) {
const searchParams: SSRatingParams = {
id,
rating: query.rating,
Expand All @@ -334,13 +336,9 @@ const updateRating = async (args: RatingArgs): Promise<RatingResponse> => {
searchParams: parseSearchParams(searchParams),
signal,
});
// .json<SSRatingResponse>();
}

return {
id: query.id,
rating: query.rating,
};
return null;
};

const getTopSongList = async (args: TopSongListArgs): Promise<SSTopSongList> => {
Expand Down
17 changes: 15 additions & 2 deletions src/renderer/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ export enum LibraryItem {
SONG = 'song',
}

export type AnyLibraryItem = Album | AlbumArtist | Artist | Playlist | Song | QueueSong;

export type AnyLibraryItems =
| Album[]
| AlbumArtist[]
| Artist[]
| Playlist[]
| Song[]
| QueueSong[];

export enum SortOrder {
ASC = 'ASC',
DESC = 'DESC',
Expand Down Expand Up @@ -773,9 +783,12 @@ export type FavoriteArgs = { query: FavoriteQuery } & BaseEndpointArgs;
// Rating
export type RawRatingResponse = RatingResponse | undefined;

export type RatingResponse = { id: string[]; rating: number };
export type RatingResponse = null;

export type RatingQuery = { id: string[]; rating: number };
export type RatingQuery = {
item: AnyLibraryItems;
rating: number;
};

export type RatingArgs = { query: RatingQuery } & BaseEndpointArgs;

Expand Down
85 changes: 23 additions & 62 deletions src/renderer/components/card/card-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import type { MouseEvent } from 'react';
import React from 'react';
import type { UnstyledButtonProps } from '@mantine/core';
import { Group } from '@mantine/core';
import { openContextModal } from '@mantine/modals';
import { RiPlayFill, RiMore2Fill, RiHeartFill, RiHeartLine } from 'react-icons/ri';
import styled from 'styled-components';
import { _Button } from '/@/renderer/components/button';
import { DropdownMenu } from '/@/renderer/components/dropdown-menu';
import type { PlayQueueAddOptions } from '/@/renderer/types';
import { Play } from '/@/renderer/types';
import { useSettingsStore } from '/@/renderer/store/settings.store';
import { LibraryItem } from '/@/renderer/api/types';
import { useHandleGeneralContextMenu } from '/@/renderer/features/context-menu/hooks/use-handle-context-menu';
import {
ALBUM_CONTEXT_MENU_ITEMS,
ARTIST_CONTEXT_MENU_ITEMS,
} from '/@/renderer/features/context-menu/context-menu-items';

type PlayButtonType = UnstyledButtonProps & React.ComponentPropsWithoutRef<'button'>;

Expand Down Expand Up @@ -100,21 +103,6 @@ const FavoriteWrapper = styled.span<{ isFavorite: boolean }>`
}
`;

const PLAY_TYPES = [
{
label: 'Play',
play: Play.NOW,
},
{
label: 'Add to queue',
play: Play.LAST,
},
{
label: 'Add to queue next',
play: Play.NEXT,
},
];

export const CardControls = ({
itemData,
itemType,
Expand All @@ -138,18 +126,10 @@ export const CardControls = ({
});
};

const openAddToPlaylistModal = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
openContextModal({
innerProps: {
albumId: itemType === LibraryItem.ALBUM ? [itemData.id] : undefined,
artistId: itemType === LibraryItem.ALBUM_ARTIST ? [itemData.id] : undefined,
},
modal: 'addToPlaylist',
size: 'md',
title: 'Add to playlist',
});
};
const handleContextMenu = useHandleGeneralContextMenu(
itemType,
itemType === LibraryItem.ALBUM ? ALBUM_CONTEXT_MENU_ITEMS : ARTIST_CONTEXT_MENU_ITEMS,
);

return (
<GridCardControlsContainer>
Expand All @@ -175,40 +155,21 @@ export const CardControls = ({
)}
</FavoriteWrapper>
</SecondaryButton>
<DropdownMenu
withinPortal
position="bottom-start"
<SecondaryButton
p={5}
sx={{ svg: { fill: 'white !important' } }}
variant="subtle"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleContextMenu(e, [itemData]);
}}
>
<DropdownMenu.Target>
<SecondaryButton
p={5}
sx={{ svg: { fill: 'white !important' } }}
variant="subtle"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<RiMore2Fill
color="white"
size={20}
/>
</SecondaryButton>
</DropdownMenu.Target>
<DropdownMenu.Dropdown>
{PLAY_TYPES.filter((type) => type.play !== playButtonBehavior).map((type) => (
<DropdownMenu.Item
key={`playtype-${type.play}`}
onClick={(e: MouseEvent<HTMLButtonElement>) => handlePlay(e, type.play)}
>
{type.label}
</DropdownMenu.Item>
))}
<DropdownMenu.Item onClick={openAddToPlaylistModal}>
Add to playlist
</DropdownMenu.Item>
</DropdownMenu.Dropdown>
</DropdownMenu>
<RiMore2Fill
color="white"
size={20}
/>
</SecondaryButton>
</Group>
</BottomControls>
</GridCardControlsContainer>
Expand Down
Loading

1 comment on commit 22fec8f

@vercel
Copy link

@vercel vercel bot commented on 22fec8f Feb 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

feishin – ./

feishin-jeffvli.vercel.app
feishin-git-development-jeffvli.vercel.app
feishin.vercel.app

Please sign in to comment.