Fix issue #14 and bump react to v18

This commit is contained in:
2022-10-14 13:44:19 +02:00
parent 8ee9710fe2
commit de32fbd5ce
11 changed files with 1454 additions and 624 deletions

View File

@@ -1,5 +1,10 @@
import ReactDOM from 'react-dom'
import React from 'react'
import ReactDOM from 'react-dom/client'
import { App } from './src/App'
const root = document.getElementById('root')
ReactDOM.render(<App />, root)
const root = ReactDOM.createRoot(document.getElementById('root')!)
root.render(
<React.StrictMode>
<App></App>
</React.StrictMode>
)

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from "react"
import { useEffect, useMemo, useState } from "react"
import { ThemeProvider } from "@emotion/react";
import {
Box,
@@ -36,7 +36,9 @@ import ArchivedDownloads from "./Archived";
const drawerWidth: number = 240;
const socket = io(`http://${localStorage.getItem('server-addr') || window.location.hostname}:${localStorage.getItem('server-port') || window.location.port}`)
const socket = io(
`http://${localStorage.getItem('server-addr') || window.location.hostname}:${localStorage.getItem('server-port') || window.location.port}`
)
interface AppBarProps extends MuiAppBarProps {
open?: boolean;

View File

@@ -18,12 +18,14 @@ import {
Typography
} from "@mui/material";
import { Buffer } from 'buffer';
import React, { Fragment, useEffect, useState } from "react";
import { Fragment, useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Socket } from "socket.io-client";
import { CliArguments } from "./classes";
import { StackableResult } from "./components/StackableResult";
import { serverStates } from "./events";
import { connected, downloading, finished } from "./features/status/statusSlice";
import { I18nBuilder } from "./i18n";
import { IDLMetadata, IDLMetadataAndPID, IMessage } from "./interfaces";
import { RootState } from "./stores/store";
import { isValidURL, toFormatArgs, updateInStateMap } from "./utils";
@@ -55,6 +57,10 @@ export default function Home({ socket }: Props) {
const [showBackdrop, setShowBackdrop] = useState(false);
const [showToast, setShowToast] = useState(true);
// memos
const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language])
const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs])
/* -------------------- Effects -------------------- */
/* WebSocket connect event handler*/
useEffect(() => {
@@ -134,7 +140,7 @@ export default function Home({ socket }: Props) {
socket.emit('send-url', {
url: immediate || url || workingUrl,
path: availableDownloadPaths[downloadPath],
params: settings.cliArgs.toString() + toFormatArgs(codes),
params: cliArgs.toString() + toFormatArgs(codes),
})
setUrl('')
setWorkingUrl('')
@@ -231,7 +237,7 @@ export default function Home({ socket }: Props) {
<TextField
fullWidth
id="urlInput"
label={settings.i18n.t('urlInput')}
label={i18n.t('urlInput')}
variant="outlined"
onChange={handleUrlChange}
disabled={!status.connected || (settings.formatSelection && downloadFormats != null)}
@@ -253,8 +259,8 @@ export default function Home({ socket }: Props) {
<FormControl fullWidth>
<Select
defaultValue={0}
value={availableDownloadPaths[downloadPath]}
onChange={(e) => setDownloadPath(e.target.value)}
value={downloadPath}
onChange={(e) => setDownloadPath(Number(e.target.value))}
>
{availableDownloadPaths.map((val: string, idx: number) => (
@@ -271,7 +277,7 @@ export default function Home({ socket }: Props) {
disabled={url === ''}
onClick={() => settings.formatSelection ? sendUrlFormatSelection() : sendUrl()}
>
{settings.i18n.t('startButton')}
{i18n.t('startButton')}
</Button>
</Grid>
<Grid item>
@@ -279,7 +285,7 @@ export default function Home({ socket }: Props) {
variant="contained"
onClick={() => abort()}
>
{settings.i18n.t('abortAllButton')}
{i18n.t('abortAllButton')}
</Button>
</Grid>
</Grid>

View File

@@ -17,12 +17,23 @@ import {
TextField,
Typography
} from "@mui/material";
import React, { useState } from "react";
import { useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { debounceTime, distinctUntilChanged, map, of, takeWhile } from "rxjs";
import { Socket } from "socket.io-client";
import { LanguageUnion, setCliArgs, setFormatSelection, setLanguage, setServerAddr, setServerPort, setTheme, ThemeUnion } from "./features/settings/settingsSlice";
import { CliArguments } from "./classes";
import {
LanguageUnion,
setCliArgs,
setFormatSelection,
setLanguage,
setServerAddr,
setServerPort,
setTheme,
ThemeUnion
} from "./features/settings/settingsSlice";
import { alreadyUpdated, updated } from "./features/status/statusSlice";
import { I18nBuilder } from "./i18n";
import { RootState } from "./stores/store";
import { validateDomain, validateIP } from "./utils";
@@ -37,6 +48,8 @@ export default function Settings({ socket }: Props) {
const [invalidIP, setInvalidIP] = useState(false);
const i18n = useMemo(() => new I18nBuilder(settings.language), [settings.language])
const cliArgs = useMemo(() => new CliArguments().fromString(settings.cliArgs), [settings.cliArgs])
/**
* Update the server ip address state and localstorage whenever the input value changes.
* Validate the ip-addr then set.s
@@ -114,14 +127,14 @@ export default function Settings({ socket }: Props) {
}}
>
<Typography pb={2} variant="h6" color="primary">
{settings.i18n.t('settingsAnchor')}
{i18n.t('settingsAnchor')}
</Typography>
<FormGroup>
<Grid container spacing={2}>
<Grid item xs={12} md={11}>
<TextField
fullWidth
label={settings.i18n.t('serverAddressTitle')}
label={i18n.t('serverAddressTitle')}
defaultValue={settings.serverAddr}
error={invalidIP}
onChange={handleAddrChange}
@@ -134,7 +147,7 @@ export default function Settings({ socket }: Props) {
<Grid item xs={12} md={1}>
<TextField
fullWidth
label={settings.i18n.t('serverPortTitle')}
label={i18n.t('serverPortTitle')}
defaultValue={settings.serverPort}
onChange={handlePortChange}
error={isNaN(Number(settings.serverPort)) || Number(settings.serverPort) > 65535}
@@ -173,35 +186,48 @@ export default function Settings({ socket }: Props) {
</Select>
</FormControl>
</Grid>
<Grid item xs={12} md={6}>
<TextField
fullWidth
label={'Max download speed' || i18n.t('serverPortTitle')}
defaultValue={settings.serverPort}
onChange={handlePortChange}
error={isNaN(Number(settings.serverPort)) || Number(settings.serverPort) > 65535}
sx={{ mb: 2 }}
/>
</Grid>
</Grid>
<FormControlLabel
control={
<Switch
defaultChecked={settings.cliArgs.noMTime}
onChange={() => dispatch(setCliArgs(settings.cliArgs.toggleNoMTime()))}
defaultChecked={cliArgs.noMTime}
onChange={() => dispatch(setCliArgs(cliArgs.toggleNoMTime().toString()))}
/>
}
label={settings.i18n.t('noMTimeCheckbox')}
label={i18n.t('noMTimeCheckbox')}
sx={{ mt: 3 }}
/>
<FormControlLabel
control={
<Switch
defaultChecked={settings.cliArgs.extractAudio}
onChange={() => dispatch(setCliArgs(settings.cliArgs.toggleExtractAudio()))}
defaultChecked={cliArgs.extractAudio}
onChange={() => dispatch(setCliArgs(cliArgs.toggleExtractAudio().toString()))}
disabled={settings.formatSelection}
/>
}
label={settings.i18n.t('extractAudioCheckbox')}
label={i18n.t('extractAudioCheckbox')}
/>
<FormControlLabel
control={
<Switch
defaultChecked={settings.formatSelection}
onChange={() => dispatch(setFormatSelection(!settings.formatSelection))}
onChange={() => {
dispatch(setCliArgs(cliArgs.disableExtractAudio().toString()))
dispatch(setFormatSelection(!settings.formatSelection))
}}
/>
}
label={settings.i18n.t('formatSelectionEnabler')}
label={i18n.t('formatSelectionEnabler')}
/>
<Grid>
<Stack direction="row">
@@ -210,7 +236,7 @@ export default function Settings({ socket }: Props) {
variant="contained"
onClick={() => dispatch(updated())}
>
{settings.i18n.t('updateBinButton')}
{i18n.t('updateBinButton')}
</Button>
{/* <Button sx={{ mr: 1, mt: 1 }} variant="outlined">Primary</Button> */}
</Stack>
@@ -222,7 +248,7 @@ export default function Settings({ socket }: Props) {
<Snackbar
open={status.updated}
autoHideDuration={1500}
message={settings.i18n.t('toastUpdated')}
message={i18n.t('toastUpdated')}
onClose={updateBinary}
/>
</Container>

View File

@@ -10,9 +10,9 @@ export interface SettingsState {
serverPort: string,
language: LanguageUnion,
theme: ThemeUnion,
cliArgs: CliArguments,
i18n: I18nBuilder,
formatSelection: boolean
cliArgs: string,
formatSelection: boolean,
ratelimit: string,
}
const initialState: SettingsState = {
@@ -20,9 +20,9 @@ const initialState: SettingsState = {
serverPort: localStorage.getItem("server-port") || window.location.port,
language: (localStorage.getItem("language") || "english") as LanguageUnion,
theme: (localStorage.getItem("theme") || "light") as ThemeUnion,
cliArgs: localStorage.getItem("cli-args") ? new CliArguments().fromString(localStorage.getItem("cli-args") ?? "") : new CliArguments(false, true),
i18n: new I18nBuilder((localStorage.getItem("language") || "english")),
cliArgs: localStorage.getItem("cli-args") ?? "",
formatSelection: localStorage.getItem("format-selection") === "true",
ratelimit: localStorage.getItem("rate-limit") ?? "",
}
export const settingsSlice = createSlice({
@@ -39,12 +39,11 @@ export const settingsSlice = createSlice({
},
setLanguage: (state, action: PayloadAction<LanguageUnion>) => {
state.language = action.payload
state.i18n.setLanguage(action.payload)
localStorage.setItem("language", action.payload)
},
setCliArgs: (state, action: PayloadAction<CliArguments>) => {
setCliArgs: (state, action: PayloadAction<string>) => {
state.cliArgs = action.payload
localStorage.setItem("cli-args", action.payload.toString())
localStorage.setItem("cli-args", action.payload)
},
setTheme: (state, action: PayloadAction<ThemeUnion>) => {
state.theme = action.payload
@@ -54,9 +53,13 @@ export const settingsSlice = createSlice({
state.formatSelection = action.payload
localStorage.setItem("format-selection", action.payload.toString())
},
setRateLimit: (state, action: PayloadAction<string>) => {
state.ratelimit = action.payload
localStorage.setItem("rate-limit", action.payload)
},
}
})
export const { setLanguage, setCliArgs, setTheme, setServerAddr, setServerPort, setFormatSelection } = settingsSlice.actions
export const { setLanguage, setCliArgs, setTheme, setServerAddr, setServerPort, setFormatSelection, setRateLimit } = settingsSlice.actions
export default settingsSlice.reducer

30
frontend/tsconfig.json Normal file
View File

@@ -0,0 +1,30 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"target": "es6",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": [
"./index.ts",
"./src/**/*"
],
"exclude": [
"node_modules"
]
}

View File

@@ -28,8 +28,8 @@
"koa-router": "^10.1.1",
"koa-static": "^5.0.0",
"mime-types": "^2.1.35",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.1",
"react-router-dom": "^6.3.0",
"rxjs": "^7.4.0",
@@ -43,6 +43,8 @@
"@types/koa-router": "^7.4.4",
"@types/mime-types": "^2.1.1",
"@types/node": "^17.0.31",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"@types/uuid": "^8.3.4",
"@vitejs/plugin-react": "^1.3.2",

1904
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -93,7 +93,7 @@ io.on('connection', socket => {
retrieveDownload(socket)
})
socket.on('disk-space', () => {
getFreeDiskSpace(socket)
getFreeDiskSpace(socket, settings.download_path || 'downloads/')
})
})

View File

@@ -1,7 +1,6 @@
import { exec, spawn } from 'child_process';
import { statSync } from 'fs';
import Logger from './BetterLogger';
// import net = require('net');
const log = Logger.instance;
@@ -18,19 +17,6 @@ export function existsInProc(pid: number): any {
}
}
/*
function retriveStdoutFromProcFd(pid) {
if (existsInProc(pid)) {
const unixSocket = fs.readlinkSync(`/proc/${pid}/fd/1`).replace('socket:[', '127.0.0.1:').replace(']', '')
if (unixSocket) {
console.log(unixSocket)
logger('proc', `found pending job on pid: ${pid} attached to UNIX socket: ${unixSocket}`)
return net.createConnection(unixSocket)
}
}
}
*/
/**
* Kills a process with a sys-call
* @param {number} pid the killed process pid
@@ -42,9 +28,9 @@ export async function killProcess(pid: number) {
})
}
export function getFreeDiskSpace(socket: any) {
export function getFreeDiskSpace(socket: any, path: string) {
const message: string = 'free-space';
exec("df -P -h | tail -1 | awk '{print $4}'", (_, stdout) => {
exec(`df -h ${path} | tail -1 | awk '{print $4}'`, (_, stdout) => {
socket.emit(message, stdout)
})
}

View File

@@ -1,12 +1,12 @@
{
"compilerOptions": {
"outDir": "./dist",
"target": "ES2018",
"target": "ES2020",
"strict": false,
"noEmit": false,
"moduleResolution": "node",
"module": "commonjs",
"skipLibCheck": true
"skipLibCheck": true,
},
"include": [
"./server/src/**/*"