remove nested rating menu

This commit is contained in:
jeffvli 2025-09-04 20:37:55 -07:00
parent dd60634a40
commit cbbf2db087

View file

@ -9,7 +9,15 @@ import {
import { closeAllModals, openContextModal, openModal } from '@mantine/modals'; import { closeAllModals, openContextModal, openModal } from '@mantine/modals';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { AnimatePresence } from 'motion/react'; import { AnimatePresence } from 'motion/react';
import { createContext, Fragment, ReactNode, useCallback, useMemo, useState } from 'react'; import {
createContext,
Fragment,
ReactNode,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
@ -88,25 +96,17 @@ export interface ContextMenuProviderProps {
children: ReactNode; children: ReactNode;
} }
function RatingIcon({ rating }: { rating: number }) {
return (
<Rating
readOnly
style={{
pointerEvents: 'none',
size: 'var(--theme-font-size-md)',
}}
value={rating}
/>
);
}
export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => { export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const disabledItems = useSettingsStore((state) => state.general.disabledContextMenu); const disabledItems = useSettingsStore((state) => state.general.disabledContextMenu);
const { t } = useTranslation(); const { t } = useTranslation();
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
const [ratingsRef, setRatingsRef] = useState<HTMLDivElement | null>(null); const [ratingsRef, setRatingsRef] = useState<HTMLDivElement | null>(null);
const [rating, setRating] = useState<number>(0);
useEffect(() => {
setRating(0);
}, [opened]);
const clickOutsideRef = useClickOutside( const clickOutsideRef = useClickOutside(
() => setOpened(false), () => setOpened(false),
@ -559,7 +559,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const updateRatingMutation = useSetRating({}); const updateRatingMutation = useSetRating({});
const handleUpdateRating = useCallback( const handleUpdateRating = useCallback(
(rating: number) => { (newRating: number) => {
if (!ctx.dataNodes && !ctx.data) return; if (!ctx.dataNodes && !ctx.data) return;
let uniqueServerIds: string[] = []; let uniqueServerIds: string[] = [];
@ -581,6 +581,8 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
}, [] as string[]); }, [] as string[]);
} }
const ratingToSet = newRating === rating ? 0 : newRating;
for (const serverId of uniqueServerIds) { for (const serverId of uniqueServerIds) {
if (ctx.dataNodes) { if (ctx.dataNodes) {
items = ctx.dataNodes items = ctx.dataNodes
@ -594,7 +596,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
{ {
query: { query: {
item: items, item: items,
rating, rating: ratingToSet,
}, },
serverId, serverId,
}, },
@ -602,7 +604,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
onSuccess: () => { onSuccess: () => {
if (ctx.dataNodes) { if (ctx.dataNodes) {
for (const node of ctx.dataNodes) { for (const node of ctx.dataNodes) {
node.setData({ ...node.data, userRating: rating }); node.setData({ ...node.data, userRating: ratingToSet });
} }
} }
}, },
@ -610,7 +612,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
); );
} }
}, },
[ctx.data, ctx.dataNodes, updateRatingMutation], [ctx.data, ctx.dataNodes, updateRatingMutation, rating],
); );
const playbackType = usePlaybackType(); const playbackType = usePlaybackType();
@ -836,43 +838,22 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
onClick: handleRemoveSelected, onClick: handleRemoveSelected,
}, },
setRating: { setRating: {
children: [
{
id: 'zeroStar',
label: <RatingIcon rating={0} />,
onClick: () => handleUpdateRating(0),
},
{
id: 'oneStar',
label: <RatingIcon rating={1} />,
onClick: () => handleUpdateRating(1),
},
{
id: 'twoStar',
label: <RatingIcon rating={2} />,
onClick: () => handleUpdateRating(2),
},
{
id: 'threeStar',
label: <RatingIcon rating={3} />,
onClick: () => handleUpdateRating(3),
},
{
id: 'fourStar',
label: <RatingIcon rating={4} />,
onClick: () => handleUpdateRating(4),
},
{
id: 'fiveStar',
label: <RatingIcon rating={5} />,
onClick: () => handleUpdateRating(5),
},
],
id: 'setRating', id: 'setRating',
label: t('action.setRating', { postProcess: 'sentenceCase' }), label: t('action.setRating', { postProcess: 'sentenceCase' }),
leftIcon: <Icon icon="star" />, leftIcon: <Icon icon="star" />,
onClick: () => {}, onClick: () => {},
rightIcon: <Icon icon="arrowRightS" />, rightIcon: (
<Group ref={setRatingsRef as any}>
<Rating
value={rating}
onChange={(e) => {
handleUpdateRating(e);
setRating(e);
}}
size="xs"
/>
</Group>
),
}, },
shareItem: { shareItem: {
disabled: !hasFeature(server, ServerFeature.SHARING_ALBUM_SONG), disabled: !hasFeature(server, ServerFeature.SHARING_ALBUM_SONG),
@ -957,7 +938,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
</ContextMenuButton> </ContextMenuButton>
</HoverCard.Target> </HoverCard.Target>
<HoverCard.Dropdown> <HoverCard.Dropdown>
<Stack gap={0} ref={setRatingsRef}> <Stack gap={0}>
{contextMenuItems[ {contextMenuItems[
item.id item.id
].children?.map((child) => ( ].children?.map((child) => (