Skip to content

Commit

Permalink
Replace redux with react props and state
Browse files Browse the repository at this point in the history
  • Loading branch information
Hal-9k1 committed Jul 9, 2024
1 parent 39bc9ae commit 09867fa
Show file tree
Hide file tree
Showing 22 changed files with 270 additions and 267 deletions.
4 changes: 1 addition & 3 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import log from 'electron-log';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
import { version as dawnVersion } from '../../package.json';
import { robotInfoActions } from '../renderer/store/robotInfoSlice';

class AppUpdater {
constructor() {
Expand Down Expand Up @@ -83,8 +82,7 @@ const createWindow = async () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
mainWindow.webContents.send('renderer-store-dispatch',
robotInfoActions.setDawnVersion(dawnVersion));
mainWindow.webContents.send('renderer-init', { dawnVersion: dawnVersion });
if (process.env.START_MINIMIZED) {
mainWindow.minimize();
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint no-unused-vars: off */
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';

export type Channels = 'renderer-store-dispatch';
export type Channels = 'renderer-init' | 'robot-connection-update';

const electronHandler = {
ipcRenderer: {
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ body {
margin: 0;
}

.App {
display: flex;
flex-direction: column;
height: 100vh;
}
.App-cols {
display: flex;
flex-direction: row;
flex-shrink: 0;
}
.App-modal-container {
position: fixed;
Expand Down
167 changes: 149 additions & 18 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,167 @@
import { StrictMode } from 'react';
import { Provider as ReduxProvider } from 'react-redux';
import store from './store/store';
import { StrictMode, useState, useEffect, useLayoutEffect } from 'react';
import Topbar from './Topbar';
import Editor from './editor/Editor';
import DeviceInfo from './DeviceInfo';
import AppConsole from './AppConsole';
import ConnectionInfoModal from './modals/ConnectionInfoModal';
import ConnectionConfigModal from './modals/ConnectionConfigModal';
import GamepadInfoModal from './modals/GamepadInfoModal';
import ResizeBar from './ResizeBar';
import './App.css';

const INITIAL_EDITOR_WIDTH_PERCENT = 0.7;
const INITIAL_COLS_HEIGHT_PERCENT = 0.7;
const MAX_EDITOR_WIDTH_PERCENT = 0.9;
const MAX_COLS_HEIGHT_PERCENT = 0.7;
const MIN_EDITOR_WIDTH_PERCENT = 0.6;
const MIN_COLS_HEIGHT_PERCENT = 0.25;

/**
* Top-level component handling layout.
*/
export default function App() {
// Most recent window.innerWidth/Height needed to clamp editor and col size
const [windowSize, setWindowSize] = useState([-1, -1]);
// Current width of editor in pixels
const [editorSize, setEditorSize] = useState(-1);
// Width of editor before ongoing resize in pixels, -1 if no resize
const [editorInitialSize, setEditorInitialSize] = useState(-1);
// Current height of cols in pixels
const [colsSize, setColsSize] = useState(-1);
// Height of cols before ongoing resize in pixels, -1 if no resize
const [colsInitialSize, setColsInitialSize] = useState(-1);
// Name of active modal, empty if no modal is active
const [activeModal, setActiveModal] = useState('');
// Dawn version string, received from main process
const [dawnVersion, setDawnVersion] = useState('');
// Runtime version string, empty if disconnected from robot
const [runtimeVersion, setRuntimeVersion] = useState('');
// Robot battery voltage, -1 if disconnected from robot
const [robotBatteryVoltage, setRobotBatteryVoltage] = useState(-1);
// Robot latency, -1 if disconnected from robot
const [robotLatencyMs, setRobotLatencyMs] = useState(-1);
// Text content of editor
const [editorContent, setEditorContent] = useState('');

useLayoutEffect(() => {
const onResize = () => {
const newSize = [window.innerWidth, window.innerHeight];
if (editorSize === -1) {
setEditorSize(INITIAL_EDITOR_WIDTH_PERCENT * newSize[0]);
} else {
// Resize proportionally
setEditorSize(editorSize * newSize[0] / windowSize[0]);
}
if (colsSize === -1) {
setColsSize(INITIAL_COLS_HEIGHT_PERCENT * newSize[1]);
} else {
setColsSize(colsSize * newSize[1] / windowSize[1]);
}
setWindowSize(newSize);
// Cancel any current resize
setEditorInitialSize(-1);
setColsInitialSize(-1);
};
window.addEventListener('resize', onResize);
onResize();
return () => window.removeEventListener('resize', onResize);
}, []);

useEffect(() => {
// Tests, for example, won't run main/preload.ts
if (window.electron) {
window.electron.ipcRenderer.once('renderer-init', (data) => {
setDawnVersion((data as { dawnVersion: string }).dawnVersion);
});
// ipcRenderer.on returns a cleanup function
return window.electron.ipcRenderer.on('robot-connection-update', (data) => {
const { runtimeVersion, robotBatteryVoltage, robotLatencyMs } = data as {
runtimeVersion?: string;
robotBatteryVoltage?: number;
robotLatencyMs?: number
};
if (runtimeVersion !== undefined) {
setRuntimeVersion(runtimeVersion);
}
if (robotBatteryVoltage !== undefined) {
setRobotBatteryVoltage(robotBatteryVoltage);
}
if (robotLatencyMs !== undefined) {
setRobotLatencyMs(robotLatencyMs);
}
});
}
}, []);

// Editor ResizeBar handlers:
const startEditorResize = () => setEditorInitialSize(editorSize);
const updateEditorResize = (d: number) => {
if (editorInitialSize === -1) {
// Drop update, the window was just resized
return false;
}
setEditorSize(Math.max(
Math.min(
editorInitialSize + d,
MAX_EDITOR_WIDTH_PERCENT * windowSize[0]
),
MIN_EDITOR_WIDTH_PERCENT * windowSize[0]
));
return true;
};
const endEditorResize = () => setEditorInitialSize(-1);

// Cols ResizeBar handlers:
const startColsResize = () => setColsInitialSize(colsSize);
const updateColsResize = (d: number) => {
if (colsInitialSize === -1) {
return false;
}
setColsSize(Math.max(
Math.min(
colsInitialSize + d,
MAX_COLS_HEIGHT_PERCENT * windowSize[1]
),
MIN_COLS_HEIGHT_PERCENT * windowSize[1]
));
return true;
};
const endColsResize = () => setColsInitialSize(-1);

const closeModal = () => setActiveModal('');

return (
<StrictMode>
<ReduxProvider store={store}>
<div className="App">
<Topbar />
<div className="App-cols">
<Editor />
<ResizeBar />
<DeviceInfo />
</div>
<AppConsole />
<div className="App-modal-container">
<ConnectionInfoModal />
<GamepadInfoModal />
</div>
<div className="App">
<Topbar
onConnectionConfigModalOpen={() => setActiveModal('ConnectionConfig')}
dawnVersion={dawnVersion}
runtimeVersion={runtimeVersion}
robotLatencyMs={robotLatencyMs}
robotBatteryVoltage={robotBatteryVoltage} />
<div className="App-cols" style={{ height: colsSize }}>
<Editor width={editorSize} onChange={setEditorContent} />
<ResizeBar
onStartResize={startEditorResize}
onUpdateResize={updateEditorResize}
onEndResize={endEditorResize}
axis="x" />
<DeviceInfo />
</div>
<ResizeBar
onStartResize={startColsResize}
onUpdateResize={updateColsResize}
onEndResize={endColsResize}
axis="y" />
<AppConsole />
<div className="App-modal-container">
<ConnectionConfigModal
isActive={activeModal === 'ConnectionConfig'}
onClose={closeModal} />
<GamepadInfoModal
isActive={activeModal === 'GamepadInfo'}
onClose={closeModal} />
</div>
</ReduxProvider>
</div>
</StrictMode>
);
}
11 changes: 11 additions & 0 deletions src/renderer/AppConsole.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.AppConsole {
padding: 10px;
font-family: "Courier New", monospace;
height: 100%;
}
.AppConsole-inner {
height: 100%;
background-color: #eee;
overflow: hidden scroll;
overflow-wrap: break-word
}
8 changes: 7 additions & 1 deletion src/renderer/AppConsole.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import './AppConsole.css';

/**
* Component displaying output and error messages from student code ran on the robot.
*/
export default function AppConsole() {
return (
<div className="AppConsole">Console</div>
<div className="AppConsole">
<div className="AppConsole-inner">
Test
</div>
</div>
);
}
9 changes: 2 additions & 7 deletions src/renderer/ConnectionConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { useDispatch } from 'react-redux';
import { modalActions, Modals } from './store/modalSlice';

/**
* Button component that opens the ConnectionConfigModal.
*/
export default function ConnectionConfig() {
const dispatch = useDispatch();
export default function ConnectionConfig({ onModalOpen }: { onModalOpen: () => void }) {
return (
<div className="ConnectionConfig">
<button className="ConnectionConfig-button"
onClick={() => dispatch(modalActions.setActive(Modals.ConnectionConfig))}>
<button className="ConnectionConfig-button" onClick={onModalOpen}>
Connection settings...
</button>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/DeviceInfo.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DeviceInfo {
flex-grow: 1;
overflow: auto scroll;
}
12 changes: 12 additions & 0 deletions src/renderer/ResizeBar.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
.ResizeBar {
background-color: gray;
flex-shrink: 0;
}
.ResizeBar-axis-x {
width: 5px;
cursor: col-resize;
}
.ResizeBar-axis-y {
height: 5px;
cursor: row-resize;
}
.ResizeBar-active-area {
top: 0;
left: 0;
position: fixed;
width: 100%;
height: 100%;
}
.ResizeBar-axis-x .ResizeBar-active-area {
cursor: col-resize;
}
.ResizeBar-axis-y .ResizeBar-active-area {
cursor: row-resize;
}
45 changes: 31 additions & 14 deletions src/renderer/ResizeBar.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { editorActions } from './store/editorSlice';
import type { State } from './store/store';
import React, { useState } from 'react';
import './ResizeBar.css'

/**
* Component allowing Editor to be resized.
*/
export default function ResizeBar() {
const isResizing = useSelector((state: State) =>
(state.editor.layoutInfo.resizeInitialXPos !== -1));
const dispatch = useDispatch();
const startResize = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) =>
dispatch(editorActions.beginResizeAtPos(e.clientX));
const updateResize = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) =>
dispatch(editorActions.resizeToPos(e.clientX));
const endResize = () => dispatch(editorActions.endResize());
export default function ResizeBar({ onStartResize, onUpdateResize, onEndResize, axis }: {
onStartResize?: () => void;
onUpdateResize: (pos: number) => boolean;
onEndResize?: () => void;
axis: 'x' | 'y'
}) {
const [isResizing, setIsResizing] = useState(false);
const [initialSize, setInitialSize] = useState(0);
const selectCoordinate = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
return axis === 'x' ? e.clientX : e.clientY;
};
const startResize = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
setIsResizing(true);
setInitialSize(selectCoordinate(e));
if (onStartResize) {
onStartResize();
}
};
const updateResize = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
if (!onUpdateResize(selectCoordinate(e) - initialSize)) {
setIsResizing(false);
}
};
const endResize = () => {
setIsResizing(false);
if (onEndResize) {
onEndResize();
}
};
return (
<div className="ResizeBar" onMouseDown={startResize}>
<div className={'ResizeBar ResizeBar-axis-' + axis} onMouseDown={startResize}>
<div className="ResizeBar-active-area" onMouseUp={endResize} onMouseLeave={endResize}
onMouseMove={updateResize} style={{ display: isResizing ? "block" : "none" }}>
</div>
Expand Down
Loading

0 comments on commit 09867fa

Please sign in to comment.