mirror of
https://git.um-react.app/um/um-react.git
synced 2025-11-28 03:23:02 +00:00
feat: basic ui layout
This commit is contained in:
56
src/features/file-listing/FileListing.tsx
Normal file
56
src/features/file-listing/FileListing.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
79
src/features/file-listing/fileListingSlice.ts
Normal file
79
src/features/file-listing/fileListingSlice.ts
Normal 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;
|
||||
Reference in New Issue
Block a user