From 43ddb2548e915fcf89f316d6ca238286b532610c Mon Sep 17 00:00:00 2001 From: Eddy Meals Date: Sat, 21 Sep 2024 19:43:40 -0700 Subject: [PATCH] Annotate return types --- .eslintrc.js | 1 + package.json | 2 +- src/common/IpcEventTypes.ts | 7 +- src/main/MainApp.ts | 75 +++++++++++++------ src/main/main.ts | 4 +- src/main/menu.ts | 52 ++++++------- src/main/network/CodeTransfer.ts | 4 +- src/main/network/PacketStream.ts | 4 +- src/main/network/RuntimeComms.ts | 35 ++++----- src/main/preload.ts | 2 +- src/main/util.ts | 2 +- src/renderer/App.tsx | 65 ++++++++-------- src/renderer/AppConsole.tsx | 4 +- src/renderer/ConnectionConfig.tsx | 4 +- src/renderer/DeviceInfo.tsx | 2 +- src/renderer/Editor.tsx | 52 +++++++++---- src/renderer/ResizeBar.tsx | 20 +++-- src/renderer/Topbar.tsx | 4 +- src/renderer/modals/ConfirmModal.tsx | 6 +- src/renderer/modals/ConnectionConfigModal.tsx | 6 +- src/renderer/modals/GamepadInfoModal.tsx | 4 +- src/renderer/modals/Modal.tsx | 4 +- 22 files changed, 205 insertions(+), 154 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 85089f7..7638500 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -20,6 +20,7 @@ module.exports = { ], 'no-redeclare': 'off', // False positives on typescript's function overloads 'no-undef': 'off', // False positives and typescript won't build if actually undefined vars + '@typescript-eslint/explicit-function-return-type': 'error', }, parserOptions: { ecmaVersion: 2022, diff --git a/package.json b/package.json index ef765f8..b80b785 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts", "doc": "typedoc --name Dawn --excludePrivate false --exclude src/node_modules/** --exclude src/__tests__/** src/**/*.ts src/**/*.tsx --validation", "postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && npm run build:dll", - "lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx", + "lint": "cross-env NODE_ENV=development eslint src --ext .js,.jsx,.ts,.tsx", "package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never && npm run build:dll", "rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app", "start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer", diff --git a/src/common/IpcEventTypes.ts b/src/common/IpcEventTypes.ts index 2d52295..602ef12 100644 --- a/src/common/IpcEventTypes.ts +++ b/src/common/IpcEventTypes.ts @@ -158,7 +158,10 @@ export interface RendererFileControlPromptDownloadData { * Data for a specialization of the renderer-file-control event, sent when the main process wants to * close the file open in the editor. */ -interface RendererFileControlPromptCreateNewFile { +export interface RendererFileControlPromptCreateNewFileData { + /** + * The subtype of file control event. + */ type: 'promptCreateNewFile'; } /** @@ -174,7 +177,7 @@ export type RendererFileControlData = | RendererFileControlExtChangeData | RendererFileControlPromptUploadData | RendererFileControlPromptDownloadData - | RendererFileControlPromptCreateNewFile; + | RendererFileControlPromptCreateNewFileData; /** * Data for the renderer-post-console event sent by the main process to add a console message to the * AppConsole. diff --git a/src/main/MainApp.ts b/src/main/MainApp.ts index 19a6310..2efb43b 100644 --- a/src/main/MainApp.ts +++ b/src/main/MainApp.ts @@ -224,7 +224,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * happen more than once in the program's lifetime (e.g. if the BrowserWindow is somehow * reloaded). */ - onPresent() { + onPresent(): void { this.#watcher?.close(); this.#savePath = null; this.#sendToRenderer('renderer-init', { @@ -237,7 +237,11 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { }); } - onReceiveRobotLogs(msgs: string[]) { + /** + * Called when the robot emits log messages. + * @param msgs - an array of new log messages. + */ + onReceiveRobotLogs(msgs: string[]): void { msgs.forEach((msg) => { this.#sendToRenderer( 'renderer-post-console', @@ -246,15 +250,28 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { }); } - onReceiveLatency(latency: number) { + /** + * Called when a latency check completes. + * @param latency - the measured robot connection latency. + */ + onReceiveLatency(latency: number): void { this.#sendToRenderer('renderer-latency-update', latency); } - onReceiveDevices(deviceInfoState: DeviceInfoState[]) { + /** + * Called when the robot sends lowcar device state. + * @param deviceInfoState - an array of the devices currently connected to the robot and their + * currently measured parameters. + */ + onReceiveDevices(deviceInfoState: DeviceInfoState[]): void { this.#sendToRenderer('renderer-devices-update', deviceInfoState); } - onRuntimeTcpError(err: Error) { + /** + * Called when an error is encountered in the TCP connection to the robot. + * @param err - the error. + */ + onRuntimeTcpError(err: Error): void { this.#sendToRenderer( 'renderer-post-console', new AppConsoleMessage( @@ -264,7 +281,12 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { ); } - onRuntimeUdpError(err: Error) { + /** + * Called when the UDP socket encounters an error or data received by the UDP socket is malformed. + * @param err - the error. Protobuf ProtocolErrors are likely the result of a UDP transmission + * error. + */ + onRuntimeUdpError(err: Error): void { this.#sendToRenderer( 'renderer-post-console', new AppConsoleMessage( @@ -274,7 +296,11 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { ); } - onRuntimeError(err: Error) { + /** + * Called when a generic Runtime communications error is encountered. + * @param err - the error. + */ + onRuntimeError(err: Error): void { this.#sendToRenderer( 'renderer-post-console', new AppConsoleMessage( @@ -284,7 +310,10 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { ); } - onRuntimeDisconnect() { + /** + * Called when the TCP connection to the robot is lost for any reason. + */ + onRuntimeDisconnect(): void { this.#sendToRenderer( 'renderer-post-console', new AppConsoleMessage('dawn-info', 'Disconnected from robot.'), @@ -296,7 +325,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * @param forceDialog - whether the save path selection dialog should be shown even if there is * a currently opened file that may be saved to. */ - promptSaveCodeFile(forceDialog: boolean) { + promptSaveCodeFile(forceDialog: boolean): void { // We need a round trip to the renderer because that's where the code in the editor actually // lives this.#sendToRenderer('renderer-file-control', { @@ -309,7 +338,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * Requests that the renderer process start to load code from a file into the editor. The renderer * may delay or ignore this request (e.g. if the editor has unsaved changes). */ - promptLoadCodeFile() { + promptLoadCodeFile(): void { this.#sendToRenderer('renderer-file-control', { type: 'promptLoad' }); } @@ -317,7 +346,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * Requests that the renderer process start to upload the last loaded file to the robot. The round * trip is needed to notify the user that unsaved changes in the editor will not be uploaded. */ - promptUploadCodeFile() { + promptUploadCodeFile(): void { this.#sendToRenderer('renderer-file-control', { type: 'promptUpload' }); } @@ -325,7 +354,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * Requests that the renderer process start to download code from the robot into the editor. The * renderer may delay or ignore this request (e.g. if the editor has unsaved changes). */ - promptDownloadCodeFile() { + promptDownloadCodeFile(): void { this.#sendToRenderer('renderer-file-control', { type: 'promptDownload' }); } @@ -333,7 +362,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * Requests that the renderer process close the open file in the editor. The renderer may delay or * ignore this request (e.g. if there are unsaved changes). */ - promptCreateNewCodeFile() { + promptCreateNewCodeFile(): void { this.#sendToRenderer('renderer-file-control', { type: 'promptCreateNewFile', }); @@ -345,7 +374,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * @param forceDialog - whether the user should be prompted for a save path even if there is a * currently open file. */ - #saveCodeFile(code: string, forceDialog: boolean) { + #saveCodeFile(code: string, forceDialog: boolean): void { let success = true; if (this.#savePath === null || forceDialog) { success = this.#showCodePathDialog('save'); @@ -377,9 +406,9 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { } /** - * Tries to load code from a file into the editor. Fails is the user does not choose a path. + * Tries to load code from a file into the editor. Fails if the user does not choose a path. */ - #openCodeFile() { + #openCodeFile(): void { const success = this.#showCodePathDialog('load'); if (success) { try { @@ -403,7 +432,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * contents) to the robot at the given IP. Does nothing if no file has been opened yet. * @param ip - the IP address to connect to via SSH */ - #uploadCodeFile(ip: string) { + #uploadCodeFile(ip: string): void { if (this.#savePath) { if ( fs @@ -453,7 +482,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * Downloads code from the robot at the given IP into the editor. * @param ip - the IP address to connect to via SSH */ - #downloadCodeFile(ip: string) { + #downloadCodeFile(ip: string): void { this.#codeTransfer .download(ip) .then((content: string) => { @@ -486,9 +515,9 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * @param e - the value * @returns A string representation of the value. */ - #getErrorDetails(e: any) { - let msg = e instanceof Error ? e.stack : String(e); - if (e.cause) { + #getErrorDetails(e: any): string { + let msg = e instanceof Error && e.stack ? e.stack : String(e); + if (e instanceof Error && e.cause) { msg += `\nCaused by: ${this.#getErrorDetails(e.cause)}`; } return msg; @@ -500,7 +529,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * @param mode - the type of dialog that should be shown * @returns Whether a new path was chosen successfully. */ - #showCodePathDialog(mode: 'save' | 'load') { + #showCodePathDialog(mode: 'save' | 'load'): boolean { let result: string | string[] | undefined; if (mode === 'save') { result = dialog.showSaveDialogSync(this.#mainWindow, { @@ -532,7 +561,7 @@ export default class MainApp implements MenuHandler, RuntimeCommsListener { * Attaches a file watcher to listen for external changes to the last saved or loaded code path, * destroying the previous one if it exists. */ - #watchCodeFile() { + #watchCodeFile(): void { this.#watcher?.close(); this.#watchDebounce = true; this.#watcher = fs.watch( diff --git a/src/main/main.ts b/src/main/main.ts index ac16122..64133c1 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -38,7 +38,7 @@ if (isDebug) { require('electron-debug')(); } -const installExtensions = async () => { +const installExtensions = async (): Promise => { const installer = require('electron-devtools-installer'); const forceDownload = !!process.env.UPGRADE_EXTENSIONS; const extensions = ['REACT_DEVELOPER_TOOLS']; @@ -51,7 +51,7 @@ const installExtensions = async () => { .catch(console.log); }; -const createWindow = async () => { +const createWindow = async (): Promise => { if (isDebug) { await installExtensions(); } diff --git a/src/main/menu.ts b/src/main/menu.ts index a683c79..9b3cb5c 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -94,7 +94,7 @@ export default class MenuBuilder { Menu.buildFromTemplate([ { label: 'Inspect element', - click: () => { + click: (): void => { this.mainWindow.webContents.inspectElement(x, y); }, }, @@ -131,7 +131,7 @@ export default class MenuBuilder { { label: 'Quit', accelerator: 'Command+Q', - click: () => { + click: (): void => { app.quit(); }, }, @@ -143,28 +143,28 @@ export default class MenuBuilder { { label: 'New', accelerator: 'Command+N', - click: () => { + click: (): void => { this.menuHandler.promptCreateNewCodeFile(); }, }, { label: 'Open', accelerator: 'Command+O', - click: () => { + click: (): void => { this.menuHandler.promptLoadCodeFile(); }, }, { label: 'Save', accelerator: 'Command+S', - click: () => { + click: (): void => { this.menuHandler.promptSaveCodeFile(false); }, }, { label: 'Save As', accelerator: 'Command+Shift+S', - click: () => { + click: (): void => { this.menuHandler.promptSaveCodeFile(true); }, }, @@ -172,13 +172,13 @@ export default class MenuBuilder { { label: 'Upload open file to robot', accelerator: 'Command+Shift+U', - click: () => { + click: (): void => { this.menuHandler.promptUploadCodeFile(); }, }, { label: 'Download code from robot', - click: () => { + click: (): void => { this.menuHandler.promptDownloadCodeFile(); }, }, @@ -206,21 +206,21 @@ export default class MenuBuilder { { label: 'Reload', accelerator: 'Command+R', - click: () => { + click: (): void => { this.mainWindow.webContents.reload(); }, }, { label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', - click: () => { + click: (): void => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); }, }, { label: 'Toggle Developer Tools', accelerator: 'Alt+Command+I', - click: () => { + click: (): void => { this.mainWindow.webContents.toggleDevTools(); }, }, @@ -232,7 +232,7 @@ export default class MenuBuilder { { label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', - click: () => { + click: (): void => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); }, }, @@ -256,13 +256,13 @@ export default class MenuBuilder { submenu: [ { label: 'Learn More', - click() { + click(): void { shell.openExternal('https://electronjs.org'); }, }, { label: 'Documentation', - click() { + click(): void { shell.openExternal( 'https://github.com/electron/electron/tree/main/docs#readme', ); @@ -270,13 +270,13 @@ export default class MenuBuilder { }, { label: 'Community Discussions', - click() { + click(): void { shell.openExternal('https://www.electronjs.org/community'); }, }, { label: 'Search Issues', - click() { + click(): void { shell.openExternal('https://github.com/electron/electron/issues'); }, }, @@ -310,41 +310,41 @@ export default class MenuBuilder { { label: '&New', accelerator: 'Ctrl+N', - click: () => { + click: (): void => { this.menuHandler.promptCreateNewCodeFile(); }, }, { label: '&Open', accelerator: 'Ctrl+O', - click: () => { + click: (): void => { this.menuHandler.promptLoadCodeFile(); }, }, { label: '&Save', accelerator: 'Ctrl+S', - click: () => { + click: (): void => { this.menuHandler.promptSaveCodeFile(false); }, }, { label: 'Save &As', accelerator: 'Ctrl+Shift+S', - click: () => { + click: (): void => { this.menuHandler.promptSaveCodeFile(true); }, }, { label: '&Upload open file to robot', accelerator: 'Ctrl+Alt+U', - click: () => { + click: (): void => { this.menuHandler.promptUploadCodeFile(); }, }, { label: 'Download code from robot', - click: () => { + click: (): void => { this.menuHandler.promptDownloadCodeFile(); }, }, @@ -359,14 +359,14 @@ export default class MenuBuilder { { label: '&Reload', accelerator: 'Ctrl+R', - click: () => { + click: (): void => { this.mainWindow.webContents.reload(); }, }, { label: 'Toggle &Full Screen', accelerator: 'F11', - click: () => { + click: (): void => { this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen(), ); @@ -375,7 +375,7 @@ export default class MenuBuilder { { label: 'Toggle &Developer Tools', accelerator: 'Ctrl+Shift+I', - click: () => { + click: (): void => { this.mainWindow.webContents.toggleDevTools(); }, }, @@ -384,7 +384,7 @@ export default class MenuBuilder { { label: 'Toggle &Full Screen', accelerator: 'F11', - click: () => { + click: (): void => { this.mainWindow.setFullScreen( !this.mainWindow.isFullScreen(), ); diff --git a/src/main/network/CodeTransfer.ts b/src/main/network/CodeTransfer.ts index 6482371..a0747e3 100644 --- a/src/main/network/CodeTransfer.ts +++ b/src/main/network/CodeTransfer.ts @@ -49,7 +49,7 @@ export default class CodeTransfer { * @param ip - the IP of the robot to connect to via SSH * @returns A Promise that resolves when the upload is complete. */ - upload(localCodePath: string, ip: string): Promise { + upload(localCodePath: string, ip: string): Promise { return this.#doSftp(ip, (sftp, resolve, reject) => { sftp.fastPut( localCodePath, @@ -62,7 +62,7 @@ export default class CodeTransfer { }), ); } else { - resolve(null); + resolve(); } }, ); diff --git a/src/main/network/PacketStream.ts b/src/main/network/PacketStream.ts index 29c3653..9185849 100644 --- a/src/main/network/PacketStream.ts +++ b/src/main/network/PacketStream.ts @@ -43,7 +43,7 @@ export default class PacketStream extends Transform { chunk: any, encoding: NodeJS.BufferEncoding, callback: TransformCallback, - ) { + ): void { let chunkBuf: Buffer; if (chunk instanceof Buffer) { chunkBuf = chunk; @@ -74,7 +74,7 @@ export default class PacketStream extends Transform { * @returns Whether a full packet was read and output and the next packet is ready to be attempted * to be read. */ - #tryReadPacket(shouldConcatHeader: boolean) { + #tryReadPacket(shouldConcatHeader: boolean): boolean { if (this.#bufLen < HEADER_LENGTH) { // Wait for complete header before reading packet return false; diff --git a/src/main/network/RuntimeComms.ts b/src/main/network/RuntimeComms.ts index 48ec7e2..2d0dfe7 100644 --- a/src/main/network/RuntimeComms.ts +++ b/src/main/network/RuntimeComms.ts @@ -134,7 +134,7 @@ export default class RuntimeComms { /** * Stops listening to the TCP and UDP sockets until resumed by setRobotIp. */ - disconnect() { + disconnect(): void { this.#tcpDisconnected = true; // Don't reconnect if (this.#pingInterval) { clearInterval(this.#pingInterval); @@ -148,7 +148,7 @@ export default class RuntimeComms { * @param ip - the address and optionally the port Runtime is listening on, separated by a colon. * @returns Whether the given IP forms a valid address. */ - setRobotIp(ip: string) { + setRobotIp(ip: string): boolean { [this.#runtimeAddr] = ip.split(':'); const portStr: string | undefined = ip.split(':')[1]; this.#runtimePort = portStr ? Number(portStr) : DEFAULT_RUNTIME_PORT; @@ -169,7 +169,7 @@ export default class RuntimeComms { * Sends a new run mode. * @param runMode - the new run mode. */ - sendRunMode(runMode: protos.IRunMode) { + sendRunMode(runMode: protos.IRunMode): void { if (this.#tcpSock) { this.#tcpSock.write(this.#createPacket(MsgType.RUN_MODE, runMode)); } @@ -179,7 +179,7 @@ export default class RuntimeComms { * Sends device preferences. * @param deviceData - device preferences to send. */ - sendDevicePreferences(deviceData: protos.IDevData) { + sendDevicePreferences(deviceData: protos.IDevData): void { if (this.#tcpSock) { this.#tcpSock.write(this.#createPacket(MsgType.DEVICE_DATA, deviceData)); } @@ -190,7 +190,7 @@ export default class RuntimeComms { * @param data - the textual challenge data to send. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars - sendChallengeInputs(data: protos.IText) { + sendChallengeInputs(data: protos.IText): void { if (this.#tcpSock) { throw new Error('Not implemented.'); // MsgTypes from old dawn are inconsistent? // this.#tcpSock.write(this.#createPacket(MsgType.CHALLENGE_DATA, data)); @@ -201,7 +201,7 @@ export default class RuntimeComms { * Sends the robot's starting position. * @param startPos - the robot's starting position index to send. */ - sendRobotStartPos(startPos: protos.IStartPos) { + sendRobotStartPos(startPos: protos.IStartPos): void { if (this.#tcpSock) { this.#tcpSock.write(this.#createPacket(MsgType.START_POS, startPos)); } @@ -212,7 +212,7 @@ export default class RuntimeComms { * @param inputs - the inputs to send. * @param source - the device that is the source of the inputs. */ - sendInputs(inputs: protos.Input[], source: protos.Source) { + sendInputs(inputs: protos.Input[], source: protos.Source): void { // if (this.#udpSock) { // this.udpSock.send(protos.UserInputs.encode({ // inputs: inputs.length ? inputs : [ @@ -236,7 +236,7 @@ export default class RuntimeComms { * Sends a timestamped packet that Runtime will echo, updating the latency when we receive it * again. */ - #sendLatencyTest() { + #sendLatencyTest(): void { if (this.#tcpSock) { this.#tcpSock.write( this.#createPacket( @@ -254,10 +254,7 @@ export default class RuntimeComms { * Disconnects the old TCP socket if open, then makes a new one and connects it to the * most recently known Runtime IP and port. */ - #connectTcp() { - this.#commsListener.onRuntimeError( - new Error('Attempting to connect tcp socket'), - ); + #connectTcp(): void { this.#tcpDisconnected = false; this.#disconnectTcp(); const tcpStream = new PacketStream().on( @@ -281,7 +278,7 @@ export default class RuntimeComms { /** * Closes the old UDP socket if open, then makes and binds a new one. */ - #connectUdp() { + #connectUdp(): void { this.#disconnectUdp(); this.#udpSock = createUdpSocket({ type: 'udp4', @@ -298,7 +295,7 @@ export default class RuntimeComms { /** * Ends and disconnects the TCP socket if open. */ - #disconnectTcp() { + #disconnectTcp(): void { if (this.#tcpSock) { this.#tcpSock.resetAndDestroy(); this.#tcpSock = null; @@ -308,7 +305,7 @@ export default class RuntimeComms { /** * Closes the UDP socket if open. */ - #disconnectUdp() { + #disconnectUdp(): void { if (this.#udpSock) { this.#udpSock.close(); this.#udpSock = null; @@ -318,7 +315,7 @@ export default class RuntimeComms { /** * Handler for TCP 'connect' event. */ - #handleTcpConnection() { + #handleTcpConnection(): void { if (this.#tcpSock) { this.#tcpSock.write(new Uint8Array([1])); // Tell Runtime that we are Dawn, not Shepherd } @@ -328,7 +325,7 @@ export default class RuntimeComms { * Processes a received packet. * @param packet - the received packet. */ - #handlePacket(packet: Packet) { + #handlePacket(packet: Packet): void { const { type, data } = packet; switch (type) { case MsgType.LOG: @@ -391,7 +388,7 @@ export default class RuntimeComms { * code because UDP packets might not be well-formed. * @param data - the payload of the received packet. */ - #handleUdpMessage(data: Buffer) { + #handleUdpMessage(data: Buffer): void { try { this.#handlePacket({ type: MsgType.DEVICE_DATA, data }); } catch (err) { @@ -402,7 +399,7 @@ export default class RuntimeComms { /** * Handles TCP 'close' event and tries to reconnect if we didn't cause the disconnection. */ - #handleTcpClose() { + #handleTcpClose(): void { this.#commsListener.onRuntimeDisconnect(); if (!this.#tcpDisconnected) { setTimeout(this.#connectTcp.bind(this), TCP_RECONNECT_DELAY); diff --git a/src/main/preload.ts b/src/main/preload.ts index bdcc80b..51605ad 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -54,7 +54,7 @@ function on( ): () => void; function on(channel: 'renderer-quit-request', func: () => void): () => void; function on(channel: RendererChannels, func: (arg?: any) => void): () => void { - const subscription = (_event: IpcRendererEvent, ...args: unknown[]) => + const subscription = (_event: IpcRendererEvent, ...args: unknown[]): void => func(...args); ipcRenderer.on(channel, subscription); diff --git a/src/main/util.ts b/src/main/util.ts index 7a57162..5a8b7e3 100644 --- a/src/main/util.ts +++ b/src/main/util.ts @@ -8,7 +8,7 @@ import path from 'path'; * @param htmlFileName - the HTML path to convert. * @returns The HTML path converted to a path suitable for the build type. */ -export function resolveHtmlPath(htmlFileName: string) { +export function resolveHtmlPath(htmlFileName: string): string { if (process.env.NODE_ENV === 'development') { const port = process.env.PORT || 1212; const url = new URL(`http://localhost:${port}`); diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index b6d9769..7cd8e9e 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -31,7 +31,7 @@ const MIN_CONSOLE_HEIGHT_PERCENT = 0.3; /** * Top-level component that communicates with main process and contains most renderer state. */ -export default function App() { +export default function App(): JSX.Element { // Current width of editor in pixels const [editorSize, setEditorSize] = useState(-1); // Width of editor before ongoing resize in pixels, -1 if no resize @@ -102,20 +102,20 @@ export default function App() { [] as DeviceInfoState[], ); - const changeActiveModal = (newModalName: string) => { + const changeActiveModal = (newModalName: string): void => { if (document.activeElement instanceof HTMLElement) { document.activeElement.blur(); } setActiveModal(newModalName); }; - const changeEditorContent = (newContent: string) => { + const changeEditorContent = (newContent: string): void => { setEditorContent(newContent); if (editorStatus === 'clean') { setEditorStatus('dirty'); } }; - const closeModal = () => changeActiveModal(''); - const handleConnectionChange = (event: ConnectionConfigChangeEvent) => { + const closeModal = (): void => changeActiveModal(''); + const handleConnectionChange = (event: ConnectionConfigChangeEvent): void => { if (event.name === 'IPAddress') { setIPAddress(event.value); } else if (event.name === 'SSHAddress') { @@ -126,8 +126,8 @@ export default function App() { setFieldStationNum(event.value); } }; - const startEditorResize = () => setEditorInitialSize(editorSize); - const updateEditorResize = (d: number) => { + const startEditorResize = (): void => setEditorInitialSize(editorSize); + const updateEditorResize = (d: number): boolean => { if (editorInitialSize === -1) { // Drop update, the window was just resized return false; @@ -143,9 +143,9 @@ export default function App() { ); return true; }; - const endEditorResize = () => setEditorInitialSize(-1); - const startColsResize = () => setConsoleInitSize(consoleSize); - const updateColsResize = (d: number) => { + const endEditorResize = (): void => setEditorInitialSize(-1); + const startColsResize = (): void => setConsoleInitSize(consoleSize); + const updateColsResize = (d: number): boolean => { if (consoleInitialSize === -1) { return false; } @@ -161,8 +161,8 @@ export default function App() { ); return true; }; - const endColsResize = () => setConsoleInitSize(-1); - const changeRunMode = (mode: RobotRunMode) => { + const endColsResize = (): void => setConsoleInitSize(-1); + const changeRunMode = (mode: RobotRunMode): void => { window.electron.ipcRenderer.sendMessage('main-update-robot-mode', mode); setRobotRunning(mode !== RobotRunMode.IDLE); }; @@ -232,11 +232,11 @@ export default function App() { // Update windowSize: useLayoutEffect(() => { - const onResize = () => + const onResize = (): void => setWindowSize([window.innerWidth, window.innerHeight]); window.addEventListener('resize', onResize); onResize(); - return () => window.removeEventListener('resize', onResize); + return (): void => window.removeEventListener('resize', onResize); }, []); useEffect(() => { // Tests won't run main/preload.ts @@ -266,9 +266,10 @@ export default function App() { setDeviceInfoState, ), ]; - return () => listenerDestructors.forEach((destructor) => destructor()); + return (): void => + listenerDestructors.forEach((destructor) => destructor()); } - return () => {}; + return (): void => {}; }, []); useEffect(() => { if (window.electron) { @@ -279,7 +280,7 @@ export default function App() { } }); } - return () => {}; + return (): void => {}; }, [consoleIsOpen]); useEffect(() => { if (window.electron) { @@ -313,7 +314,7 @@ export default function App() { } }); } - return () => {}; + return (): void => {}; }, [editorStatus, saveFile, loadFile, uploadDownloadFile, createNewFile]); useEffect(() => { @@ -326,14 +327,14 @@ export default function App() { } }); } - return () => {}; + return (): void => {}; }, [activeModal, editorStatus, closeWindow]); return (
+ onConnectionConfigModalOpen={(): void => changeActiveModal('ConnectionConfig') } dawnVersion={dawnVersion} @@ -355,23 +356,23 @@ export default function App() { onOpen={loadFile} onSave={saveFile} onNewFile={createNewFile} - onRobotUpload={() => uploadDownloadFile(true)} - onRobotDownload={() => uploadDownloadFile(false)} - onStartRobot={(opmode: 'auto' | 'teleop') => { + onRobotUpload={(): void => uploadDownloadFile(true)} + onRobotDownload={(): void => uploadDownloadFile(false)} + onStartRobot={(opmode: 'auto' | 'teleop'): void => { changeRunMode( opmode === 'auto' ? RobotRunMode.AUTO : RobotRunMode.TELEOP, ); }} - onStopRobot={() => changeRunMode(RobotRunMode.IDLE)} - onToggleConsole={() => { + onStopRobot={(): void => changeRunMode(RobotRunMode.IDLE)} + onToggleConsole={(): void => { setConsoleIsOpen((v) => !v); setConsoleIsAlerted(false); }} - onClearConsole={() => { + onClearConsole={(): void => { setConsoleMsgs([]); setConsoleIsAlerted(false); }} - onToggleKeyboardControls={() => { + onToggleKeyboardControls={(): void => { setKbCtrlEnabled((v) => !v); }} /> @@ -411,7 +412,7 @@ export default function App() { { + onConfirm={(): void => { window.electron.ipcRenderer.sendMessage('main-file-control', { type: 'load', }); @@ -436,7 +437,7 @@ export default function App() { uploadDownloadFile(true)} + onConfirm={(): void => uploadDownloadFile(true)} modalTitle="Confirm upload" >

@@ -446,7 +447,9 @@ export default function App() { setShowDirtyUploadWarning(!e.target.checked)} + onChange={(e): void => + setShowDirtyUploadWarning(!e.target.checked) + } /> Don't show this warning again @@ -454,7 +457,7 @@ export default function App() { uploadDownloadFile(false)} + onConfirm={(): void => uploadDownloadFile(false)} modalTitle="Confirm download" >

diff --git a/src/renderer/AppConsole.tsx b/src/renderer/AppConsole.tsx index d7ef8dd..0def89f 100644 --- a/src/renderer/AppConsole.tsx +++ b/src/renderer/AppConsole.tsx @@ -3,7 +3,7 @@ import './AppConsole.css'; /** * Component displaying output and error messages from student code ran on the robot. - * @param props - props + * @param props * @param props.height - the height of the console in pixels * @param props.messages - array of console messages to display */ @@ -13,7 +13,7 @@ export default function AppConsole({ }: { height: number; messages: AppConsoleMessage[]; -}) { +}): JSX.Element { return (

diff --git a/src/renderer/ConnectionConfig.tsx b/src/renderer/ConnectionConfig.tsx
index d76670a..9b70b04 100644
--- a/src/renderer/ConnectionConfig.tsx
+++ b/src/renderer/ConnectionConfig.tsx
@@ -1,13 +1,13 @@
 /**
  * Button component that opens the ConnectionConfigModal.
- * @param props - props
+ * @param props
  * @param props.onModalOpen - click handler for the button that opens the ConnectionConfigModal
  */
 export default function ConnectionConfig({
   onModalOpen,
 }: {
   onModalOpen: () => void;
-}) {
+}): JSX.Element {
   return (
     
- -