feat: ui improvement, working audio preview

This commit is contained in:
鲁树人 2023-05-14 23:47:56 +01:00
parent 8ab267019c
commit b99590908e
4 changed files with 119 additions and 63 deletions

View File

@ -4,4 +4,5 @@ export enum DECRYPTION_WORKER_ACTION_NAME {
export interface DecryptionResult { export interface DecryptionResult {
decrypted: string; // blob uri decrypted: string; // blob uri
ext: string;
} }

View File

@ -1,72 +1,17 @@
import { import { VStack } from '@chakra-ui/react';
Avatar,
Box,
Link,
Table,
TableContainer,
Tbody,
Td,
Text,
Th,
Thead,
Tr,
Wrap,
WrapItem,
} from '@chakra-ui/react';
import { ProcessState, selectFiles } from './fileListingSlice'; import { selectFiles } from './fileListingSlice';
import { useAppSelector } from '../../hooks'; import { useAppSelector } from '../../hooks';
import { FileRow } from './FileRow';
export function FileListing() { export function FileListing() {
const files = useAppSelector(selectFiles); const files = useAppSelector(selectFiles);
return ( return (
<TableContainer> <VStack>
<Table variant="striped">
<Thead>
<Tr>
<Th w="1%"></Th>
<Th></Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{Object.entries(files).map(([id, file]) => ( {Object.entries(files).map(([id, file]) => (
<Tr key={id}> <FileRow key={id} id={id} file={file} />
<Td>
{file.metadata.cover && <Avatar size="sm" name="专辑封面" src={file.metadata.cover} />}
{!file.metadata.cover && <Text></Text>}
</Td>
<Td>
<Box as="h4" fontWeight="semibold" mt="1">
{file.metadata.name || file.fileName}
</Box>
{file.state === ProcessState.COMPLETE && (
<>
<Text>: {file.metadata.album}</Text>
<Text>: {file.metadata.artist}</Text>
<Text>: {file.metadata.albumArtist}</Text>
</>
)}
</Td>
<Td>
<Wrap>
<WrapItem></WrapItem>
<WrapItem>
{/* TODO: Use correct file name */}
{file.decrypted && (
<Link isExternal href={file.decrypted} download="test.flac">
</Link>
)}
</WrapItem>
<WrapItem></WrapItem>
</Wrap>
</Td>
</Tr>
))} ))}
</Tbody> </VStack>
</Table>
</TableContainer>
); );
} }

View File

@ -0,0 +1,107 @@
import {
Avatar,
Box,
Button,
Card,
CardBody,
Center,
Grid,
GridItem,
Link,
Text,
VStack,
Wrap,
WrapItem,
} from '@chakra-ui/react';
import { DecryptedAudioFile, ProcessState } from './fileListingSlice';
import { useRef } from 'react';
interface FileRowProps {
id: string;
file: DecryptedAudioFile;
}
export function FileRow({ id, file }: FileRowProps) {
const isDecrypted = file.state === ProcessState.COMPLETE;
const decryptedName = file.fileName.replace(/\.[a-z\d]{3,6}$/, '') + '.' + file.ext;
const audioPlayerRef = useRef<HTMLAudioElement>(null);
const togglePlay = () => {
const player = audioPlayerRef.current;
if (!player) {
return;
}
if (player.paused) {
player.play();
} else {
player.pause();
}
};
return (
<Card w="full">
<CardBody>
<Grid
templateAreas={`
"cover title title"
"cover meta action"
`}
gridTemplateRows={'min-content 1fr'}
gridTemplateColumns={'160px 1fr'}
gap="1"
>
<GridItem area="cover">
<Center w="160px" h="160px">
{file.metadata.cover && <Avatar size="sm" name="专辑封面" src={file.metadata.cover} />}
{!file.metadata.cover && <Text></Text>}
</Center>
</GridItem>
<GridItem area="title">
<Box w="full" as="h4" fontWeight="semibold" mt="1">
{file.metadata.name || file.fileName}
</Box>
</GridItem>
<GridItem area="meta">
{isDecrypted && (
<Box>
<Text>: {file.metadata.album}</Text>
<Text>: {file.metadata.artist}</Text>
<Text>: {file.metadata.albumArtist}</Text>
</Box>
)}
</GridItem>
<GridItem area="action">
<VStack>
{file.decrypted && <audio controls autoPlay={false} src={file.decrypted} ref={audioPlayerRef} />}
<Wrap>
{isDecrypted && (
<WrapItem>
<Button type="button" onClick={togglePlay}>
/
</Button>
</WrapItem>
)}
<WrapItem>
{/* TODO: Use correct file name */}
{file.decrypted && (
<Link type="button" as={Button} isExternal href={file.decrypted} download={decryptedName}>
</Link>
)}
</WrapItem>
<WrapItem>
<Button type="button" onClick={() => alert('todo')}>
</Button>
</WrapItem>
</Wrap>
</VStack>
</GridItem>
</Grid>
</CardBody>
</Card>
);
}

View File

@ -27,6 +27,7 @@ export interface AudioMetadata {
export interface DecryptedAudioFile { export interface DecryptedAudioFile {
fileName: string; fileName: string;
raw: string; // blob uri raw: string; // blob uri
ext: string;
decrypted: string; // blob uri decrypted: string; // blob uri
state: ProcessState; state: ProcessState;
errorMessage: null | string; errorMessage: null | string;
@ -65,6 +66,7 @@ export const fileListingSlice = createSlice({
fileName: payload.fileName, fileName: payload.fileName,
raw: payload.blobURI, raw: payload.blobURI,
decrypted: '', decrypted: '',
ext: '',
state: ProcessState.UNTOUCHED, state: ProcessState.UNTOUCHED,
errorMessage: null, errorMessage: null,
metadata: { metadata: {
@ -91,6 +93,7 @@ export const fileListingSlice = createSlice({
file.state = ProcessState.COMPLETE; file.state = ProcessState.COMPLETE;
file.decrypted = action.payload.decrypted; file.decrypted = action.payload.decrypted;
file.ext = action.payload.ext;
// TODO: populate file metadata // TODO: populate file metadata
}); });