Merge changes from @houkunlin

This commit is contained in:
鲁树人 2023-05-21 14:28:13 +01:00
commit a5d8e6bd11
3 changed files with 107 additions and 78 deletions

22
src/CurrentYear.tsx Normal file
View File

@ -0,0 +1,22 @@
import { useEffect, useState } from 'react';
// Update every half hour
const TIMER_UPDATE_INTERVAL = 30 * 60 * 1000;
const getCurrentYear = () => new Date().getFullYear();
export function CurrentYear() {
const [year, setYear] = useState(getCurrentYear);
useEffect(() => {
const updateTime = () => setYear(getCurrentYear);
updateTime();
const timer = setInterval(updateTime, TIMER_UPDATE_INTERVAL);
return () => {
clearInterval(timer);
};
}, []);
return <>{year}</>;
}

View File

@ -1,6 +1,7 @@
import { Center, Flex, Link, Text } from '@chakra-ui/react'; import { Center, Flex, Link, Text } from '@chakra-ui/react';
import { Suspense } from 'react'; import { Suspense } from 'react';
import { SDKVersion } from './SDKVersion'; import { SDKVersion } from './SDKVersion';
import { CurrentYear } from './CurrentYear';
export function Footer() { export function Footer() {
return ( return (
@ -25,7 +26,8 @@ export function Footer() {
{') - 移除已购音乐的加密保护。'} {') - 移除已购音乐的加密保护。'}
</Flex> </Flex>
<Text> <Text>
{'Copyright © 2019 - 2023 '} {'Copyright © 2019 - '}
<CurrentYear />{' '}
<Link href="https://git.unlock-music.dev/um" isExternal> <Link href="https://git.unlock-music.dev/um" isExternal>
UnlockMusic UnlockMusic
</Link> </Link>

View File

@ -4,19 +4,21 @@ import {
Card, Card,
CardBody, CardBody,
Center, Center,
Collapse,
Grid, Grid,
GridItem, GridItem,
Image, Image,
Link, Link,
Text, Text,
useDisclosure,
VStack, VStack,
Wrap, Wrap,
WrapItem, WrapItem,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { DecryptedAudioFile, ProcessState, deleteFile } from './fileListingSlice'; import { DecryptedAudioFile, deleteFile, ProcessState } from './fileListingSlice';
import { useRef } from 'react'; import { useCallback, useRef } from 'react';
import { useAppDispatch } from '~/hooks'; import { useAppDispatch } from '~/hooks';
import coverFallback from '~/assets/no-cover.svg'; import noCoverFallbackImageURL from '~/assets/no-cover.svg';
interface FileRowProps { interface FileRowProps {
id: string; id: string;
@ -24,6 +26,7 @@ interface FileRowProps {
} }
export function FileRow({ id, file }: FileRowProps) { export function FileRow({ id, file }: FileRowProps) {
const { isOpen, onClose } = useDisclosure({ defaultIsOpen: true });
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isDecrypted = file.state === ProcessState.COMPLETE; const isDecrypted = file.state === ProcessState.COMPLETE;
const metadata = file.metadata; const metadata = file.metadata;
@ -45,97 +48,99 @@ export function FileRow({ id, file }: FileRowProps) {
} }
}; };
const handleDeleteRow = () => { const handleDeleteRow = useCallback(() => {
dispatch(deleteFile({ id })); onClose();
}; setTimeout(() => {
dispatch(deleteFile({ id }));
}, 500);
}, [dispatch, id, onClose]);
return ( return (
<Card w="full" data-testid="file-row"> <Collapse in={isOpen} animateOpacity unmountOnExit startingHeight={0} style={{ width: '100%' }}>
<CardBody> <Card w="full" data-testid="file-row">
<Grid <CardBody>
templateAreas={{ <Grid
base: ` templateAreas={{
base: `
"cover" "cover"
"title" "title"
"meta" "meta"
"action" "action"
`, `,
md: ` md: `
"cover title title" "cover title title"
"cover meta action" "cover meta action"
`, `,
}} }}
gridTemplateRows={{ gridTemplateRows={{
base: 'repeat(auto-fill)', base: 'repeat(auto-fill)',
md: 'min-content 1fr', md: 'min-content 1fr',
}} }}
gridTemplateColumns={{ gridTemplateColumns={{
base: '1fr', base: '1fr',
md: '160px 1fr', md: '160px 1fr',
}} }}
gap="1" gap="1"
> >
<GridItem area="cover"> <GridItem area="cover">
<Center w="160px" h="160px" m="auto"> <Center w="160px" h="160px" m="auto">
{metadata && (
<Image <Image
objectFit="cover" objectFit="cover"
src={metadata.cover} src={metadata?.cover || noCoverFallbackImageURL}
alt={`"${metadata.album}" 的专辑封面`} alt={`${metadata?.album} 的专辑封面`}
fallbackSrc={coverFallback}
/> />
)} </Center>
</Center> </GridItem>
</GridItem> <GridItem area="title">
<GridItem area="title"> <Box w="full" as="h4" fontWeight="semibold" mt="1" textAlign={{ base: 'center', md: 'left' }}>
<Box w="full" as="h4" fontWeight="semibold" mt="1" textAlign={{ base: 'center', md: 'left' }}> <span data-testid="audio-meta-song-name">{metadata?.name ?? nameWithoutExt}</span>
<span data-testid="audio-meta-song-name">{metadata?.name ?? nameWithoutExt}</span>
</Box>
</GridItem>
<GridItem area="meta">
{isDecrypted && metadata && (
<Box>
<Text>
: <span data-testid="audio-meta-album-name">{metadata.album}</span>
</Text>
<Text>
: <span data-testid="audio-meta-song-artist">{metadata.artist}</span>
</Text>
<Text>
: <span data-testid="audio-meta-album-artist">{metadata.albumArtist}</span>
</Text>
</Box> </Box>
)} </GridItem>
</GridItem> <GridItem area="meta">
<GridItem area="action"> {isDecrypted && metadata && (
<VStack> <Box>
{file.decrypted && <audio controls autoPlay={false} src={file.decrypted} ref={audioPlayerRef} />} <Text>
: <span data-testid="audio-meta-album-name">{metadata.album}</span>
</Text>
<Text>
: <span data-testid="audio-meta-song-artist">{metadata.artist}</span>
</Text>
<Text>
: <span data-testid="audio-meta-album-artist">{metadata.albumArtist}</span>
</Text>
</Box>
)}
</GridItem>
<GridItem area="action">
<VStack>
{file.decrypted && <audio controls autoPlay={false} src={file.decrypted} ref={audioPlayerRef} />}
<Wrap> <Wrap>
{isDecrypted && ( {isDecrypted && (
<WrapItem>
<Button type="button" onClick={togglePlay}>
/
</Button>
</WrapItem>
)}
<WrapItem> <WrapItem>
<Button type="button" onClick={togglePlay}> {file.decrypted && (
/ <Link isExternal href={file.decrypted} download={decryptedName}>
<Button as="span"></Button>
</Link>
)}
</WrapItem>
<WrapItem>
<Button type="button" onClick={handleDeleteRow}>
</Button> </Button>
</WrapItem> </WrapItem>
)} </Wrap>
<WrapItem> </VStack>
{file.decrypted && ( </GridItem>
<Link isExternal href={file.decrypted} download={decryptedName}> </Grid>
<Button as="span"></Button> </CardBody>
</Link> </Card>
)} </Collapse>
</WrapItem>
<WrapItem>
<Button type="button" onClick={handleDeleteRow}>
</Button>
</WrapItem>
</Wrap>
</VStack>
</GridItem>
</Grid>
</CardBody>
</Card>
); );
} }