mirror of
https://git.um-react.app/um/um-react.git
synced 2025-11-28 03:23:02 +00:00
refactor: move components to sub dir
This commit is contained in:
23
src/components/App.tsx
Normal file
23
src/components/App.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Box, Center, Container } from '@chakra-ui/react';
|
||||
import { SelectFile } from './SelectFile';
|
||||
|
||||
import { FileListing } from '~/features/file-listing/FileListing';
|
||||
import { Footer } from './Footer';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Box height="full" width="full" pt="4">
|
||||
<Container maxW="container.large">
|
||||
<Center>
|
||||
<SelectFile />
|
||||
</Center>
|
||||
<Box mt="8">
|
||||
<FileListing />
|
||||
</Box>
|
||||
<Footer />
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
23
src/components/AppRoot.tsx
Normal file
23
src/components/AppRoot.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { useEffect } from 'react';
|
||||
import App from './App';
|
||||
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { theme } from '~/theme';
|
||||
import { persistSettings } from '~/features/settings/persistSettings';
|
||||
import { setupStore } from '~/store';
|
||||
|
||||
// Private to this file only.
|
||||
const store = setupStore();
|
||||
|
||||
export function AppRoot() {
|
||||
useEffect(() => persistSettings(store), []);
|
||||
|
||||
return (
|
||||
<ChakraProvider theme={theme}>
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</ChakraProvider>
|
||||
);
|
||||
}
|
||||
22
src/components/CurrentYear.tsx
Normal file
22
src/components/CurrentYear.tsx
Normal 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}</>;
|
||||
}
|
||||
43
src/components/Footer.tsx
Normal file
43
src/components/Footer.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Center, Flex, Link, Text } from '@chakra-ui/react';
|
||||
import { Suspense } from 'react';
|
||||
import { SDKVersion } from './SDKVersion';
|
||||
import { CurrentYear } from './CurrentYear';
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<Center height="footer.container">
|
||||
<Center
|
||||
height="footer.content"
|
||||
fontSize="sm"
|
||||
textAlign="center"
|
||||
position="fixed"
|
||||
bottom="0"
|
||||
w="full"
|
||||
bg="gray.100"
|
||||
color="gray.800"
|
||||
left="0"
|
||||
flexDir="column"
|
||||
>
|
||||
<Flex as={Text}>
|
||||
{'音乐解锁 (__APP_VERSION_SHORT__'}
|
||||
<Suspense>
|
||||
<SDKVersion />
|
||||
</Suspense>
|
||||
{') - 移除已购音乐的加密保护。'}
|
||||
</Flex>
|
||||
<Text>
|
||||
{'Copyright © 2019 - '}
|
||||
<CurrentYear />{' '}
|
||||
<Link href="https://git.unlock-music.dev/um" isExternal>
|
||||
UnlockMusic 团队
|
||||
</Link>
|
||||
{' | 音乐解锁授权基于'}
|
||||
<Link href="https://git.unlock-music.dev/um/um-react/src/branch/main/LICENSE" isExternal>
|
||||
MIT许可协议
|
||||
</Link>
|
||||
。
|
||||
</Text>
|
||||
</Center>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
33
src/components/SDKVersion.tsx
Normal file
33
src/components/SDKVersion.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { InfoOutlineIcon } from '@chakra-ui/icons';
|
||||
import { Tooltip, VStack, Text, Flex } from '@chakra-ui/react';
|
||||
import { workerClientBus } from '~/decrypt-worker/client';
|
||||
import { DECRYPTION_WORKER_ACTION_NAME } from '~/decrypt-worker/constants';
|
||||
|
||||
import usePromise from 'react-promise-suspense';
|
||||
|
||||
const getSDKVersion = async (): Promise<string> => {
|
||||
return workerClientBus.request(DECRYPTION_WORKER_ACTION_NAME.VERSION, null);
|
||||
};
|
||||
|
||||
export function SDKVersion() {
|
||||
const sdkVersion = usePromise(getSDKVersion, []);
|
||||
|
||||
return (
|
||||
<Flex as="span" pl="1" alignItems="center" data-testid="sdk-version">
|
||||
<Tooltip
|
||||
hasArrow
|
||||
placement="top"
|
||||
label={
|
||||
<VStack>
|
||||
<Text>App: __APP_VERSION__</Text>
|
||||
<Text>SDK: {sdkVersion}</Text>
|
||||
</VStack>
|
||||
}
|
||||
bg="gray.300"
|
||||
color="black"
|
||||
>
|
||||
<InfoOutlineIcon />
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
77
src/components/SelectFile.tsx
Normal file
77
src/components/SelectFile.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { Box, Text } from '@chakra-ui/react';
|
||||
import { UnlockIcon } from '@chakra-ui/icons';
|
||||
|
||||
import { useAppDispatch } from '~/hooks';
|
||||
import { addNewFile, processFile } from '~/features/file-listing/fileListingSlice';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
export function SelectFile() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
multiple: true,
|
||||
onDropAccepted(files, _event) {
|
||||
console.debug(
|
||||
'react-dropzone/onDropAccepted(%o, %o)',
|
||||
files.length,
|
||||
files.map((x) => x.name)
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const blobURI = URL.createObjectURL(file);
|
||||
const fileName = file.name;
|
||||
const fileId = 'file://' + nanoid();
|
||||
|
||||
// FIXME: this should be a single action/thunk that first adds the item, then updates it.
|
||||
dispatch(
|
||||
addNewFile({
|
||||
id: fileId,
|
||||
blobURI,
|
||||
fileName,
|
||||
})
|
||||
);
|
||||
dispatch(processFile({ fileId }));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
{...getRootProps()}
|
||||
w="100%"
|
||||
maxW={480}
|
||||
borderWidth="1px"
|
||||
borderRadius="lg"
|
||||
transitionDuration="0.5s"
|
||||
p="6"
|
||||
cursor="pointer"
|
||||
display="flex"
|
||||
flexDir="column"
|
||||
alignItems="center"
|
||||
_hover={{
|
||||
borderColor: 'gray.400',
|
||||
bg: 'gray.50',
|
||||
}}
|
||||
{...(isDragActive && {
|
||||
bg: 'blue.50',
|
||||
borderColor: 'blue.700',
|
||||
})}
|
||||
>
|
||||
<input {...getInputProps()} />
|
||||
|
||||
<Box pb={3}>
|
||||
<UnlockIcon boxSize={8} />
|
||||
</Box>
|
||||
<Box textAlign="center">
|
||||
将文件拖到此处,或
|
||||
<Text as="span" color="teal.400">
|
||||
点我选择
|
||||
</Text>
|
||||
需要解密的文件
|
||||
<Text fontSize="sm" opacity="50%">
|
||||
仅在浏览器内对文件进行解锁,无需消耗流量
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user