diff --git a/.vscode/launch.json b/.vscode/launch.json index e972b42..a12d224 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,9 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "${fileDirname}" + "program": "${fileDirname}", + "buildFlags": ["-ldflags=-X main.overrideUrl=http://localhost:1234"], + "env": { "DEBUG": "true" } } ] } diff --git a/main.go b/main.go index e42839a..ee4bf6d 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,6 @@ import ( webview "github.com/webview/webview_go" ) -// FIXME: Design UI (with live warnings/errors). // FIXME: Validate written image. // TODO: Future support for flashing to an internal drive? @@ -132,7 +131,6 @@ func main() { } else if !stat.Mode().IsRegular() { w.Eval("setDialogReact(" + ParseToJsString("Error: Select a regular file!") + ")") } else { // Send this back to React. - w.Eval("setFileSizeReact(" + strconv.Itoa(int(stat.Size())) + ")") w.Eval("setFileReact(" + ParseToJsString(filename) + ")") } } @@ -142,7 +140,7 @@ func main() { var inputPipe io.WriteCloser var cancelled bool = false var mutex sync.Mutex - w.Bind("flash", func(file string, selectedDevice string) { + w.Bind("flash", func(file string, device string, deviceSize int) { cancelled = false stat, err := os.Stat(file) if err != nil { @@ -151,18 +149,19 @@ func main() { } else if !stat.Mode().IsRegular() { w.Eval("setDialogReact(" + ParseToJsString("Error: Select a regular file!") + ")") return - } else { - w.Eval("setFileSizeReact(" + strconv.Itoa(int(stat.Size())) + ")") + } else if stat.Size() > int64(deviceSize) { + w.Eval("setDialogReact(" + ParseToJsString("Error: The disk image is too big to fit on the selected drive!") + ")") + return } - channel, stdin, err := app.CopyConvert(file, selectedDevice) + fileSizeStr := strconv.Itoa(int(stat.Size())) + channel, stdin, err := app.CopyConvert(file, device) inputPipe = stdin if err != nil { w.Eval("setDialogReact(" + ParseToJsString("Error: "+err.Error()) + ")") return - } else { - w.Eval("setSpeedReact(" + ParseToJsString("0 MB/s") + ")") // Show progress instantly. - w.Eval("setProgressReact(0)") } + // Show progress instantly. + w.Eval("setProgressReact({ bytes: 0, total: " + fileSizeStr + ", speed: '0 MB/s' })") go (func() { errored := false for { @@ -177,10 +176,11 @@ func main() { w.Dispatch(func() { if progress.Error != nil { // Error is always the last emitted. errored = true - w.Eval("setDialogReact(" + ParseToJsString("Error: "+progress.Error.Error()) + ")") + w.Eval("setProgressReact(" + ParseToJsString("Error: "+progress.Error.Error()) + ")") } else { - w.Eval("setSpeedReact(" + ParseToJsString(progress.Speed) + ")") - w.Eval("setProgressReact(" + strconv.Itoa(progress.Bytes) + ")") + w.Eval("setProgressReact({ bytes: " + strconv.Itoa(progress.Bytes) + + ", total: " + fileSizeStr + + ", speed: " + ParseToJsString(progress.Speed) + " })") } }) } else { diff --git a/renderer/App.module.scss b/renderer/App.module.scss index 3fcf4f6..4c37e52 100644 --- a/renderer/App.module.scss +++ b/renderer/App.module.scss @@ -11,26 +11,3 @@ width: 100%; max-width: 540px; } - -.select-container { - display: flex; - padding-top: 0.4em; - padding-bottom: 0.4em; - > :last-child { - margin-left: 0.4em; - } -} - -.flash-progress-container { - display: flex; - align-items: center; - padding-top: 0.4em; -} - -.full-width { - width: 100%; -} - -.spacer { - width: 5px; -} diff --git a/renderer/App.tsx b/renderer/App.tsx index 70ddec4..bf98040 100644 --- a/renderer/App.tsx +++ b/renderer/App.tsx @@ -1,118 +1,55 @@ +import { DialogContent, DialogTitle, Modal, ModalClose, ModalDialog } from '@mui/joy' import { useEffect, useState } from 'react' -import JSBI from 'jsbi' -import Dialog from './Dialog' import * as styles from './App.module.scss' -import { Button, Option, Select, Textarea, Typography, useColorScheme } from '@mui/joy' +import MainScreen from './screens/MainScreen' +import ProgressScreen from './screens/ProgressScreen' // TODO: Experiment with lg size for the UI. const App = (): JSX.Element => { const [file, setFile] = useState('') - const [speed, setSpeed] = useState('') - const [dialog, setDialog] = useState('') - const [confirm, setConfirm] = useState(false) + const [device, setDevice] = useState(null) const [devices, setDevices] = useState([]) - const [fileSize, setFileSize] = useState(0) - const [progress, setProgress] = useState(null) - const [selectedDevice, setSelectedDevice] = useState(null) globalThis.setFileReact = setFile - globalThis.setSpeedReact = setSpeed - globalThis.setDialogReact = setDialog globalThis.setDevicesReact = setDevices + const [dialog, setDialog] = useState('') + globalThis.setDialogReact = setDialog + const [progress, setProgress] = useState(null) globalThis.setProgressReact = setProgress - globalThis.setFileSizeReact = setFileSize - useColorScheme().setMode('light') useEffect(() => { globalThis.refreshDevices() }, []) - useEffect(() => setSelectedDevice(null), [devices]) + useEffect(() => setDevice(null), [devices]) - const inProgress = typeof progress === 'number' - useEffect(() => setConfirm(false), [inProgress]) - const onFlashButtonClick = (): void => { - if (inProgress) { - // FIXME: A dialog would be better. - if (confirm) { - setConfirm(false) - globalThis.cancelFlash() - } else setConfirm(true) - return - } - setProgress(null) - if (selectedDevice === null) return setDialog('Error: Select a device to flash the ISO to!') - if (file === '') return setDialog('Error: Select an ISO to flash to a device!') - if (JSBI.greaterThan(JSBI.BigInt(fileSize), JSBI.BigInt(selectedDevice.split(' ')[0]))) { - return setDialog('Error: The ISO file is too big to fit on the selected drive!') - } - if (!confirm) return setConfirm(true) - setConfirm(false) - globalThis.flash(file, selectedDevice.split(' ')[1]) - } - const onFileInputChange: React.ChangeEventHandler = event => - setFile(event.target.value.replace(/\n/g, '')) - - const progressPercent = inProgress - ? JSBI.divide(JSBI.multiply(JSBI.BigInt(progress), JSBI.BigInt(100)), JSBI.BigInt(fileSize)) - : JSBI.BigInt(0) return (
- setDialog('')} - message={dialog.startsWith('Error: ') ? dialog.substring(7) : dialog} - error={dialog.startsWith('Error: ')} - /> + setDialog('')}> + + + {dialog.startsWith('Error: ') ? 'Error' : 'Info'} + + {dialog.startsWith('Error: ') ? dialog.substring(7) : dialog} + + +
- Step 1: Select the disk image (.iso, .img, etc) to flash. -
- -