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 {
list_devices(): Promise<string[]>
listAttachedDevices(): 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
on(callback: (eventid: string, deviceid: string, data: any) => void): void
send(deviceid: string, jsonstr: string): Promise<void>
save(deviceid: string): Promise<void>
}
@@ -26,7 +23,7 @@ export interface IElectronApi {
declare global {
interface Window {
nanoSerialApi: INanoSerialApi
electronApi: IElectronApi
nanoIpc: INanoSerialApi
appIpc: IElectronApi
}
}

View File

@@ -1,8 +1,8 @@
import { contextBridge, ipcRenderer } from 'electron'
// expose an API to choose available devices
contextBridge.exposeInMainWorld('nanoSerialApi', {
listConnectedDevices() {
contextBridge.exposeInMainWorld('nanoIpc', {
listAttachedDevices() {
return ipcRenderer.invoke('nanoSerialApi:list_devices')
},
connect(deviceid) {
@@ -21,7 +21,7 @@ contextBridge.exposeInMainWorld('nanoSerialApi', {
}
})
contextBridge.exposeInMainWorld('electronApi', {
contextBridge.exposeInMainWorld('appIpc', {
platform: process.platform,
isDevelopment: process.env.NODE_ENV !== 'production',
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 ConfigPane from '@renderer/components/config/ConfigPane.vue'
import Navbar from '@renderer/components/navbar/Navbar.vue'
import { useDeviceStore } from '@renderer/deviceStore'
import { useDeviceStore, initializeDevices } from '@renderer/deviceStore'
const deviceStore = useDeviceStore()
initializeDevices()
// const menuActions = {
// connect: () => store.setConnected(!store.connected),
@@ -13,7 +14,7 @@ const deviceStore = useDeviceStore()
// skin: () => store.switchPreviewDeviceModel()
// }
// electronApi.onMenu((key) => {
// appIpc.onMenu((key) => {
// console.log('menu', key)
// if (menuActions[key]) {
// menuActions[key]()
@@ -24,18 +25,18 @@ const deviceStore = useDeviceStore()
// handle device events
// const handlers = useMessageHandlers(store)
// nanoSerialApi.on_event('device-attached', (evt, deviceid, data) => store.device_attached(deviceid))
// nanoSerialApi.on_event('device-detached', (evt, deviceid, data) => store.device_detached(deviceid))
// nanoSerialApi.on_event('device-error', (evt, deviceid, data) => {
// nanoIpc.on_event('device-attached', (evt, deviceid, data) => store.device_attached(deviceid))
// nanoIpc.on_event('device-detached', (evt, deviceid, data) => store.device_detached(deviceid))
// nanoIpc.on_event('device-error', (evt, deviceid, data) => {
// /* TODO handle connection errors */
// })
// nanoSerialApi.on_event('connected', (evt, deviceid, data) => store.device_connected(deviceid))
// nanoSerialApi.on_event('disconnected', (evt, deviceid, data) => store.device_disconnected(deviceid))
// nanoSerialApi.on_event('update', (evt, deviceid, data) => {
// nanoIpc.on_event('connected', (evt, deviceid, data) => store.device_connected(deviceid))
// nanoIpc.on_event('disconnected', (evt, deviceid, data) => store.device_disconnected(deviceid))
// nanoIpc.on_event('update', (evt, deviceid, data) => {
// handlers.handle_message(data)
// })
// // 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>
<template>
<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
// const props = {}
// 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) {
// // this.selectedProfile.keys[this.selectedKey].pressed = color
// const props = {}
// 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
@@ -204,7 +204,7 @@ export const useAppStore = defineStore('app', {
// // TODO auto-connect to the device
// const deviceid = Object.keys(this.devices)[0]
// console.log('Auto-connecting to device ', deviceid)
// window.nanoSerialApi.connect(deviceid)
// window.nanoIpc.connect(deviceid)
// }
// },
// update_devices(deviceid, attached) {
@@ -220,7 +220,7 @@ export const useAppStore = defineStore('app', {
// if (Object.keys(this.devices).length == 1) {
// // TODO auto-connect to the device
// console.log('Auto-connecting to device ', deviceid)
// window.nanoSerialApi.connect(deviceid)
// window.nanoIpc.connect(deviceid)
// }
// },
// device_detached(deviceid) {
@@ -236,10 +236,10 @@ export const useAppStore = defineStore('app', {
// this.connected = true
// this.connectedId = deviceid
// // 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
// // 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?
// // or only on demand?

View File

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

View File

@@ -58,8 +58,7 @@
<draggable
key="categoriesDraggable"
group="profileCategories"
item-key="name"
:list="deviceStore.profileCategories"
:list="deviceStore.profileTags"
v-bind="dragOptions"
handle=".category-handle"
@start="drag = true"
@@ -67,7 +66,7 @@
@change="onCategoryDrop"
>
<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 -->
<CollapsibleTrigger
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
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">
({{ 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">
<GripHorizontal class="category-handle mb-0.5 inline-block size-4" />
@@ -88,7 +87,7 @@
key="profilesDraggable"
group="profiles"
item-key="id"
:list="dragCategory.element.profiles"
:list="deviceStore.profilesByTag[dragCategory.element]"
v-bind="dragOptions"
handle=".profile-handle"
@start="drag = true"

View File

@@ -36,14 +36,14 @@ interface Profile {
guiEnable: boolean
}
const { nanoSerialApi } = window
const { nanoIpc } = window
export const useDeviceStore = defineStore('device', {
state: () => ({
attachedDeviceIds: [] as string[], // list of attached device ids
currentDeviceId: null as string | null, // id of the current device
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
orientation: 0 as number, // orientation of the device
dirtyState: false as boolean, // whether the device state has changed
@@ -55,22 +55,47 @@ export const useDeviceStore = defineStore('device', {
getters: {
connected: (state) => state.currentDeviceId !== null,
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: {
setAttachedDeviceIds(deviceIds: string[]) {
this.attachedDeviceIds = deviceIds
},
setConnected(connected: boolean) {
// TODO: This is here for compatibility, but it should be removed
// Real connect calls would need to know the last device id
// Maybe that should be stored here
// Then connect connects to the last device id of falls back to the first
attachDevice(deviceId: string) {
if (!this.attachedDeviceIds.includes(deviceId)) {
this.attachedDeviceIds.push(deviceId)
}
},
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) {
this.currentProfileName = profileName
if (updateDevice) {
nanoSerialApi.send(this.currentDeviceId!, JSON.stringify({ current: profileName }))
nanoIpc.send(this.currentDeviceId!, JSON.stringify({ current: profileName }))
}
},
setOrientation(orientation: number, updateDevice: boolean = true) {
@@ -82,6 +107,101 @@ export const useDeviceStore = defineStore('device', {
},
cycleOrientation() {
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)
// 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)
// })