Add remaining lyric actions functionality
This commit is contained in:
parent
aaa1b5f63a
commit
e56350c1c2
5 changed files with 60 additions and 99 deletions
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Reference in a new issue