Add remaining lyric actions functionality

This commit is contained in:
jeffvli 2023-06-09 03:53:49 -07:00 committed by Jeff
parent aaa1b5f63a
commit e56350c1c2
5 changed files with 60 additions and 99 deletions

View file

@ -1,32 +0,0 @@
import { RiCloseFill } from 'react-icons/ri';
import styled from 'styled-components';
import { Button } from '/@/renderer/components';
const LyricClearButton = styled(Button)`
position: absolute;
right: 10px;
z-index: 999;
bottom: 6vh;
@media (max-width: 768px) {
bottom: 3vh;
}
`;
interface LyricSkipProps {
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
}
export const LyricSkip = ({ onClick }: LyricSkipProps) => {
return (
<LyricClearButton
leftIcon={<RiCloseFill />}
size="xl"
tooltip={{ label: 'Remove incorrect lyrics', position: 'top' }}
variant="default"
onClick={onClick}
>
Clear
</LyricClearButton>
);
};

View file

@ -2,14 +2,31 @@ import { RiAddFill, RiSubtractFill } from 'react-icons/ri';
import { LyricsOverride } from '/@/renderer/api/types'; import { LyricsOverride } from '/@/renderer/api/types';
import { Button, NumberInput } from '/@/renderer/components'; import { Button, NumberInput } from '/@/renderer/components';
import { openLyricSearchModal } from '/@/renderer/features/lyrics/components/lyrics-search-form'; import { openLyricSearchModal } from '/@/renderer/features/lyrics/components/lyrics-search-form';
import { useCurrentSong } from '/@/renderer/store'; import {
useCurrentSong,
useLyricsSettings,
useSettingsStore,
useSettingsStoreActions,
} from '/@/renderer/store';
interface LyricsActionsProps { interface LyricsActionsProps {
onSearchOverride?: (params: LyricsOverride) => void; onRemoveLyric: () => void;
onSearchOverride: (params: LyricsOverride) => void;
} }
export const LyricsActions = ({ onSearchOverride }: LyricsActionsProps) => { export const LyricsActions = ({ onRemoveLyric, onSearchOverride }: LyricsActionsProps) => {
const currentSong = useCurrentSong(); const currentSong = useCurrentSong();
const { setSettings } = useSettingsStoreActions();
const { delayMs } = useLyricsSettings();
const handleLyricOffset = (e: number) => {
setSettings({
lyrics: {
...useSettingsStore.getState().lyrics,
delayMs: e,
},
});
};
return ( return (
<> <>
@ -27,30 +44,30 @@ export const LyricsActions = ({ onSearchOverride }: LyricsActionsProps) => {
Search Search
</Button> </Button>
<Button <Button
tooltip={{ label: 'Decrease offset', openDelay: 500 }} aria-label="Decrease lyric offset"
variant="subtle" variant="subtle"
onClick={() => handleLyricOffset(delayMs - 50)}
> >
<RiSubtractFill /> <RiSubtractFill />
</Button> </Button>
<NumberInput <NumberInput
aria-label="Lyric offset"
styles={{ input: { textAlign: 'center' } }} styles={{ input: { textAlign: 'center' } }}
value={delayMs || 0}
width={55} width={55}
onChange={handleLyricOffset}
/> />
<Button <Button
tooltip={{ label: 'Increase offset', openDelay: 500 }} aria-label="Increase lyric offset"
variant="subtle" variant="subtle"
onClick={() => handleLyricOffset(delayMs + 50)}
> >
<RiAddFill /> <RiAddFill />
</Button> </Button>
<Button <Button
uppercase uppercase
variant="subtle" variant="subtle"
onClick={() => onClick={onRemoveLyric}
openLyricSearchModal({
artist: currentSong?.artistName,
name: currentSong?.name,
})
}
> >
Clear Clear
</Button> </Button>

View file

@ -9,7 +9,7 @@ import { SynchronizedLyrics } from './synchronized-lyrics';
import { ScrollArea, Spinner, TextTitle } from '/@/renderer/components'; import { ScrollArea, Spinner, TextTitle } from '/@/renderer/components';
import { ErrorFallback } from '/@/renderer/features/action-required'; import { ErrorFallback } from '/@/renderer/features/action-required';
import { UnsynchronizedLyrics } from '/@/renderer/features/lyrics/unsynchronized-lyrics'; import { UnsynchronizedLyrics } from '/@/renderer/features/lyrics/unsynchronized-lyrics';
import { getServerById, useCurrentSong } from '/@/renderer/store'; import { getServerById, useCurrentSong, usePlayerStore } from '/@/renderer/store';
import { import {
FullLyricsMetadata, FullLyricsMetadata,
LyricsOverride, LyricsOverride,
@ -20,7 +20,7 @@ import { LyricsActions } from '/@/renderer/features/lyrics/lyrics-actions';
const ActionsContainer = styled.div` const ActionsContainer = styled.div`
position: absolute; position: absolute;
bottom: 4rem; bottom: 0;
left: 0; left: 0;
z-index: 50; z-index: 50;
display: flex; display: flex;
@ -42,6 +42,7 @@ const ActionsContainer = styled.div`
const LyricsContainer = styled.div` const LyricsContainer = styled.div`
position: relative; position: relative;
display: flex;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -67,6 +68,7 @@ const ScrollContainer = styled(motion(ScrollArea))`
); );
&.mantine-ScrollArea-root { &.mantine-ScrollArea-root {
width: 100%;
height: 100%; height: 100%;
} }
@ -154,16 +156,16 @@ export const Lyrics = () => {
return ( return (
<ErrorBoundary FallbackComponent={ErrorFallback}> <ErrorBoundary FallbackComponent={ErrorFallback}>
{isLoadingLyrics ? ( <LyricsContainer>
<Spinner {isLoadingLyrics ? (
container <Spinner
size={25} container
/> size={25}
) : ( />
<AnimatePresence mode="sync"> ) : (
<LyricsContainer> <AnimatePresence mode="sync">
{!data?.lyrics || clear ? ( {!data?.lyrics || clear ? (
<Center> <Center w="100%">
<Group> <Group>
<RiInformationFill size="2rem" /> <RiInformationFill size="2rem" />
<TextTitle <TextTitle
@ -182,25 +184,21 @@ export const Lyrics = () => {
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
> >
{isSynchronizedLyrics ? ( {isSynchronizedLyrics ? (
<SynchronizedLyrics <SynchronizedLyrics {...lyricsMetadata} />
{...lyricsMetadata}
onRemoveLyric={() => setClear(true)}
/>
) : ( ) : (
<UnsynchronizedLyrics <UnsynchronizedLyrics {...(lyricsMetadata as UnsynchronizedLyricMetadata)} />
{...(lyricsMetadata as UnsynchronizedLyricMetadata)}
onRemoveLyric={() => setClear(true)}
/>
)} )}
</ScrollContainer> </ScrollContainer>
)} )}
</AnimatePresence>
<ActionsContainer> )}
<LyricsActions onSearchOverride={handleOnSearchOverride} /> <ActionsContainer>
</ActionsContainer> <LyricsActions
</LyricsContainer> onRemoveLyric={() => setClear(true)}
</AnimatePresence> onSearchOverride={handleOnSearchOverride}
)} />
</ActionsContainer>
</LyricsContainer>
</ErrorBoundary> </ErrorBoundary>
); );
}; };

View file

@ -12,7 +12,6 @@ import isElectron from 'is-electron';
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref'; import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
import { FullLyricsMetadata, SynchronizedLyricsArray } from '/@/renderer/api/types'; import { FullLyricsMetadata, SynchronizedLyricsArray } from '/@/renderer/api/types';
import styled from 'styled-components'; import styled from 'styled-components';
import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
@ -22,14 +21,12 @@ const SynchronizedLyricsContainer = styled.div`
interface SynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> { interface SynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
lyrics: SynchronizedLyricsArray; lyrics: SynchronizedLyricsArray;
onRemoveLyric: () => void;
} }
export const SynchronizedLyrics = ({ export const SynchronizedLyrics = ({
artist, artist,
lyrics, lyrics,
name, name,
onRemoveLyric,
remote, remote,
source, source,
}: SynchronizedLyricsProps) => { }: SynchronizedLyricsProps) => {
@ -146,16 +143,6 @@ export const SynchronizedLyrics = ({
} }
}, []); }, []);
const removeLyric = useCallback(() => {
onRemoveLyric();
if (lyricTimer.current) {
clearTimeout(lyricTimer.current);
}
timerEpoch.current += 1;
}, [onRemoveLyric]);
useEffect(() => { useEffect(() => {
// Copy the follow settings into a ref that can be accessed in the timeout // Copy the follow settings into a ref that can be accessed in the timeout
followRef.current = settings.follow; followRef.current = settings.follow;
@ -276,13 +263,10 @@ export const SynchronizedLyrics = ({
/> />
)} )}
{remote && ( {remote && (
<> <LyricLine
<LyricLine className="lyric-credit"
className="lyric-credit" text={`(Matched as ${artist} by ${name})`}
text={`(Matched as ${artist} by ${name})`} />
/>
<LyricSkip onClick={removeLyric} />
</>
)} )}
{lyrics.map(([, text], idx) => ( {lyrics.map(([, text], idx) => (
<LyricLine <LyricLine

View file

@ -2,11 +2,9 @@ import { useMemo } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line'; import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
import { FullLyricsMetadata } from '/@/renderer/api/types'; import { FullLyricsMetadata } from '/@/renderer/api/types';
import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
interface UnsynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> { interface UnsynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
lyrics: string; lyrics: string;
onRemoveLyric: () => void;
} }
const UnsynchronizedLyricsContainer = styled.div` const UnsynchronizedLyricsContainer = styled.div`
@ -17,7 +15,6 @@ export const UnsynchronizedLyrics = ({
artist, artist,
lyrics, lyrics,
name, name,
onRemoveLyric,
remote, remote,
source, source,
}: UnsynchronizedLyricsProps) => { }: UnsynchronizedLyricsProps) => {
@ -34,13 +31,10 @@ export const UnsynchronizedLyrics = ({
/> />
)} )}
{remote && ( {remote && (
<> <LyricLine
<LyricLine className="lyric-credit"
className="lyric-credit" text={`(Matched as ${artist} by ${name})`}
text={`(Matched as ${artist} by ${name})`} />
/>
<LyricSkip onClick={onRemoveLyric} />
</>
)} )}
{lines.map((text, idx) => ( {lines.map((text, idx) => (
<LyricLine <LyricLine