Add typed ipc

This commit is contained in:
Robert Kossessa
2024-03-02 18:09:16 +01:00
parent bee6ab1076
commit 43b384dfef
8 changed files with 89 additions and 103 deletions

View File

@@ -9,7 +9,7 @@ const NANO_BAUD_RATE = 115200
class NanoDevices extends EventEmitter { class NanoDevices extends EventEmitter {
all_nano_devices: { [key: string]: PortInfo } = {} all_nano_devices: { [key: string]: PortInfo } = {}
connected_nano_devices: { [key: string]: { port: SerialPort; data: string } } = {} connected_nano_devices: { [key: string]: { port: SerialPort } } = {}
_list() { _list() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -105,7 +105,7 @@ class NanoDevices extends EventEmitter {
}) })
port.on('open', () => { port.on('open', () => {
resolve(nano_device.serialNumber) resolve(nano_device.serialNumber)
this.connected_nano_devices[nano_device.serialNumber!] = { port: port, data: '' } this.connected_nano_devices[nano_device.serialNumber!] = { port: port }
this.emit('nanodevices:connected', nano_device.serialNumber) this.emit('nanodevices:connected', nano_device.serialNumber)
}) })
port.on('data', (data) => { port.on('data', (data) => {

View File

@@ -1,8 +1,31 @@
import { ElectronAPI } from '@electron-toolkit/preload' export interface INanoDevicesAPI {
list_devices(): Promise<string[]>
connect(deviceid: string): Promise<string>
disconnect(deviceid: string): Promise<string>
on_event(
eventid_filter: string,
callback: (eventid: string, deviceid: string, data: any) => void
): void
send(deviceid: string, jsonstr: string): Promise<void>
}
export interface IElectronAPI {
platform: NodeJS.Platform
isDevelopment: boolean
minimizeWindow: () => void
toggleMaximizeWindow: () => void
closeWindow: () => void
openExternal: (url: string) => void
onMaximized: (callback: () => void) => void
onUnmaximized: (callback: () => void) => void
onMenu: (callback: (key: string) => void) => void
openDevTools: () => void
reload: () => void
}
declare global { declare global {
interface Window { interface Window {
electron: ElectronAPI nanoDevicesAPI: INanoDevicesAPI
api: unknown electronAPI: IElectronAPI
} }
} }

View File

@@ -1,61 +1,43 @@
import { contextBridge, ipcRenderer } from 'electron' import { contextBridge, ipcRenderer } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer // expose an API to choose available devices
const api = {} contextBridge.exposeInMainWorld('nanoDevicesAPI', {
list_devices() {
// Use `contextBridge` APIs to expose Electron APIs to return ipcRenderer.invoke('nanodevices:list_devices')
// renderer only if context isolation is enabled, otherwise },
// just add to the DOM global. connect(deviceid) {
if (process.contextIsolated) { return ipcRenderer.invoke('nanodevices:connect', deviceid)
try { },
// expose an API to choose available devices disconnect(deviceid) {
contextBridge.exposeInMainWorld('nanodevices', { return ipcRenderer.invoke('nanodevices:disconnect', deviceid)
list_devices() { },
return ipcRenderer.invoke('nanodevices:list_devices') on_event(eventid_filter, callback) {
}, console.log('attaching filter for ', eventid_filter)
connect(deviceid) { ipcRenderer.on('nanodevices:event', (_event, eventid, deviceid, ...data) => {
return ipcRenderer.invoke('nanodevices:connect', deviceid) console.log('Event in ipcRenderer ', eventid, deviceid, data)
}, if (eventid_filter == '*' || eventid_filter == eventid) {
disconnect(deviceid) { callback(eventid, deviceid, ...data)
return ipcRenderer.invoke('nanodevices:disconnect', deviceid)
},
on_event(eventid_filter, callback) {
console.log('attaching filter for ', eventid_filter)
ipcRenderer.on('nanodevices:event', (_event, eventid, deviceid, ...data) => {
console.log('Event in ipcRenderer ', eventid, deviceid, data)
if (eventid_filter == '*' || eventid_filter == eventid) {
callback(eventid, deviceid, ...data)
}
})
},
send(deviceid, jsonstr) {
return ipcRenderer.invoke('nanodevices:send', deviceid, jsonstr)
} }
}) })
},
contextBridge.exposeInMainWorld('electron', { send(deviceid, jsonstr) {
platform: process.platform, return ipcRenderer.invoke('nanodevices:send', deviceid, jsonstr)
isDevelopment: process.env.NODE_ENV !== 'production',
minimizeWindow: () => ipcRenderer.send('electron:minimizeWindow'),
toggleMaximizeWindow: () => ipcRenderer.send('electron:toggleMaximizeWindow'),
closeWindow: () => ipcRenderer.send('electron:closeWindow'),
openExternal: (url) => ipcRenderer.send('electron:openExternal', url),
onMaximized: (callback) => ipcRenderer.on('electron:maximized', callback),
onUnmaximized: (callback) => ipcRenderer.on('electron:unmaximized', callback),
onMenu: (callback) =>
ipcRenderer.on('electron:menu', (event, key) => {
callback(key)
}),
openDevTools: () => ipcRenderer.send('electron:openDevTools'),
reload: () => ipcRenderer.send('electron:reload')
})
} catch (error) {
console.error(error)
} }
} else { })
// @ts-ignore (define in dts)
window.electron = electronAPI contextBridge.exposeInMainWorld('electronAPI', {
// @ts-ignore (define in dts) platform: process.platform,
window.api = api isDevelopment: process.env.NODE_ENV !== 'production',
} minimizeWindow: () => ipcRenderer.send('electron:minimizeWindow'),
toggleMaximizeWindow: () => ipcRenderer.send('electron:toggleMaximizeWindow'),
closeWindow: () => ipcRenderer.send('electron:closeWindow'),
openExternal: (url) => ipcRenderer.send('electron:openExternal', url),
onMaximized: (callback) => ipcRenderer.on('electron:maximized', callback),
onUnmaximized: (callback) => ipcRenderer.on('electron:unmaximized', callback),
onMenu: (callback) =>
ipcRenderer.on('electron:menu', (event, key) => {
callback(key)
}),
openDevTools: () => ipcRenderer.send('electron:openDevTools'),
reload: () => ipcRenderer.send('electron:reload')
})

View File

@@ -6,7 +6,7 @@ import Navbar from '@renderer/components/navbar/Navbar.vue'
import { useStore } from '@renderer/store' import { useStore } from '@renderer/store'
import { useMessageHandlers } from '@renderer/device' import { useMessageHandlers } from '@renderer/device'
const { electron } = window const { electronAPI, nanoDevicesAPI } = window
const store = useStore() const store = useStore()
const menuActions = { const menuActions = {
@@ -15,7 +15,7 @@ const menuActions = {
skin: () => store.switchPreviewDeviceModel() skin: () => store.switchPreviewDeviceModel()
} }
electron?.onMenu((key) => { electronAPI.onMenu((key) => {
console.log('menu', key) console.log('menu', key)
if (menuActions[key]) { if (menuActions[key]) {
menuActions[key]() menuActions[key]()
@@ -26,24 +26,20 @@ store.fetchProfiles() // TODO remove me!
// handle device events // handle device events
const handlers = useMessageHandlers(store) const handlers = useMessageHandlers(store)
window.nanodevices.on_event('device-attached', (evt, deviceid, data) => nanoDevicesAPI.on_event('device-attached', (evt, deviceid, data) => store.device_attached(deviceid))
store.device_attached(deviceid) nanoDevicesAPI.on_event('device-detached', (evt, deviceid, data) => store.device_detached(deviceid))
) nanoDevicesAPI.on_event('device-error', (evt, deviceid, data) => {
window.nanodevices.on_event('device-detached', (evt, deviceid, data) =>
store.device_detached(deviceid)
)
window.nanodevices.on_event('device-error', (evt, deviceid, data) => {
/* TODO handle connection errors */ /* TODO handle connection errors */
}) })
window.nanodevices.on_event('connected', (evt, deviceid, data) => store.device_connected(deviceid)) nanoDevicesAPI.on_event('connected', (evt, deviceid, data) => store.device_connected(deviceid))
window.nanodevices.on_event('disconnected', (evt, deviceid, data) => nanoDevicesAPI.on_event('disconnected', (evt, deviceid, data) =>
store.device_disconnected(deviceid) store.device_disconnected(deviceid)
) )
window.nanodevices.on_event('update', (evt, deviceid, data) => { nanoDevicesAPI.on_event('update', (evt, deviceid, data) => {
handlers.handle_message(data) handlers.handle_message(data)
}) })
// get list of the currently attached devices // get list of the currently attached devices
window.nanodevices.list_devices().then((devs) => store.init_devices(devs)) nanoDevicesAPI.list_devices().then((devs) => store.init_devices(devs))
</script> </script>
<template> <template>
<main class="flex h-screen w-screen select-none flex-col"> <main class="flex h-screen w-screen select-none flex-col">

View File

@@ -1,13 +0,0 @@
<script setup lang="ts">
import { reactive } from 'vue'
const versions = reactive({ ...window.electron.process.versions })
</script>
<template>
<ul class="versions">
<li class="electron-version">Electron v{{ versions.electron }}</li>
<li class="chrome-version">Chromium v{{ versions.chrome }}</li>
<li class="node-version">Node v{{ versions.node }}</li>
</ul>
</template>

View File

@@ -86,7 +86,7 @@
</MenubarMenu> </MenubarMenu>
<MenubarButton <MenubarButton
class="app-titlebar-button" class="app-titlebar-button"
@click="electron?.openExternal('https://discord.gg/jgRd77YN5T')" @click="electronAPI.openExternal('https://discord.gg/jgRd77YN5T')"
> >
Community Community
</MenubarButton> </MenubarButton>
@@ -104,10 +104,10 @@
<p>v0.1</p> <p>v0.1</p>
</MenubarItem> </MenubarItem>
<MenubarItem>Contact Support</MenubarItem> <MenubarItem>Contact Support</MenubarItem>
<template v-if="electron?.isDevelopment"> <template v-if="electronAPI.isDevelopment">
<MenubarSeparator /> <MenubarSeparator />
<MenubarItem @click="electron?.openDevTools">Developer Tools</MenubarItem> <MenubarItem @click="electronAPI.openDevTools">Developer Tools</MenubarItem>
<MenubarItem @click="electron?.reload">Reload</MenubarItem> <MenubarItem @click="electronAPI.reload">Reload</MenubarItem>
</template> </template>
</MenubarContent> </MenubarContent>
</MenubarMenu> </MenubarMenu>
@@ -124,21 +124,21 @@
<button <button
v-if="minimizable" v-if="minimizable"
class="app-titlebar-button flex grow items-center justify-center px-2 hover:text-white" class="app-titlebar-button flex grow items-center justify-center px-2 hover:text-white"
@click="electron?.minimizeWindow" @click="electronAPI.minimizeWindow"
> >
<Minus class="size-5" /> <Minus class="size-5" />
</button> </button>
<button <button
v-if="maximizable" v-if="maximizable"
class="app-titlebar-button flex grow items-center justify-center px-2 hover:text-white" class="app-titlebar-button flex grow items-center justify-center px-2 hover:text-white"
@click="electron?.toggleMaximizeWindow" @click="electronAPI.toggleMaximizeWindow"
> >
<Copy v-if="isMaximized" class="size-4" /> <Copy v-if="isMaximized" class="size-4" />
<Square v-else class="mr-0.5 size-3.5" /> <Square v-else class="mr-0.5 size-3.5" />
</button> </button>
<button <button
class="app-titlebar-button flex grow items-center justify-center px-2 hover:text-white" class="app-titlebar-button flex grow items-center justify-center px-2 hover:text-white"
@click="electron?.closeWindow" @click="electronAPI.closeWindow"
> >
<X class="mr-0.5 size-5" /> <X class="mr-0.5 size-5" />
</button> </button>
@@ -171,9 +171,9 @@ const showDisconnectButton = ref(false)
const isMaximized = ref(false) const isMaximized = ref(false)
const { electron } = window const { electronAPI } = window
const isMacOS = electron?.platform === 'darwin' const isMacOS = electronAPI.platform === 'darwin'
const zoomFactor = ref(1) const zoomFactor = ref(1)
const previewDeviceNames = ref({ const previewDeviceNames = ref({
@@ -185,12 +185,12 @@ onMounted(() => {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
zoomFactor.value = window.outerWidth / window.innerWidth zoomFactor.value = window.outerWidth / window.innerWidth
}) })
electron?.onMaximized((maximized) => { electronAPI.onMaximized((maximized) => {
console.log(maximized) console.log(maximized)
isMaximized.value = true isMaximized.value = true
}) })
electron?.onUnmaximized(() => { electronAPI.onUnmaximized(() => {
isMaximized.value = false isMaximized.value = false
}) })
}) })

View File

@@ -20,10 +20,8 @@ const app = createApp(App)
app.use(pinia) app.use(pinia)
app.use(i18n) app.use(i18n)
app.config.globalProperties.window = window
// TODO remove this // TODO remove this
window.nanodevices.on_event('*', (eventid, deviceid, ...data) => { window.nanoDevicesAPI.on_event('*', (eventid, deviceid, ...data) => {
console.log('Event on window ', eventid, deviceid, data) console.log('Event on window ', eventid, deviceid, data)
}) })

View File

@@ -211,7 +211,7 @@ export const useStore = defineStore('main', {
// TODO auto-connect to the device // TODO auto-connect to the device
const deviceid = Object.keys(this.devices)[0] const deviceid = Object.keys(this.devices)[0]
console.log('Auto-connecting to device ', deviceid) console.log('Auto-connecting to device ', deviceid)
window.nanodevices.connect(deviceid) window.nanoDevicesAPI.connect(deviceid)
} }
}, },
update_devices(deviceid, attached) { update_devices(deviceid, attached) {
@@ -227,7 +227,7 @@ export const useStore = defineStore('main', {
if (Object.keys(this.devices).length == 1) { if (Object.keys(this.devices).length == 1) {
// TODO auto-connect to the device // TODO auto-connect to the device
console.log('Auto-connecting to device ', deviceid) console.log('Auto-connecting to device ', deviceid)
window.nanodevices.connect(deviceid) window.nanoDevicesAPI.connect(deviceid)
} }
}, },
device_detached(deviceid) { device_detached(deviceid) {