Include lyric simplification, restore clear button

- merges lyric simplification
- restores metadata/clear
This commit is contained in:
Kendall Garner 2023-06-05 09:00:12 -07:00 committed by Jeff
parent f92cd89c46
commit 8835fc640a
6 changed files with 63 additions and 31 deletions

View file

@ -1036,6 +1036,18 @@ export type InternetProviderLyricResponse = {
source: string; source: string;
}; };
export type SynchronizedLyricMetadata = {
lyrics: SynchronizedLyricsArray;
remote: boolean;
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
export type UnsyhchronizedLyricMetadata = {
lyrics: string;
remote: boolean;
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
export type FullLyricsMetadata = SynchronizedLyricMetadata | UnsyhchronizedLyricMetadata;
export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>; export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
export const instanceOfCancellationError = (error: any) => { export const instanceOfCancellationError = (error: any) => {

View file

@ -6,10 +6,10 @@ const LyricClearButton = styled(Button)`
position: absolute; position: absolute;
right: 10px; right: 10px;
z-index: 999; z-index: 999;
top: 7vh; bottom: 6vh;
@media (max-width: 768px) { @media (max-width: 768px) {
top: 5vh; bottom: 3vh;
} }
`; `;
@ -22,7 +22,7 @@ export const LyricSkip = ({ onClick }: LyricSkipProps) => {
<LyricClearButton <LyricClearButton
leftIcon={<RiCloseFill />} leftIcon={<RiCloseFill />}
size="xl" size="xl"
tooltip={{ label: 'Remove incorrect lyrics', position: 'bottom' }} tooltip={{ label: 'Remove incorrect lyrics', position: 'top' }}
variant="default" variant="default"
onClick={onClick} onClick={onClick}
> >

View file

@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { Center, Group } from '@mantine/core'; import { Center, Group } from '@mantine/core';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
@ -9,6 +10,7 @@ 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 } from '/@/renderer/store';
import { FullLyricsMetadata, SynchronizedLyricMetadata } from '/@/renderer/api/types';
const LyricsScrollContainer = styled(motion(ScrollArea))` const LyricsScrollContainer = styled(motion(ScrollArea))`
z-index: 1; z-index: 1;
@ -36,10 +38,18 @@ const LyricsScrollContainer = styled(motion(ScrollArea))`
} }
`; `;
function isSynchronized(data: FullLyricsMetadata): data is SynchronizedLyricMetadata {
// Type magic. The only difference between Synchronized and Unsynchhronized is
// the datatype of lyrics. This makes Typescript happier later...
return Array.isArray(data.lyrics);
}
export const Lyrics = () => { export const Lyrics = () => {
const currentSong = useCurrentSong(); const currentSong = useCurrentSong();
const currentServer = getServerById(currentSong?.serverId); const currentServer = getServerById(currentSong?.serverId);
const [clear, setClear] = useState(false);
const { data, isLoading } = useSongLyrics( const { data, isLoading } = useSongLyrics(
{ {
query: { songId: currentSong?.id || '' }, query: { songId: currentSong?.id || '' },
@ -48,6 +58,11 @@ export const Lyrics = () => {
currentSong, currentSong,
); );
useEffect(() => {
// We want to reset the clear flag whenever a song changes
setClear(false);
}, [currentSong]);
return ( return (
<ErrorBoundary FallbackComponent={ErrorFallback}> <ErrorBoundary FallbackComponent={ErrorFallback}>
{isLoading ? ( {isLoading ? (
@ -55,7 +70,7 @@ export const Lyrics = () => {
container container
size={25} size={25}
/> />
) : !data?.lyrics ? ( ) : !data?.lyrics || clear ? (
<Center> <Center>
<Group> <Group>
<RiInformationFill size="2rem" /> <RiInformationFill size="2rem" />
@ -74,19 +89,15 @@ export const Lyrics = () => {
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
> >
{Array.isArray(data.lyrics) ? ( {isSynchronized(data) ? (
<SynchronizedLyrics <SynchronizedLyrics
lyrics={data.lyrics} {...data}
override={null} onRemoveLyric={() => setClear(true)}
source={data.source}
onRemoveLyric={() => {}}
/> />
) : ( ) : (
<UnsynchronizedLyrics <UnsynchronizedLyrics
lyrics={data.lyrics} {...data}
override={null} onRemoveLyric={() => setClear(true)}
source={data.source}
onRemoveLyric={() => {}}
/> />
)} )}
</LyricsScrollContainer> </LyricsScrollContainer>

View file

@ -1,9 +1,10 @@
import { useQuery } from '@tanstack/react-query'; import { UseQueryResult, useQuery } from '@tanstack/react-query';
import { import {
LyricsQuery, LyricsQuery,
QueueSong, QueueSong,
SynchronizedLyricsArray, SynchronizedLyricsArray,
InternetProviderLyricResponse, InternetProviderLyricResponse,
FullLyricsMetadata,
} from '/@/renderer/api/types'; } from '/@/renderer/api/types';
import { QueryHookArgs } from '/@/renderer/lib/react-query'; import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById, useLyricsSettings } from '/@/renderer/store'; import { getServerById, useLyricsSettings } from '/@/renderer/store';
@ -37,7 +38,9 @@ const formatLyrics = (lyrics: string) => {
return formattedLyrics; return formattedLyrics;
}; };
export const useServerLyrics = (args: QueryHookArgs<LyricsQuery>) => { export const useServerLyrics = (
args: QueryHookArgs<LyricsQuery>,
): UseQueryResult<string | null> => {
const { query, serverId } = args; const { query, serverId } = args;
const server = getServerById(serverId); const server = getServerById(serverId);
@ -55,7 +58,10 @@ export const useServerLyrics = (args: QueryHookArgs<LyricsQuery>) => {
}); });
}; };
export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong | undefined) => { export const useSongLyrics = (
args: QueryHookArgs<LyricsQuery>,
song: QueueSong | undefined,
): UseQueryResult<FullLyricsMetadata> => {
const { query } = args; const { query } = args;
const { fetch } = useLyricsSettings(); const { fetch } = useLyricsSettings();
const server = getServerById(song?.serverId); const server = getServerById(song?.serverId);
@ -73,6 +79,7 @@ export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong
artist: song.artists?.[0]?.name, artist: song.artists?.[0]?.name,
lyrics: formatLyrics(song.lyrics), lyrics: formatLyrics(song.lyrics),
name: song.name, name: song.name,
remote: false,
source: server?.name ?? 'music server', source: server?.name ?? 'music server',
}; };
} }
@ -88,6 +95,7 @@ export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong
artist: song.artists?.[0]?.name, artist: song.artists?.[0]?.name,
lyrics: jfLyrics, lyrics: jfLyrics,
name: song.name, name: song.name,
remote: false,
source: server?.name ?? 'music server', source: server?.name ?? 'music server',
}; };
} }
@ -101,6 +109,7 @@ export const useSongLyrics = (args: QueryHookArgs<LyricsQuery>, song: QueueSong
return { return {
...remoteLyricsResult, ...remoteLyricsResult,
lyrics: formatLyrics(remoteLyricsResult.lyrics), lyrics: formatLyrics(remoteLyricsResult.lyrics),
remote: true,
}; };
} }
} }

View file

@ -10,7 +10,7 @@ import { PlaybackType, PlayerStatus } from '/@/renderer/types';
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line'; import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { PlayersRef } from '/@/renderer/features/player/ref/players-ref'; import { PlayersRef } from '/@/renderer/features/player/ref/players-ref';
import { LyricOverride, 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'; import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
@ -20,17 +20,17 @@ const SynchronizedLyricsContainer = styled.div`
padding: 5rem 0; padding: 5rem 0;
`; `;
interface SynchronizedLyricsProps { interface SynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
lyrics: SynchronizedLyricsArray; lyrics: SynchronizedLyricsArray;
onRemoveLyric: () => void; onRemoveLyric: () => void;
override: LyricOverride | null;
source: string | null;
} }
export const SynchronizedLyrics = ({ export const SynchronizedLyrics = ({
artist,
lyrics, lyrics,
name,
onRemoveLyric, onRemoveLyric,
override, remote,
source, source,
}: SynchronizedLyricsProps) => { }: SynchronizedLyricsProps) => {
const playersRef = PlayersRef; const playersRef = PlayersRef;
@ -275,11 +275,11 @@ export const SynchronizedLyrics = ({
text={`Lyrics provided by ${source}`} text={`Lyrics provided by ${source}`}
/> />
)} )}
{override && ( {remote && (
<> <>
<LyricLine <LyricLine
className="lyric-credit" className="lyric-credit"
text={`(Matched as ${override.name} by ${override.artist})`} text={`(Matched as ${artist} by ${name})`}
/> />
<LyricSkip onClick={removeLyric} /> <LyricSkip onClick={removeLyric} />
</> </>

View file

@ -1,14 +1,12 @@
import { useMemo } from 'react'; 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 { LyricOverride } from '/@/renderer/api/types'; import { FullLyricsMetadata } from '/@/renderer/api/types';
import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip'; import { LyricSkip } from '/@/renderer/features/lyrics/lyric-skip';
interface UnsynchronizedLyricsProps { interface UnsynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
lyrics: string; lyrics: string;
onRemoveLyric: () => void; onRemoveLyric: () => void;
override: LyricOverride | null;
source: string | null;
} }
const UnsynchronizedLyricsContainer = styled.div` const UnsynchronizedLyricsContainer = styled.div`
@ -16,9 +14,11 @@ const UnsynchronizedLyricsContainer = styled.div`
`; `;
export const UnsynchronizedLyrics = ({ export const UnsynchronizedLyrics = ({
onRemoveLyric, artist,
lyrics, lyrics,
override, name,
onRemoveLyric,
remote,
source, source,
}: UnsynchronizedLyricsProps) => { }: UnsynchronizedLyricsProps) => {
const lines = useMemo(() => { const lines = useMemo(() => {
@ -33,11 +33,11 @@ export const UnsynchronizedLyrics = ({
text={`Lyrics provided by ${source}`} text={`Lyrics provided by ${source}`}
/> />
)} )}
{override && ( {remote && (
<> <>
<LyricLine <LyricLine
className="lyric-credit" className="lyric-credit"
text={`(Matched as ${override.name} by ${override.artist})`} text={`(Matched as ${artist} by ${name})`}
/> />
<LyricSkip onClick={onRemoveLyric} /> <LyricSkip onClick={onRemoveLyric} />
</> </>