2023-05-14 22:47:56 +00:00
|
|
|
import {
|
|
|
|
Box,
|
|
|
|
Button,
|
|
|
|
Card,
|
|
|
|
CardBody,
|
|
|
|
Center,
|
2023-05-16 08:24:00 +00:00
|
|
|
Collapse,
|
2023-05-14 22:47:56 +00:00
|
|
|
Grid,
|
|
|
|
GridItem,
|
2023-05-16 08:24:00 +00:00
|
|
|
Image,
|
2023-05-14 22:47:56 +00:00
|
|
|
Link,
|
|
|
|
Text,
|
2023-05-16 08:24:00 +00:00
|
|
|
useDisclosure,
|
2023-05-14 22:47:56 +00:00
|
|
|
VStack,
|
|
|
|
Wrap,
|
|
|
|
WrapItem,
|
|
|
|
} from '@chakra-ui/react';
|
2023-05-16 08:24:00 +00:00
|
|
|
import { DecryptedAudioFile, deleteFile, ProcessState } from './fileListingSlice';
|
|
|
|
import { useCallback, useRef } from 'react';
|
2023-05-15 20:05:03 +00:00
|
|
|
import { useAppDispatch } from '~/hooks';
|
2023-05-17 00:46:20 +00:00
|
|
|
import coverFallback from '~/assets/no-cover.svg';
|
2023-05-14 22:47:56 +00:00
|
|
|
|
|
|
|
interface FileRowProps {
|
|
|
|
id: string;
|
|
|
|
file: DecryptedAudioFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function FileRow({ id, file }: FileRowProps) {
|
2023-05-17 01:10:36 +00:00
|
|
|
const { isOpen, onClose } = useDisclosure({ defaultIsOpen: true });
|
2023-05-15 20:05:03 +00:00
|
|
|
const dispatch = useAppDispatch();
|
2023-05-14 22:47:56 +00:00
|
|
|
const isDecrypted = file.state === ProcessState.COMPLETE;
|
2023-05-17 00:46:20 +00:00
|
|
|
const metadata = file.metadata;
|
2023-05-14 22:47:56 +00:00
|
|
|
|
2023-05-14 23:41:39 +00:00
|
|
|
const nameWithoutExt = file.fileName.replace(/\.[a-z\d]{3,6}$/, '');
|
|
|
|
const decryptedName = nameWithoutExt + '.' + file.ext;
|
2023-05-14 22:47:56 +00:00
|
|
|
|
|
|
|
const audioPlayerRef = useRef<HTMLAudioElement>(null);
|
2023-05-16 08:24:00 +00:00
|
|
|
const togglePlay = useCallback(() => {
|
2023-05-14 22:47:56 +00:00
|
|
|
const player = audioPlayerRef.current;
|
|
|
|
if (!player) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player.paused) {
|
|
|
|
player.play();
|
|
|
|
} else {
|
|
|
|
player.pause();
|
|
|
|
}
|
2023-05-16 08:24:00 +00:00
|
|
|
}, []);
|
2023-05-14 22:47:56 +00:00
|
|
|
|
2023-05-16 08:24:00 +00:00
|
|
|
const handleDeleteRow = useCallback(() => {
|
2023-05-17 01:10:36 +00:00
|
|
|
onClose();
|
2023-05-16 08:24:00 +00:00
|
|
|
setTimeout(() => {
|
|
|
|
dispatch(deleteFile({ id }));
|
|
|
|
}, 500);
|
2023-05-17 01:10:36 +00:00
|
|
|
}, [dispatch, id, onClose]);
|
2023-05-15 20:05:03 +00:00
|
|
|
|
2023-05-14 22:47:56 +00:00
|
|
|
return (
|
2023-05-16 08:24:00 +00:00
|
|
|
<Collapse in={isOpen} animateOpacity unmountOnExit startingHeight={0} style={{ width: '100%' }}>
|
2023-05-17 01:18:41 +00:00
|
|
|
<Card w="full" data-testid="file-row">
|
2023-05-16 08:24:00 +00:00
|
|
|
<CardBody>
|
|
|
|
<Grid
|
|
|
|
templateAreas={{
|
|
|
|
base: `
|
2023-05-14 23:41:39 +00:00
|
|
|
"cover"
|
|
|
|
"title"
|
|
|
|
"meta"
|
|
|
|
"action"
|
|
|
|
`,
|
2023-05-16 08:24:00 +00:00
|
|
|
md: `
|
2023-05-14 23:41:39 +00:00
|
|
|
"cover title title"
|
|
|
|
"cover meta action"
|
|
|
|
`,
|
2023-05-16 08:24:00 +00:00
|
|
|
}}
|
|
|
|
gridTemplateRows={{
|
|
|
|
base: 'repeat(auto-fill)',
|
|
|
|
md: 'min-content 1fr',
|
|
|
|
}}
|
|
|
|
gridTemplateColumns={{
|
|
|
|
base: '1fr',
|
|
|
|
md: '160px 1fr',
|
|
|
|
}}
|
|
|
|
gap="1"
|
|
|
|
>
|
|
|
|
<GridItem area="cover">
|
|
|
|
<Center w="160px" h="160px" m="auto">
|
2023-05-17 01:18:41 +00:00
|
|
|
{metadata && (
|
|
|
|
<Image
|
|
|
|
objectFit="cover"
|
|
|
|
src={metadata.cover}
|
|
|
|
alt={`"${metadata.album}" 的专辑封面`}
|
|
|
|
fallbackSrc={coverFallback}
|
|
|
|
/>
|
|
|
|
)}
|
2023-05-16 08:24:00 +00:00
|
|
|
</Center>
|
|
|
|
</GridItem>
|
|
|
|
<GridItem area="title">
|
|
|
|
<Box w="full" as="h4" fontWeight="semibold" mt="1" textAlign={{ base: 'center', md: 'left' }}>
|
2023-05-17 01:18:41 +00:00
|
|
|
<span data-testid="audio-meta-song-name">{metadata?.name ?? nameWithoutExt}</span>
|
2023-05-14 22:47:56 +00:00
|
|
|
</Box>
|
2023-05-16 08:24:00 +00:00
|
|
|
</GridItem>
|
|
|
|
<GridItem area="meta">
|
2023-05-17 01:18:41 +00:00
|
|
|
{isDecrypted && metadata && (
|
2023-05-16 08:24:00 +00:00
|
|
|
<Box>
|
2023-05-17 01:18:41 +00:00
|
|
|
<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>
|
2023-05-16 08:24:00 +00:00
|
|
|
</Box>
|
|
|
|
)}
|
|
|
|
</GridItem>
|
|
|
|
<GridItem area="action">
|
|
|
|
<VStack>
|
|
|
|
{file.decrypted && <audio controls autoPlay={false} src={file.decrypted} ref={audioPlayerRef} />}
|
2023-05-14 22:47:56 +00:00
|
|
|
|
2023-05-16 08:24:00 +00:00
|
|
|
<Wrap>
|
|
|
|
{isDecrypted && (
|
|
|
|
<WrapItem>
|
|
|
|
<Button type="button" onClick={togglePlay}>
|
|
|
|
播放/暂停
|
|
|
|
</Button>
|
|
|
|
</WrapItem>
|
|
|
|
)}
|
|
|
|
<WrapItem>
|
|
|
|
{file.decrypted && (
|
|
|
|
<Link isExternal href={file.decrypted} download={decryptedName}>
|
|
|
|
<Button as="span">下载</Button>
|
|
|
|
</Link>
|
|
|
|
)}
|
|
|
|
</WrapItem>
|
2023-05-14 22:47:56 +00:00
|
|
|
<WrapItem>
|
2023-05-16 08:24:00 +00:00
|
|
|
<Button type="button" onClick={handleDeleteRow}>
|
|
|
|
删除
|
2023-05-14 22:47:56 +00:00
|
|
|
</Button>
|
|
|
|
</WrapItem>
|
2023-05-16 08:24:00 +00:00
|
|
|
</Wrap>
|
|
|
|
</VStack>
|
|
|
|
</GridItem>
|
|
|
|
</Grid>
|
|
|
|
</CardBody>
|
|
|
|
</Card>
|
|
|
|
</Collapse>
|
2023-05-14 22:47:56 +00:00
|
|
|
);
|
|
|
|
}
|