From 765b36cc989324a562661e30d19ba45adfb198fc Mon Sep 17 00:00:00 2001 From: marcobaobao Date: Fri, 23 Jun 2023 11:41:55 +0200 Subject: [PATCH] code refactoring --- frontend/src/Layout.tsx | 234 ++++++++++--------- frontend/src/assets/i18n.yaml | 16 ++ frontend/src/components/DownloadDialog.tsx | 12 +- frontend/src/components/Splash.tsx | 6 +- frontend/src/providers/i18nProvider.tsx | 30 +++ frontend/src/providers/rpcClientProvider.tsx | 31 +++ frontend/src/views/Archive.tsx | 7 +- frontend/src/views/Home.tsx | 14 +- frontend/src/views/Settings.tsx | 12 +- 9 files changed, 229 insertions(+), 133 deletions(-) create mode 100644 frontend/src/providers/i18nProvider.tsx create mode 100644 frontend/src/providers/rpcClientProvider.tsx diff --git a/frontend/src/Layout.tsx b/frontend/src/Layout.tsx index 29fde64..21bd067 100644 --- a/frontend/src/Layout.tsx +++ b/frontend/src/Layout.tsx @@ -32,8 +32,10 @@ import AppBar from './components/AppBar' import Drawer from './components/Drawer' import Logout from './components/Logout' -import { formatGiB } from './utils' import ThemeToggler from './components/ThemeToggler' +import I18nProvider from './providers/i18nProvider' +import RPCCLientProvider from './providers/rpcClientProvider' +import { formatGiB } from './utils' export default function Layout() { const [open, setOpen] = useState(false) @@ -59,123 +61,131 @@ export default function Layout() { return ( - - - - - - - - - yt-dlp WebUI - - { - status.freeSpace ? + + + + + + + + + + + yt-dlp WebUI + + { + status.freeSpace ? +
+ + +  {formatGiB(status.freeSpace)}  + +
+ : null + }
- -  {formatGiB(status.freeSpace)}  + + +  {status.connected ? settings.serverAddr : 'not connected'} +
- : null - } -
- -  {status.connected ? settings.serverAddr : 'not connected'} -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } \ No newline at end of file diff --git a/frontend/src/assets/i18n.yaml b/frontend/src/assets/i18n.yaml index d2e7cf9..0505946 100644 --- a/frontend/src/assets/i18n.yaml +++ b/frontend/src/assets/i18n.yaml @@ -29,6 +29,8 @@ languages: customArgs: Enable custom yt-dlp args (great power = great responsabilities) customArgsInput: Custom yt-dlp arguments rpcConnErr: Error while conencting to RPC server + splashText: No active downloads + archiveTitle: Archive italian: urlInput: URL di YouTube o di qualsiasi altro servizio supportato statusTitle: Stato @@ -57,6 +59,8 @@ languages: customArgs: Enable custom yt-dlp args (great power = great responsabilities) customArgsInput: Custom yt-dlp arguments rpcConnErr: Error nella connessione al server RPC + splashText: Nessun download attivo + archiveTitle: Archivio chinese: urlInput: YouTube 或其他受支持服务的视频网址 statusTitle: 状态 @@ -85,6 +89,8 @@ languages: customArgs: 启用自定义 yt-dlp 参数(能力越大 = 责任越大) customArgsInput: 自定义 yt-dlp 参数 rpcConnErr: Error while conencting to RPC server + splashText: No active downloads + archiveTitle: Archive spanish: urlInput: URL de YouTube u otro servicio compatible statusTitle: Estado @@ -113,6 +119,8 @@ languages: customArgs: Habilitar los argumentos yt-dlp personalizados (un gran poder conlleva una gran responsabilidad) customArgsInput: Argumentos yt-dlp personalizados rpcConnErr: Error al conectarse al servidor RPC + splashText: No active downloads + archiveTitle: Archive russian: urlInput: YouTube or other supported service video url statusTitle: Status @@ -141,6 +149,8 @@ languages: customArgs: Enable custom yt-dlp args (great power = great responsabilities) customArgsInput: Custom yt-dlp arguments rpcConnErr: Error while conencting to RPC server + splashText: No active downloads + archiveTitle: Archive korean: urlInput: YouTube나 다른 지원되는 사이트의 URL statusTitle: 상태 @@ -169,6 +179,8 @@ languages: customArgs: Enable custom yt-dlp args (great power = great responsabilities) customArgsInput: Custom yt-dlp arguments rpcConnErr: Error while conencting to RPC server + splashText: No active downloads + archiveTitle: Archive japanese: urlInput: YouTubeまたはサポート済み動画のURL statusTitle: 状態 @@ -198,6 +210,8 @@ languages: customArgs: yt-dlpのオプションの有効化 (最適設定にする場合) customArgsInput: yt-dlpのオプション rpcConnErr: Error while conencting to RPC server + splashText: No active downloads + archiveTitle: Archive catalan: urlInput: URL de YouTube o d'un altre servei compatible statusTitle: Estat @@ -226,3 +240,5 @@ languages: customArgs: Habilitar els arguments yt-dlp personalitzats (un gran poder comporta una gran responsabilitat) customArgsInput: Arguments yt-dlp personalitzats rpcConnErr: Error en connectar-se al servidor RPC + splashText: No active downloads + archiveTitle: Archive diff --git a/frontend/src/components/DownloadDialog.tsx b/frontend/src/components/DownloadDialog.tsx index c489104..7280b66 100644 --- a/frontend/src/components/DownloadDialog.tsx +++ b/frontend/src/components/DownloadDialog.tsx @@ -21,12 +21,12 @@ import Toolbar from '@mui/material/Toolbar' import { TransitionProps } from '@mui/material/transitions' import Typography from '@mui/material/Typography' import { Buffer } from 'buffer' -import { forwardRef, useEffect, useMemo, useRef, useState } from 'react' +import { forwardRef, useContext, useEffect, useMemo, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import FormatsGrid from '../components/FormatsGrid' import { CliArguments } from '../lib/argsParser' -import I18nBuilder from '../lib/intl' -import { RPCClient } from '../lib/rpcClient' +import { I18nContext } from '../providers/i18nProvider' +import { RPCClientContext } from '../providers/rpcClientProvider' import { RootState } from '../stores/store' import type { DLMetadata } from '../types' import { isValidURL, toFormatArgs } from '../utils' @@ -67,10 +67,12 @@ export default function DownloadDialog({ open, onClose }: Props) { const [workingUrl, setWorkingUrl] = useState('') // memos - const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language]) - const client = useMemo(() => new RPCClient(), [settings.serverAddr, settings.serverPort]) const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs]) + // context + const { i18n } = useContext(I18nContext) + const { client } = useContext(RPCClientContext) + // refs const urlInputRef = useRef(null) const customFilenameInputRef = useRef(null) diff --git a/frontend/src/components/Splash.tsx b/frontend/src/components/Splash.tsx index 62cee4a..c6cc1d3 100644 --- a/frontend/src/components/Splash.tsx +++ b/frontend/src/components/Splash.tsx @@ -1,5 +1,7 @@ import CloudDownloadIcon from '@mui/icons-material/CloudDownload' import { Container, SvgIcon, Typography, styled } from '@mui/material' +import { useContext } from 'react' +import { I18nContext } from '../providers/i18nProvider' const FlexContainer = styled(Container)({ display: 'flex', @@ -19,6 +21,8 @@ const Title = styled(Typography)({ }) export default function Splash() { + const { i18n } = useContext(I18nContext) + return ( @@ -27,7 +31,7 @@ export default function Splash() { </SvgIcon> - No active downloads + {i18n.t('splashText')} ) diff --git a/frontend/src/providers/i18nProvider.tsx b/frontend/src/providers/i18nProvider.tsx new file mode 100644 index 0000000..000c0a2 --- /dev/null +++ b/frontend/src/providers/i18nProvider.tsx @@ -0,0 +1,30 @@ +import { createContext, useMemo } from 'react' +import { useSelector } from 'react-redux' +import I18nBuilder from '../lib/intl' +import { RootState, store } from '../stores/store' + +type Props = { + children: React.ReactNode +} + +interface Context { + i18n: I18nBuilder +} + +export const I18nContext = createContext({ + i18n: new I18nBuilder(store.getState().settings.language) +}) + +export default function I18nProvider({ children }: Props) { + const settings = useSelector((state: RootState) => state.settings) + + const i18n = useMemo(() => new I18nBuilder( + settings.language + ), [settings.language]) + + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/frontend/src/providers/rpcClientProvider.tsx b/frontend/src/providers/rpcClientProvider.tsx new file mode 100644 index 0000000..d3d516d --- /dev/null +++ b/frontend/src/providers/rpcClientProvider.tsx @@ -0,0 +1,31 @@ +import { createContext, useMemo } from 'react' +import { useSelector } from 'react-redux' +import { RPCClient } from '../lib/rpcClient' +import { RootState } from '../stores/store' + +type Props = { + children: React.ReactNode +} + +interface Context { + client: RPCClient +} + +export const RPCClientContext = createContext({ + client: new RPCClient() +}) + +export default function RPCCLientProvider({ children }: Props) { + const settings = useSelector((state: RootState) => state.settings) + + const client = useMemo(() => new RPCClient(), [ + settings.serverAddr, + settings.serverPort, + ]) + + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/frontend/src/views/Archive.tsx b/frontend/src/views/Archive.tsx index d52981e..7f62870 100644 --- a/frontend/src/views/Archive.tsx +++ b/frontend/src/views/Archive.tsx @@ -27,7 +27,7 @@ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile' import VideoFileIcon from '@mui/icons-material/VideoFile' import { Buffer } from 'buffer' -import { useEffect, useMemo, useState, useTransition } from 'react' +import { useContext, useEffect, useMemo, useState, useTransition } from 'react' import { useSelector } from 'react-redux' import { BehaviorSubject, Subject, combineLatestWith, map, share } from 'rxjs' import { useObservable } from '../hooks/observable' @@ -36,11 +36,14 @@ import { DeleteRequest, DirectoryEntry } from '../types' import { roundMiB } from '../utils' import { useNavigate } from 'react-router-dom' import { ffetch } from '../lib/httpClient' +import { I18nContext } from '../providers/i18nProvider' export default function Downloaded() { const settings = useSelector((state: RootState) => state.settings) const navigate = useNavigate() + const { i18n } = useContext(I18nContext) + const [openDialog, setOpenDialog] = useState(false) const serverAddr = @@ -160,7 +163,7 @@ export default function Downloaded() { flexDirection: 'column', }}> - {'Archive'} + {i18n.t('archiveTitle')} {selectable.length === 0 && 'No files found'} diff --git a/frontend/src/views/Home.tsx b/frontend/src/views/Home.tsx index 598b3ee..e2f5884 100644 --- a/frontend/src/views/Home.tsx +++ b/frontend/src/views/Home.tsx @@ -12,7 +12,7 @@ import { SpeedDialIcon, styled } from '@mui/material' -import { useEffect, useMemo, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import DownloadDialog from '../components/DownloadDialog' import { DownloadsCardView } from '../components/DownloadsCardView' @@ -20,8 +20,9 @@ import { DownloadsListView } from '../components/DownloadsListView' import Splash from '../components/Splash' import { toggleListView } from '../features/settings/settingsSlice' import { connected, setFreeSpace } from '../features/status/statusSlice' -import I18nBuilder from '../lib/intl' -import { RPCClient, socket$ } from '../lib/rpcClient' +import { socket$ } from '../lib/rpcClient' +import { I18nContext } from '../providers/i18nProvider' +import { RPCClientContext } from '../providers/rpcClientProvider' import { RootState } from '../stores/store' import type { RPCResponse, RPCResult } from '../types' import { dateTimeComparatorFunc } from '../utils' @@ -41,9 +42,9 @@ export default function Home() { const [openDialog, setOpenDialog] = useState(false) const [socketHasError, setSocketHasError] = useState(false) - // memos - const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language]) - const client = useMemo(() => new RPCClient(), [settings.serverAddr, settings.serverPort]) + // context + const { i18n } = useContext(I18nContext) + const { client } = useContext(RPCClientContext) /* -------------------- Effects -------------------- */ @@ -120,7 +121,6 @@ export default function Home() { client.killAll() } - /* -------------------- styled components -------------------- */ const Input = styled('input')({ diff --git a/frontend/src/views/Settings.tsx b/frontend/src/views/Settings.tsx index 71577d2..262e2c7 100644 --- a/frontend/src/views/Settings.tsx +++ b/frontend/src/views/Settings.tsx @@ -17,7 +17,7 @@ import { TextField, Typography } from '@mui/material' -import { useEffect, useMemo, useState } from 'react' +import { useContext, useEffect, useMemo, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { Subject, @@ -26,9 +26,6 @@ import { map, takeWhile } from 'rxjs' -import { CliArguments } from '../lib/argsParser' -import I18nBuilder from '../lib/intl' -import { RPCClient } from '../lib/rpcClient' import { LanguageUnion, ThemeUnion, @@ -43,6 +40,9 @@ import { setTheme } from '../features/settings/settingsSlice' import { updated } from '../features/status/statusSlice' +import { CliArguments } from '../lib/argsParser' +import { I18nContext } from '../providers/i18nProvider' +import { RPCClientContext } from '../providers/rpcClientProvider' import { RootState } from '../stores/store' import { validateDomain, validateIP } from '../utils' @@ -54,9 +54,9 @@ export default function Settings() { const [invalidIP, setInvalidIP] = useState(false); - const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language]) + const { i18n } = useContext(I18nContext) + const { client } = useContext(RPCClientContext) - const client = useMemo(() => new RPCClient(), []) const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), []) const serverAddr$ = useMemo(() => new Subject(), [])