feat: basic ui layout

This commit is contained in:
鲁树人
2023-05-07 23:29:37 +01:00
parent 53682a1cdb
commit 38aa81b5bc
16 changed files with 375 additions and 104 deletions

View File

@@ -0,0 +1,56 @@
import { useEffect } from 'react';
import { Avatar, Box, Table, TableContainer, Tbody, Td, Text, Th, Thead, Tr, Wrap, WrapItem } from '@chakra-ui/react';
import { addNewFile, selectFiles } from './fileListingSlice';
import { useAppDispatch, useAppSelector } from '../../hooks';
export function FileListing() {
const dispatch = useAppDispatch();
const files = useAppSelector(selectFiles);
useEffect(() => {
// FIXME: Remove test data
if (files.length === 0) {
dispatch(addNewFile({ id: String(Date.now()), fileName: '测试文件名.mgg', blobURI: '' }));
}
}, []);
return (
<TableContainer>
<Table variant="striped">
<Thead>
<Tr>
<Th w="1%"></Th>
<Th></Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{files.map((file) => (
<Tr key={file.id}>
<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>
<Text>: {file.metadata.album}</Text>
<Text>: {file.metadata.artist}</Text>
<Text>: {file.metadata.albumArtist}</Text>
</Td>
<Td>
<Wrap>
<WrapItem></WrapItem>
<WrapItem></WrapItem>
<WrapItem></WrapItem>
</Wrap>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
);
}

View File

@@ -0,0 +1,79 @@
import { createSlice } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from '../../store';
export enum ProcessState {
UNTOUCHED = 'UNTOUCHED',
COMPLETE = 'COMPLETE',
ERROR = 'ERROR',
}
export enum ListingMode {
LIST = 'LIST',
CARD = 'CARD',
}
export interface AudioMetadata {
name: string;
artist: string;
album: string;
albumArtist: string;
cover: string; // blob uri
}
export interface DecryptedAudioFile {
id: string;
fileName: string;
raw: string; // blob uri
decrypted: string; // blob uri
state: ProcessState;
errorMessage: null | string;
metadata: AudioMetadata;
}
export interface FileListingState {
files: DecryptedAudioFile[];
displayMode: ListingMode;
}
const initialState: FileListingState = {
files: [],
displayMode: ListingMode.LIST,
};
export const fileListingSlice = createSlice({
name: 'fileListing',
initialState,
reducers: {
addNewFile: (state, { payload }: PayloadAction<{ id: string; fileName: string; blobURI: string }>) => {
state.files.push({
id: payload.id,
fileName: payload.fileName,
raw: payload.blobURI,
decrypted: '',
state: ProcessState.UNTOUCHED,
errorMessage: null,
metadata: {
name: '',
artist: '',
album: '',
albumArtist: '',
cover: '',
},
});
},
setDecryptedContent: (state, { payload }: PayloadAction<{ id: string; decryptedBlobURI: string }>) => {
const file = state.files.find((file) => file.id === payload.id);
if (file) {
file.decrypted = payload.decryptedBlobURI;
}
},
},
});
export const { addNewFile, setDecryptedContent } = fileListingSlice.actions;
export const selectFileCount = (state: RootState) => state.fileListing.files.length;
export const selectFiles = (state: RootState) => state.fileListing.files;
export const selectFileListingMode = (state: RootState) => state.fileListing.displayMode;
export default fileListingSlice.reducer;