UPD: Move some functionality to new stores

This commit is contained in:
Robert Kossessa
2024-03-12 14:34:31 +01:00
parent 974beee118
commit fb903f194a
8 changed files with 175 additions and 58 deletions

View File

@@ -1,11 +1,8 @@
export interface INanoSerialApi { export interface INanoSerialApi {
list_devices(): Promise<string[]> listAttachedDevices(): Promise<string[]>
connect(deviceid: string): Promise<string> connect(deviceid: string): Promise<string>
disconnect(deviceid: string): Promise<string> disconnect(deviceid: string): Promise<string>
on_event( on(callback: (eventid: string, deviceid: string, data: any) => void): void
eventid_filter: string,
callback: (eventid: string, deviceid: string, data: any) => void
): void
send(deviceid: string, jsonstr: string): Promise<void> send(deviceid: string, jsonstr: string): Promise<void>
save(deviceid: string): Promise<void> save(deviceid: string): Promise<void>
} }
@@ -26,7 +23,7 @@ export interface IElectronApi {
declare global { declare global {
interface Window { interface Window {
nanoSerialApi: INanoSerialApi nanoIpc: INanoSerialApi
electronApi: IElectronApi appIpc: IElectronApi
} }
} }

View File

@@ -1,8 +1,8 @@
import { contextBridge, ipcRenderer } from 'electron' import { contextBridge, ipcRenderer } from 'electron'
// expose an API to choose available devices // expose an API to choose available devices
contextBridge.exposeInMainWorld('nanoSerialApi', { contextBridge.exposeInMainWorld('nanoIpc', {
listConnectedDevices() { listAttachedDevices() {
return ipcRenderer.invoke('nanoSerialApi:list_devices') return ipcRenderer.invoke('nanoSerialApi:list_devices')
}, },
connect(deviceid) { connect(deviceid) {
@@ -21,7 +21,7 @@ contextBridge.exposeInMainWorld('nanoSerialApi', {
} }
}) })
contextBridge.exposeInMainWorld('electronApi', { contextBridge.exposeInMainWorld('appIpc', {
platform: process.platform, platform: process.platform,
isDevelopment: process.env.NODE_ENV !== 'production', isDevelopment: process.env.NODE_ENV !== 'production',
minimizeWindow: () => ipcRenderer.send('electron:minimizeWindow'), minimizeWindow: () => ipcRenderer.send('electron:minimizeWindow'),

View File

@@ -3,9 +3,10 @@ import ProfileManager from '@renderer/components/profile/ProfileManager.vue'
import DevicePreview from '@renderer/components/device/DevicePreview.vue' import DevicePreview from '@renderer/components/device/DevicePreview.vue'
import ConfigPane from '@renderer/components/config/ConfigPane.vue' import ConfigPane from '@renderer/components/config/ConfigPane.vue'
import Navbar from '@renderer/components/navbar/Navbar.vue' import Navbar from '@renderer/components/navbar/Navbar.vue'
import { useDeviceStore } from '@renderer/deviceStore' import { useDeviceStore, initializeDevices } from '@renderer/deviceStore'
const deviceStore = useDeviceStore() const deviceStore = useDeviceStore()
initializeDevices()
// const menuActions = { // const menuActions = {
// connect: () => store.setConnected(!store.connected), // connect: () => store.setConnected(!store.connected),
@@ -13,7 +14,7 @@ const deviceStore = useDeviceStore()
// skin: () => store.switchPreviewDeviceModel() // skin: () => store.switchPreviewDeviceModel()
// } // }
// electronApi.onMenu((key) => { // appIpc.onMenu((key) => {
// console.log('menu', key) // console.log('menu', key)
// if (menuActions[key]) { // if (menuActions[key]) {
// menuActions[key]() // menuActions[key]()
@@ -24,18 +25,18 @@ const deviceStore = useDeviceStore()
// handle device events // handle device events
// const handlers = useMessageHandlers(store) // const handlers = useMessageHandlers(store)
// nanoSerialApi.on_event('device-attached', (evt, deviceid, data) => store.device_attached(deviceid)) // nanoIpc.on_event('device-attached', (evt, deviceid, data) => store.device_attached(deviceid))
// nanoSerialApi.on_event('device-detached', (evt, deviceid, data) => store.device_detached(deviceid)) // nanoIpc.on_event('device-detached', (evt, deviceid, data) => store.device_detached(deviceid))
// nanoSerialApi.on_event('device-error', (evt, deviceid, data) => { // nanoIpc.on_event('device-error', (evt, deviceid, data) => {
// /* TODO handle connection errors */ // /* TODO handle connection errors */
// }) // })
// nanoSerialApi.on_event('connected', (evt, deviceid, data) => store.device_connected(deviceid)) // nanoIpc.on_event('connected', (evt, deviceid, data) => store.device_connected(deviceid))
// nanoSerialApi.on_event('disconnected', (evt, deviceid, data) => store.device_disconnected(deviceid)) // nanoIpc.on_event('disconnected', (evt, deviceid, data) => store.device_disconnected(deviceid))
// nanoSerialApi.on_event('update', (evt, deviceid, data) => { // nanoIpc.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
// nanoSerialApi.list_devices().then((devs) => store.init_devices(devs)) // nanoIpc.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

@@ -187,13 +187,13 @@ export const useAppStore = defineStore('app', {
// // this.selectedProfile.keys[this.selectedKey].default = color // // this.selectedProfile.keys[this.selectedKey].default = color
// const props = {} // const props = {}
// props[`button${this.selectedKey.toUpperCase()}Idle`] = color.rgbNumber() // props[`button${this.selectedKey.toUpperCase()}Idle`] = color.rgbNumber()
// nanoSerialApi.send(this.connectedId, { p: { name: 'Default Profile', ...props } }) // nanoIpc.send(this.connectedId, { p: { name: 'Default Profile', ...props } })
// }, // },
// setKeyPressedColor(color) { // setKeyPressedColor(color) {
// // this.selectedProfile.keys[this.selectedKey].pressed = color // // this.selectedProfile.keys[this.selectedKey].pressed = color
// const props = {} // const props = {}
// props[`button${this.selectedKey.toUpperCase()}Press`] = color.rgbNumber() // props[`button${this.selectedKey.toUpperCase()}Press`] = color.rgbNumber()
// nanoSerialApi.send(this.connectedId, { p: { name: 'Default Profile', ...props } }) // nanoIpc.send(this.connectedId, { p: { name: 'Default Profile', ...props } })
// }, // },
// // devices, device attachment, connection, and disconnection // // devices, device attachment, connection, and disconnection
@@ -204,7 +204,7 @@ export const useAppStore = defineStore('app', {
// // 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.nanoSerialApi.connect(deviceid) // window.nanoIpc.connect(deviceid)
// } // }
// }, // },
// update_devices(deviceid, attached) { // update_devices(deviceid, attached) {
@@ -220,7 +220,7 @@ export const useAppStore = defineStore('app', {
// 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.nanoSerialApi.connect(deviceid) // window.nanoIpc.connect(deviceid)
// } // }
// }, // },
// device_detached(deviceid) { // device_detached(deviceid) {
@@ -236,10 +236,10 @@ export const useAppStore = defineStore('app', {
// this.connected = true // this.connected = true
// this.connectedId = deviceid // this.connectedId = deviceid
// // TODO load profiles from device // // TODO load profiles from device
// // nanoSerialApi.send(deviceid, { profiles: "#all" }) // request profiles // // nanoIpc.send(deviceid, { profiles: "#all" }) // request profiles
// // "Default Profile", for now, is the only profile after the device // // "Default Profile", for now, is the only profile after the device
// // starts up, so it is also the current (eg. 'selected') profile // // starts up, so it is also the current (eg. 'selected') profile
// // nanoSerialApi.send(deviceid, { p: "Default Profile" }) // request Default Profile // // nanoIpc.send(deviceid, { p: "Default Profile" }) // request Default Profile
// // TODO maybe you want to request all the profiles right now? // // TODO maybe you want to request all the profiles right now?
// // or only on demand? // // or only on demand?

View File

@@ -90,32 +90,32 @@
</MenubarMenu> </MenubarMenu>
<MenubarButton <MenubarButton
class="app-titlebar-button" class="app-titlebar-button"
@click="electronApi.openExternal('https://discord.gg/jgRd77YN5T')" @click="appIpc.openExternal('https://discord.gg/jgRd77YN5T')"
> >
Community Community
</MenubarButton> </MenubarButton>
<MenubarMenu> <MenubarMenu>
<MenubarTrigger class="app-titlebar-button">Help</MenubarTrigger> <MenubarTrigger class="app-titlebar-button">Help</MenubarTrigger>
<MenubarContent> <MenubarContent>
<MenubarItem @click="electronApi.openExternal('https://github.com/katbinaris/zeroone')" <MenubarItem @click="appIpc.openExternal('https://github.com/katbinaris/zeroone')"
>Software Source</MenubarItem >Software Source</MenubarItem
> >
<MenubarItem <MenubarItem
@click="electronApi.openExternal('https://github.com/katbinaris/NanoD_RatchetH1')" @click="appIpc.openExternal('https://github.com/katbinaris/NanoD_RatchetH1')"
>Firmware Source</MenubarItem >Firmware Source</MenubarItem
> >
<MenubarItem <MenubarItem
@click="electronApi.openExternal('https://github.com/katbinaris/Nano_D_PlusPlus')" @click="appIpc.openExternal('https://github.com/katbinaris/Nano_D_PlusPlus')"
>Hardware Source</MenubarItem >Hardware Source</MenubarItem
> >
<MenubarSeparator /> <MenubarSeparator />
<MenubarItem <MenubarItem
@click="electronApi.openExternal('https://github.com/katbinaris/zeroone/issues/new')" @click="appIpc.openExternal('https://github.com/katbinaris/zeroone/issues/new')"
>Report Software Issue</MenubarItem >Report Software Issue</MenubarItem
> >
<MenubarItem <MenubarItem
@click=" @click="
electronApi.openExternal('https://github.com/katbinaris/NanoD_RatchetH1/issues/new') appIpc.openExternal('https://github.com/katbinaris/NanoD_RatchetH1/issues/new')
" "
>Report Device Issue</MenubarItem >Report Device Issue</MenubarItem
> >
@@ -124,13 +124,13 @@
<p>Software Version:&nbsp;</p> <p>Software Version:&nbsp;</p>
<p>v0.1</p> <p>v0.1</p>
</MenubarItem> </MenubarItem>
<MenubarItem @click="electronApi.openExternal('https://discord.gg/jgRd77YN5T')" <MenubarItem @click="appIpc.openExternal('https://discord.gg/jgRd77YN5T')"
>Contact Support</MenubarItem >Contact Support</MenubarItem
> >
<template v-if="electronApi.isDevelopment"> <template v-if="appIpc.isDevelopment">
<MenubarSeparator /> <MenubarSeparator />
<MenubarItem @click="electronApi.openDevTools">Developer Tools</MenubarItem> <MenubarItem @click="appIpc.openDevTools">Developer Tools</MenubarItem>
<MenubarItem @click="electronApi.reload">Reload</MenubarItem> <MenubarItem @click="appIpc.reload">Reload</MenubarItem>
</template> </template>
</MenubarContent> </MenubarContent>
</MenubarMenu> </MenubarMenu>
@@ -166,21 +166,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="electronApi.minimizeWindow" @click="appIpc.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="electronApi.toggleMaximizeWindow" @click="appIpc.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="electronApi.closeWindow" @click="appIpc.closeWindow"
> >
<X class="mr-0.5 size-5" /> <X class="mr-0.5 size-5" />
</button> </button>
@@ -215,9 +215,9 @@ const showDisconnectButton = ref(false)
const isMaximized = ref(false) const isMaximized = ref(false)
const { electronApi } = window const { appIpc } = window
const isMacOS = electronApi.platform === 'darwin' const isMacOS = appIpc.platform === 'darwin'
const zoomFactor = ref(1) const zoomFactor = ref(1)
const numberOfChanges = ref(27) const numberOfChanges = ref(27)
@@ -231,12 +231,12 @@ onMounted(() => {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
zoomFactor.value = window.outerWidth / window.innerWidth zoomFactor.value = window.outerWidth / window.innerWidth
}) })
electronApi.onMaximized((maximized) => { appIpc.onMaximized((maximized) => {
console.log(maximized) console.log(maximized)
isMaximized.value = true isMaximized.value = true
}) })
electronApi.onUnmaximized(() => { appIpc.onUnmaximized(() => {
isMaximized.value = false isMaximized.value = false
}) })
}) })

View File

@@ -58,8 +58,7 @@
<draggable <draggable
key="categoriesDraggable" key="categoriesDraggable"
group="profileCategories" group="profileCategories"
item-key="name" :list="deviceStore.profileTags"
:list="deviceStore.profileCategories"
v-bind="dragOptions" v-bind="dragOptions"
handle=".category-handle" handle=".category-handle"
@start="drag = true" @start="drag = true"
@@ -67,7 +66,7 @@
@change="onCategoryDrop" @change="onCategoryDrop"
> >
<template #item="dragCategory"> <template #item="dragCategory">
<Collapsible v-model:open="collapse[dragCategory.element.name]" :default-open="true"> <Collapsible v-model:open="collapse[dragCategory.element]" :default-open="true">
<!-- TODO: Make profile groups computed instead defining them of using v-for --> <!-- TODO: Make profile groups computed instead defining them of using v-for -->
<CollapsibleTrigger <CollapsibleTrigger
class="group h-12 w-full border-0 border-b bg-zinc-900 py-2 text-left text-sm text-muted-foreground" class="group h-12 w-full border-0 border-b bg-zinc-900 py-2 text-left text-sm text-muted-foreground"
@@ -75,9 +74,9 @@
<ChevronRight <ChevronRight
class="chevrot mb-0.5 ml-4 inline-block size-4 transition-transform" class="chevrot mb-0.5 ml-4 inline-block size-4 transition-transform"
/> />
{{ dragCategory.element.name {{ dragCategory.element
}}<span class="font-heading text-sm text-zinc-600"> }}<span class="font-heading text-sm text-zinc-600">
({{ dragCategory.element.profiles?.length || 0 }})</span ({{ deviceStore.profilesByTag[dragCategory.element].length || 0 }})</span
> >
<span class="float-right mx-4 w-4 cursor-grab text-zinc-600"> <span class="float-right mx-4 w-4 cursor-grab text-zinc-600">
<GripHorizontal class="category-handle mb-0.5 inline-block size-4" /> <GripHorizontal class="category-handle mb-0.5 inline-block size-4" />
@@ -88,7 +87,7 @@
key="profilesDraggable" key="profilesDraggable"
group="profiles" group="profiles"
item-key="id" item-key="id"
:list="dragCategory.element.profiles" :list="deviceStore.profilesByTag[dragCategory.element]"
v-bind="dragOptions" v-bind="dragOptions"
handle=".profile-handle" handle=".profile-handle"
@start="drag = true" @start="drag = true"

View File

@@ -36,14 +36,14 @@ interface Profile {
guiEnable: boolean guiEnable: boolean
} }
const { nanoSerialApi } = window const { nanoIpc } = window
export const useDeviceStore = defineStore('device', { export const useDeviceStore = defineStore('device', {
state: () => ({ state: () => ({
attachedDeviceIds: [] as string[], // list of attached device ids attachedDeviceIds: [] as string[], // list of attached device ids
currentDeviceId: null as string | null, // id of the current device currentDeviceId: null as string | null, // id of the current device
profileNames: [] as string[], // list of profile names profileNames: [] as string[], // list of profile names
profiles: {} as Record<string, Profile>, // map of profiles by name profiles: [] as Profile[], // list of profiles
currentProfileName: null as string | null, // name of the current profile currentProfileName: null as string | null, // name of the current profile
orientation: 0 as number, // orientation of the device orientation: 0 as number, // orientation of the device
dirtyState: false as boolean, // whether the device state has changed dirtyState: false as boolean, // whether the device state has changed
@@ -55,22 +55,47 @@ export const useDeviceStore = defineStore('device', {
getters: { getters: {
connected: (state) => state.currentDeviceId !== null, connected: (state) => state.currentDeviceId !== null,
currentProfile: (state) => currentProfile: (state) =>
state.currentProfileName ? state.profiles[state.currentProfileName] : null state.currentProfileName
? state.profiles.find((profile) => profile.name === state.currentProfileName)
: null,
profileTags: (state) => state.profiles.map((profile) => profile.profileTag),
profilesByTag: (state) =>
state.profiles.reduce((acc, profile) => {
acc[profile.profileTag] = profile
return acc
}, {})
}, },
actions: { actions: {
setAttachedDeviceIds(deviceIds: string[]) { setAttachedDeviceIds(deviceIds: string[]) {
this.attachedDeviceIds = deviceIds this.attachedDeviceIds = deviceIds
}, },
setConnected(connected: boolean) { attachDevice(deviceId: string) {
// TODO: This is here for compatibility, but it should be removed if (!this.attachedDeviceIds.includes(deviceId)) {
// Real connect calls would need to know the last device id this.attachedDeviceIds.push(deviceId)
// Maybe that should be stored here }
// Then connect connects to the last device id of falls back to the first },
detachDevice(deviceId: string) {
const index = this.attachedDeviceIds.indexOf(deviceId)
if (index !== -1) {
this.attachedDeviceIds.splice(index, 1)
}
},
connectDevice(deviceId: string, updateDevice: boolean = true) {
this.currentDeviceId = deviceId
if (updateDevice) {
nanoIpc.connect(deviceId)
}
},
disconnectDevice(deviceId: string, updateDevice: boolean = true) {
this.currentDeviceId = null
if (updateDevice) {
nanoIpc.disconnect(deviceId)
}
}, },
setCurrentProfile(profileName: string, updateDevice: boolean = true) { setCurrentProfile(profileName: string, updateDevice: boolean = true) {
this.currentProfileName = profileName this.currentProfileName = profileName
if (updateDevice) { if (updateDevice) {
nanoSerialApi.send(this.currentDeviceId!, JSON.stringify({ current: profileName })) nanoIpc.send(this.currentDeviceId!, JSON.stringify({ current: profileName }))
} }
}, },
setOrientation(orientation: number, updateDevice: boolean = true) { setOrientation(orientation: number, updateDevice: boolean = true) {
@@ -82,6 +107,101 @@ export const useDeviceStore = defineStore('device', {
}, },
cycleOrientation() { cycleOrientation() {
this.setOrientation((this.orientation + 90) % 360) this.setOrientation((this.orientation + 90) % 360)
},
setAngle(angle: number) {
this.angle = angle
} }
} }
}) })
export const initializeDevices = () => {
const deviceStore = useDeviceStore()
// register event handlers
nanoIpc.on((eventid, deviceid, ...data) => {
console.log('Received event', eventid, deviceid, data)
if (eventid === 'device-attached') {
deviceStore.attachDevice(deviceid)
if (deviceStore.attachedDeviceIds.length === 1) {
deviceStore.connectDevice(deviceid)
}
}
if (eventid === 'device-detached') {
deviceStore.detachDevice(deviceid)
}
if (eventid === 'device-connected') {
deviceStore.connectDevice(deviceid, false)
}
if (eventid === 'device-disconnected') {
deviceStore.disconnectDevice(deviceid, false)
}
if (eventid === 'update') {
if ('a' in data) {
deviceStore.setAngle(data.a as number)
}
}
})
// get initial device list
nanoIpc.listAttachedDevices().then((deviceIds) => {
deviceStore.setAttachedDeviceIds(deviceIds)
if (deviceIds.length > 0) {
nanoIpc.connect(deviceIds[0])
}
})
}
// // devices, device attachment, connection, and disconnection
// init_devices(ids) {
// console.log('Initializing devices: ', ids)
// for (const id of ids) this.update_devices(id, true)
// if (Object.keys(this.devices).length == 1) {
// // TODO auto-connect to the device
// const deviceid = Object.keys(this.devices)[0]
// console.log('Auto-connecting to device ', deviceid)
// window.nanoIpc.connect(deviceid)
// }
// },
// update_devices(deviceid, attached) {
// if (attached) {
// if (!this.devices.hasOwnProperty(deviceid))
// this.devices[deviceid] = { serialNumber: deviceid, connected: false }
// } else {
// if (this.devices.hasOwnProperty(deviceid)) delete this.devices[deviceid] // TODO maybe mark as detached instead of deleting? then we can remember its name, etc...
// }
// },
// device_attached(deviceid) {
// this.update_devices(deviceid, true)
// if (Object.keys(this.devices).length == 1) {
// // TODO auto-connect to the device
// console.log('Auto-connecting to device ', deviceid)
// window.nanoIpc.connect(deviceid)
// }
// },
// device_detached(deviceid) {
// if (this.devices[deviceid].connected) {
// // detached event arrived before disconnected event?
// this.devices[deviceid].connected = false
// this.connected = false
// }
// this.update_devices(deviceid, false)
// },
// device_connected(deviceid) {
// this.devices[deviceid].connected = true
// this.connected = true
// this.connectedId = deviceid
// // TODO load profiles from device
// // nanoIpc.send(deviceid, { profiles: "#all" }) // request profiles
// // "Default Profile", for now, is the only profile after the device
// // starts up, so it is also the current (eg. 'selected') profile
// // nanoIpc.send(deviceid, { p: "Default Profile" }) // request Default Profile
// // TODO maybe you want to request all the profiles right now?
// // or only on demand?
// },
// device_disconnected(deviceid) {
// this.devices[deviceid].connected = false
// this.connected = false
// this.connectedId = null
// // TODO switch UI to disconnected state
// },

View File

@@ -23,7 +23,7 @@ app.use(pinia)
app.use(i18n) app.use(i18n)
// TODO remove this // TODO remove this
// window.nanoSerialApi.on_event('*', (eventid, deviceid, ...data) => { // window.nanoIpc.on_event('*', (eventid, deviceid, ...data) => {
// console.log('Event on window ', eventid, deviceid, data) // console.log('Event on window ', eventid, deviceid, data)
// }) // })