Skip to content

Commit

Permalink
Overhaul user interface using Joy UI
Browse files Browse the repository at this point in the history
  • Loading branch information
retrixe committed Nov 10, 2024
1 parent 0bd9a30 commit ef0e6d2
Show file tree
Hide file tree
Showing 9 changed files with 723 additions and 108 deletions.
5 changes: 1 addition & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func main() {
}
w = webview.New(debug)
defer w.Destroy()
w.SetSize(540, 240, webview.HintNone)
w.SetSize(640, 320, webview.HintNone)
w.SetTitle("Imprint " + version)

// Bind a function to inject JavaScript and CSS via webview.Eval.
Expand Down Expand Up @@ -113,9 +113,6 @@ func main() {
}
// Call setDevicesReact.
w.Eval("setDevicesReact([" + strings.Join(jsonifiedDevices, ", ") + "])")
if len(jsonifiedDevices) >= 1 {
w.Eval("setSelectedDeviceReact(" + jsonifiedDevices[0] + ")")
}
})

// Bind a function to prompt for file.
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
},
"packageManager": "[email protected]",
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/joy": "5.0.0-beta.48",
"jsbi": "^4.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
Expand Down
26 changes: 15 additions & 11 deletions renderer/App.module.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
.container {
padding: 8px;
.root {
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.select-image-container {
display: flex;
padding-bottom: 0.4em;
.container {
width: 100%;
max-width: 540px;
}

.select-device-container {
.select-container {
display: flex;
padding-bottom: 0.4em;
padding-top: 0.4em;
padding-bottom: 0.4em;
> :last-child {
margin-left: 0.4em;
}
}

.flash-progress-container {
Expand All @@ -23,10 +31,6 @@
width: 100%;
}

.refresh-button {
min-width: 69px;
}

.spacer {
width: 5px;
}
84 changes: 51 additions & 33 deletions renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,29 @@ 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'

// 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 [devices, setDevices] = useState(['N/A'])
const [devices, setDevices] = useState<string[]>([])
const [fileSize, setFileSize] = useState(0)
const [progress, setProgress] = useState<number | string | null>(null)
const [selectedDevice, setSelectedDevice] = useState('N/A')
const [selectedDevice, setSelectedDevice] = useState<string | null>(null)
globalThis.setFileReact = setFile
globalThis.setSpeedReact = setSpeed
globalThis.setDialogReact = setDialog
globalThis.setDevicesReact = setDevices
globalThis.setProgressReact = setProgress
globalThis.setFileSizeReact = setFileSize
globalThis.setSelectedDeviceReact = setSelectedDevice
useColorScheme().setMode('light')
useEffect(() => {
globalThis.refreshDevices()
}, [])
useEffect(() => setSelectedDevice(null), [devices])

const inProgress = typeof progress === 'number'
useEffect(() => setConfirm(false), [inProgress])
Expand All @@ -35,7 +38,7 @@ const App = (): JSX.Element => {
return
}
setProgress(null)
if (selectedDevice === 'N/A') return setDialog('Error: Select a device to flash the ISO to!')
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!')
Expand All @@ -51,52 +54,67 @@ const App = (): JSX.Element => {
? JSBI.divide(JSBI.multiply(JSBI.BigInt(progress), JSBI.BigInt(100)), JSBI.BigInt(fileSize))
: JSBI.BigInt(0)
return (
<>
{dialog !== '' && (
<Dialog
handleDismiss={() => setDialog('')}
message={dialog.startsWith('Error: ') ? dialog.substring(7) : dialog}
error={dialog.startsWith('Error: ')}
/>
)}
<div className={styles.root}>
<Dialog
open={dialog !== ''}
onClose={() => setDialog('')}
message={dialog.startsWith('Error: ') ? dialog.substring(7) : dialog}
error={dialog.startsWith('Error: ')}
/>
<div className={styles.container}>
<span>Step 1: Enter the path to the file.</span>
<div className={styles['select-image-container']}>
<textarea className={styles['full-width']} value={file} onChange={onFileInputChange} />
<button onClick={() => globalThis.promptForFile()}>Select ISO</button>
<Typography>Step 1: Select the disk image (.iso, .img, etc) to flash.</Typography>
<div className={styles['select-container']}>
<Button variant='soft' onClick={() => globalThis.promptForFile()}>
Select File
</Button>
<Textarea
minRows={2}
maxRows={2}
required
placeholder='Path to disk image'
className={styles['full-width']}
value={file}
onChange={onFileInputChange}
/>
</div>
<span>Step 2: Select the device to flash the ISO to.</span>
<div className={styles['select-device-container']}>
<select
<br />
<Typography>Step 2: Select the device to flash to.</Typography>
<div className={styles['select-container']}>
<Select
className={styles['full-width']}
placeholder='Select a device'
value={selectedDevice}
onChange={e => setSelectedDevice(e.target.value)}
required
onChange={(_, value) => setSelectedDevice(value)}
>
{devices.map(device => (
<option key={device} value={device}>
<Option key={device} value={device}>
{device.substr(device.indexOf(' ') + 1)}
</option>
</Option>
))}
</select>
<button onClick={() => globalThis.refreshDevices()} className={styles['refresh-button']}>
</Select>
<Button onClick={() => globalThis.refreshDevices()} variant='soft'>
Refresh
</button>
</Button>
</div>
<span>Step 3: Click the button below to begin flashing.</span>

<br />
<div className={styles['flash-progress-container']}>
<button onClick={onFlashButtonClick}>
{confirm ? 'Confirm?' : inProgress ? 'Cancel' : 'Flash'}
</button>
<div className={styles.spacer} />
{/* FIXME: Add Settings dialog to disable validation. */}
{inProgress && (
<span>
// FIXME: Move this to a dedicated screen.
<Typography className={styles['full-width']}>
Progress: {progressPercent.toString()}% | Speed: {speed}
</span>
</Typography>
)}
<div className={styles['full-width']} />
<Button onClick={onFlashButtonClick}>
{confirm ? 'Confirm?' : inProgress ? 'Cancel' : 'Flash'}
</Button>
{typeof progress === 'string' && <span>{progress}</span>}
</div>
</div>
</>
</div>
)
}

Expand Down
36 changes: 2 additions & 34 deletions renderer/Dialog.module.scss
Original file line number Diff line number Diff line change
@@ -1,39 +1,7 @@
.dialog {
background-color: rgba(0, 0, 0, 0.4);
justify-content: center;
align-items: center;
position: fixed;
display: flex;
height: 100%;
width: 100%;
z-index: 1;
}

.dialog-contents {
background-color: white;
justify-content: flex-start;
flex-direction: column;
max-height: 180px;
max-width: 270px;
display: flex;
padding: 8px;
height: 80%;
width: 60%;
}

.header {
margin: 0px;
color: black;
}

.error {
color: #ff5555;
.dialog-sheet {
max-width: 320px;
}

.flex-spacer {
flex: 1;
}

.dismiss-button {
align-self: center;
}
23 changes: 10 additions & 13 deletions renderer/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import { DialogContent, DialogTitle, Modal, ModalClose, ModalDialog } from '@mui/joy'
import * as styles from './Dialog.module.scss'

const Dialog = (props: {
handleDismiss: () => void
open: boolean
onClose: () => void
message: string
error: boolean
}): JSX.Element => {
return (
<div className={styles.dialog}>
<div className={styles['dialog-contents']}>
<h2 className={`${styles.header} ${props.error ? styles.error : ''}`}>
{props.error ? 'Error' : 'Message'}
</h2>
<p>{props.message}</p>
<div className={styles['flex-spacer']} />
<button className={styles['dismiss-button']} onClick={props.handleDismiss}>
Dismiss
</button>
</div>
</div>
<Modal open={props.open} onClose={props.onClose}>
<ModalDialog className={styles['dialog-sheet']} color={props.error ? 'danger' : undefined}>
<ModalClose variant='soft' />
<DialogTitle>{props.error ? 'Error' : 'Info'}</DialogTitle>
<DialogContent>{props.message}</DialogContent>
</ModalDialog>
</Modal>
)
}

Expand Down
7 changes: 0 additions & 7 deletions renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@
name='viewport'
content='user-scalable=0, initial-scale=1, minimum-scale=1, width=device-width, height=device-height'
/>
<style>
body {
margin: 0;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",
Ubuntu,Cantarell,Oxygen-Sans,"Helvetica Neue",Arial,Roboto,sans-serif;
}
</style>
<style id='inject-css'></style>
</head>
<body><div id="app"></div><script type="module" src="./index.tsx" /></body>
Expand Down
17 changes: 15 additions & 2 deletions renderer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CssBaseline, CssVarsProvider, extendTheme } from '@mui/joy'
import { createRoot } from 'react-dom/client'
import App from './App'

Expand All @@ -15,10 +16,22 @@ declare global {
var setDevicesReact: (devices: string[]) => void
var setFileSizeReact: (fileSize: number) => void
var setProgressReact: (progress: number | string | null) => void
var setSelectedDeviceReact: (selectedDevice: string) => void
} /* eslint-enable no-var */

const theme = extendTheme({
fontFamily: {
body: 'system-ui, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif',
display:
'system-ui, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif',
},
})

const el = document.getElementById('app')
if (el !== null) {
createRoot(el).render(<App />)
createRoot(el).render(
<CssVarsProvider theme={theme}>
<CssBaseline />
<App />
</CssVarsProvider>,
)
}
Loading

0 comments on commit ef0e6d2

Please sign in to comment.