[bugfix/feature]: Improve ratings (#332)
* [bugfix/feature]: Improve ratings Fix: add preventDefault/stopPropagation to prevent scrolling to top in queue Feat: instead of double click for clear, click on same value
This commit is contained in:
parent
e5564c2ac2
commit
4ec981df83
7 changed files with 27 additions and 79 deletions
|
@ -1,5 +1,5 @@
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { Group, Image, Rating, Text, Title } from '@mantine/core';
|
import { Group, Image, Text, Title } from '@mantine/core';
|
||||||
import { useInfo, useSend, useShowImage } from '/@/remote/store';
|
import { useInfo, useSend, useShowImage } from '/@/remote/store';
|
||||||
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
|
import { RemoteButton } from '/@/remote/components/buttons/remote-button';
|
||||||
import formatDuration from 'format-duration';
|
import formatDuration from 'format-duration';
|
||||||
|
@ -18,6 +18,7 @@ import {
|
||||||
import { PlayerRepeat, PlayerStatus } from '/@/renderer/types';
|
import { PlayerRepeat, PlayerStatus } from '/@/renderer/types';
|
||||||
import { WrapperSlider } from '/@/remote/components/wrapped-slider';
|
import { WrapperSlider } from '/@/remote/components/wrapped-slider';
|
||||||
import { Tooltip } from '/@/renderer/components/tooltip';
|
import { Tooltip } from '/@/renderer/components/tooltip';
|
||||||
|
import { Rating } from '/@/renderer/components';
|
||||||
|
|
||||||
export const RemoteContainer = () => {
|
export const RemoteContainer = () => {
|
||||||
const { repeat, shuffle, song, status, volume } = useInfo();
|
const { repeat, shuffle, song, status, volume } = useInfo();
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||||
import { MouseEvent } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { Rating as MantineRating, RatingProps as MantineRatingProps } from '@mantine/core';
|
import { Rating as MantineRating, RatingProps } from '@mantine/core';
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Tooltip } from '/@/renderer/components/tooltip';
|
|
||||||
|
|
||||||
interface RatingProps extends Omit<MantineRatingProps, 'onClick'> {
|
|
||||||
onClick: (e: MouseEvent<HTMLDivElement>, value: number | undefined) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledRating = styled(MantineRating)`
|
const StyledRating = styled(MantineRating)`
|
||||||
& .mantine-Rating-symbolBody {
|
& .mantine-Rating-symbolBody {
|
||||||
|
@ -16,18 +12,28 @@ const StyledRating = styled(MantineRating)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Rating = ({ onClick, ...props }: RatingProps) => {
|
export const Rating = ({ onChange, ...props }: RatingProps) => {
|
||||||
// const debouncedOnClick = debounce(onClick, 100);
|
const valueChange = useCallback(
|
||||||
|
(rating: number) => {
|
||||||
|
if (onChange) {
|
||||||
|
if (rating === props.value) {
|
||||||
|
onChange(0);
|
||||||
|
} else {
|
||||||
|
onChange(rating);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onChange, props.value],
|
||||||
|
);
|
||||||
|
|
||||||
|
const debouncedOnChange = debounce(valueChange, 100);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<StyledRating
|
||||||
label="Double click to clear"
|
{...props}
|
||||||
openDelay={1000}
|
onChange={(e) => {
|
||||||
>
|
debouncedOnChange(e);
|
||||||
<StyledRating
|
}}
|
||||||
{...props}
|
/>
|
||||||
onDoubleClick={(e) => onClick(e, props.value)}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/* eslint-disable import/no-cycle */
|
/* eslint-disable import/no-cycle */
|
||||||
import { MouseEvent } from 'react';
|
|
||||||
import type { ICellRendererParams } from '@ag-grid-community/core';
|
import type { ICellRendererParams } from '@ag-grid-community/core';
|
||||||
import { Rating } from '/@/renderer/components/rating';
|
import { Rating } from '/@/renderer/components/rating';
|
||||||
import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell';
|
import { CellContainer } from '/@/renderer/components/virtual-table/cells/generic-cell';
|
||||||
|
@ -9,8 +8,6 @@ export const RatingCell = ({ value, node }: ICellRendererParams) => {
|
||||||
const updateRatingMutation = useSetRating({});
|
const updateRatingMutation = useSetRating({});
|
||||||
|
|
||||||
const handleUpdateRating = (rating: number) => {
|
const handleUpdateRating = (rating: number) => {
|
||||||
if (!value) return;
|
|
||||||
|
|
||||||
updateRatingMutation.mutate(
|
updateRatingMutation.mutate(
|
||||||
{
|
{
|
||||||
query: {
|
query: {
|
||||||
|
@ -27,32 +24,12 @@ export const RatingCell = ({ value, node }: ICellRendererParams) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClearRating = (e: MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
updateRatingMutation.mutate(
|
|
||||||
{
|
|
||||||
query: {
|
|
||||||
item: [value],
|
|
||||||
rating: 0,
|
|
||||||
},
|
|
||||||
serverId: value?.serverId,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
|
||||||
node.setData({ ...node.data, userRating: 0 });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CellContainer $position="center">
|
<CellContainer $position="center">
|
||||||
<Rating
|
<Rating
|
||||||
size="xs"
|
size="xs"
|
||||||
value={value?.userRating}
|
value={value?.userRating}
|
||||||
onChange={handleUpdateRating}
|
onChange={handleUpdateRating}
|
||||||
onClick={handleClearRating}
|
|
||||||
/>
|
/>
|
||||||
</CellContainer>
|
</CellContainer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -55,18 +55,6 @@ export const AlbumDetailHeader = forwardRef(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClearRating = () => {
|
|
||||||
if (!detailQuery?.data || !detailQuery?.data.userRating) return;
|
|
||||||
|
|
||||||
updateRatingMutation.mutate({
|
|
||||||
query: {
|
|
||||||
item: [detailQuery.data],
|
|
||||||
rating: 0,
|
|
||||||
},
|
|
||||||
serverId: detailQuery.data.serverId,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const showRating = detailQuery?.data?.serverType === ServerType.NAVIDROME;
|
const showRating = detailQuery?.data?.serverType === ServerType.NAVIDROME;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -96,7 +84,6 @@ export const AlbumDetailHeader = forwardRef(
|
||||||
}
|
}
|
||||||
value={detailQuery?.data?.userRating || 0}
|
value={detailQuery?.data?.userRating || 0}
|
||||||
onChange={handleUpdateRating}
|
onChange={handleUpdateRating}
|
||||||
onClick={handleClearRating}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { forwardRef, Fragment, Ref, MouseEvent } from 'react';
|
import { forwardRef, Fragment, Ref } from 'react';
|
||||||
import { Group, Rating, Stack } from '@mantine/core';
|
import { Group, Rating, Stack } from '@mantine/core';
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
import { LibraryItem, ServerType } from '/@/renderer/api/types';
|
import { LibraryItem, ServerType } from '/@/renderer/api/types';
|
||||||
|
@ -55,21 +55,6 @@ export const AlbumArtistDetailHeader = forwardRef(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClearRating = (_e: MouseEvent<HTMLDivElement>, rating?: number) => {
|
|
||||||
if (!detailQuery?.data || !detailQuery?.data.userRating) return;
|
|
||||||
|
|
||||||
const isSameRatingAsPrevious = rating === detailQuery.data.userRating;
|
|
||||||
if (!isSameRatingAsPrevious) return;
|
|
||||||
|
|
||||||
updateRatingMutation.mutate({
|
|
||||||
query: {
|
|
||||||
item: [detailQuery.data],
|
|
||||||
rating: 0,
|
|
||||||
},
|
|
||||||
serverId: detailQuery.data.serverId,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const showRating = detailQuery?.data?.serverType === ServerType.NAVIDROME;
|
const showRating = detailQuery?.data?.serverType === ServerType.NAVIDROME;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -99,7 +84,6 @@ export const AlbumArtistDetailHeader = forwardRef(
|
||||||
}
|
}
|
||||||
value={detailQuery?.data?.userRating || 0}
|
value={detailQuery?.data?.userRating || 0}
|
||||||
onChange={handleUpdateRating}
|
onChange={handleUpdateRating}
|
||||||
onClick={handleClearRating}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -710,7 +710,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
<Rating
|
<Rating
|
||||||
readOnly
|
readOnly
|
||||||
value={0}
|
value={0}
|
||||||
onClick={() => {}}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
onClick: () => handleUpdateRating(0),
|
onClick: () => handleUpdateRating(0),
|
||||||
|
@ -721,7 +720,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
<Rating
|
<Rating
|
||||||
readOnly
|
readOnly
|
||||||
value={1}
|
value={1}
|
||||||
onClick={() => {}}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
onClick: () => handleUpdateRating(1),
|
onClick: () => handleUpdateRating(1),
|
||||||
|
@ -732,7 +730,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
<Rating
|
<Rating
|
||||||
readOnly
|
readOnly
|
||||||
value={2}
|
value={2}
|
||||||
onClick={() => {}}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
onClick: () => handleUpdateRating(2),
|
onClick: () => handleUpdateRating(2),
|
||||||
|
@ -743,7 +740,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
<Rating
|
<Rating
|
||||||
readOnly
|
readOnly
|
||||||
value={3}
|
value={3}
|
||||||
onClick={() => {}}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
onClick: () => handleUpdateRating(3),
|
onClick: () => handleUpdateRating(3),
|
||||||
|
@ -754,7 +750,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
<Rating
|
<Rating
|
||||||
readOnly
|
readOnly
|
||||||
value={4}
|
value={4}
|
||||||
onClick={() => {}}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
onClick: () => handleUpdateRating(4),
|
onClick: () => handleUpdateRating(4),
|
||||||
|
@ -765,7 +760,6 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
|
||||||
<Rating
|
<Rating
|
||||||
readOnly
|
readOnly
|
||||||
value={5}
|
value={5}
|
||||||
onClick={() => {}}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
onClick: () => handleUpdateRating(5),
|
onClick: () => handleUpdateRating(5),
|
||||||
|
|
|
@ -212,7 +212,6 @@ export const RightControls = () => {
|
||||||
size="sm"
|
size="sm"
|
||||||
value={currentSong?.userRating || 0}
|
value={currentSong?.userRating || 0}
|
||||||
onChange={handleUpdateRating}
|
onChange={handleUpdateRating}
|
||||||
onClick={handleClearRating}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
Reference in a new issue