Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow saving widget profiles on the vehicle #566

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/components/EditMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@
<span class="mdi mdi-upload" />
</label>
</div>
<div
v-tooltip="'Export profiles to vehicle.'"
class="icon-btn mdi mdi-briefcase-upload"
@click="store.exportProfilesToVehicle"
/>
<div
v-tooltip="'Import profiles from vehicle.'"
class="icon-btn mdi mdi-briefcase-download"
@click="store.importProfilesFromVehicle"
/>
</div>
</div>
<div class="w-full h-px my-2 sm bg-slate-800/40" />
Expand Down
75 changes: 75 additions & 0 deletions src/libs/blueos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export const getBagOfHoldingFromVehicle = async (
vehicleAddress: string,
bagName: string
): Promise<Record<string, any>> => {
try {
const response = await fetch(`http://${vehicleAddress}/bag/v1.0/get/${bagName}`)
if (!(await response.ok)) {
throw new Error(await response.text())
}
return await response.json()
} catch (error) {
throw new Error(`Could not get bag of holdings for ${bagName}. ${error}`)
}
}

export const getCockpitStorageFromVehicle = async (vehicleAddress: string): Promise<Record<string, any>> => {
try {
return await getBagOfHoldingFromVehicle(vehicleAddress, 'cockpit')
} catch (error) {
throw new Error(`Could not get Cockpit's storage data from vehicle. ${error}`)
}
}

export const getKeyDataFromCockpitVehicleStorage = async (
vehicleAddress: string,
storageKey: string
): Promise<Record<string, any> | undefined> => {
const cockpitVehicleStorage = await getCockpitStorageFromVehicle(vehicleAddress)
return cockpitVehicleStorage[storageKey]
}

export const setBagOfHoldingOnVehicle = async (
vehicleAddress: string,
bagName: string,
bagData: Record<string, any> | any
): Promise<void> => {
try {
await fetch(`http://${vehicleAddress}/bag/v1.0/set/${bagName}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(bagData),
})
} catch (error) {
throw new Error(`Could not set bag of holdings for ${bagName}. ${error}`)
}
}

export const setCockpitStorageOnVehicle = async (
vehicleAddress: string,
storageData: Record<string, any> | any
): Promise<void> => {
try {
await setBagOfHoldingOnVehicle(vehicleAddress, 'cockpit', storageData)
} catch (error) {
throw new Error(`Could not set Cockpit's storage data on vehicle. ${error}`)
}
}

export const setKeyDataOnCockpitVehicleStorage = async (
vehicleAddress: string,
storageKey: string,
storageData: Record<string, any> | any
): Promise<void> => {
let previousVehicleStorage: Record<string, any> = {}
try {
previousVehicleStorage = await getCockpitStorageFromVehicle(vehicleAddress)
} catch (error) {
console.error(error)
}
const newVehicleStorage = previousVehicleStorage
newVehicleStorage[storageKey] = storageData

await setCockpitStorageOnVehicle(vehicleAddress, newVehicleStorage)
}
31 changes: 30 additions & 1 deletion src/stores/widgetManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import { computed, onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'

import { widgetProfiles } from '@/assets/defaults'
import { miniWidgetsProfile } from '@/assets/defaults'
import { getKeyDataFromCockpitVehicleStorage, setKeyDataOnCockpitVehicleStorage } from '@/libs/blueos'
import * as Words from '@/libs/funny-name/words'
import { CockpitAction, registerActionCallback, unregisterActionCallback } from '@/libs/joystick/protocols'
import { isEqual } from '@/libs/utils'
import type { Point2D, SizeRect2D } from '@/types/general'
import type { MiniWidget, MiniWidgetContainer } from '@/types/miniWidgets'
import { type Profile, type View, type Widget, isProfile, isView, WidgetType } from '@/types/widgets'

import { useMainVehicleStore } from './mainVehicle'

export const useWidgetManagerStore = defineStore('widget-manager', () => {
const vehicleStore = useMainVehicleStore()
const editingMode = ref(false)
const showGrid = ref(true)
const gridInterval = ref(0.01)
Expand Down Expand Up @@ -159,6 +163,28 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => {
reader.readAsText(e.target.files[0])
}

const importProfilesFromVehicle = async (): Promise<void> => {
const newProfiles = await getKeyDataFromCockpitVehicleStorage(
vehicleStore.globalAddress,
'cockpit-saved-profiles-v7'
)
if (!Array.isArray(newProfiles) || !newProfiles.every((profile) => isProfile(profile))) {
Swal.fire({ icon: 'error', text: 'Could not import profiles from vehicle. Invalid data.', timer: 3000 })
return
}
savedProfiles.value = newProfiles
Swal.fire({ icon: 'success', text: 'Cockpit profiles imported from vehicle.', timer: 3000 })
}

const exportProfilesToVehicle = async (): Promise<void> => {
await setKeyDataOnCockpitVehicleStorage(
vehicleStore.globalAddress,
'cockpit-saved-profiles-v7',
savedProfiles.value
)
Swal.fire({ icon: 'success', text: 'Cockpit profiles exported to vehicle.', timer: 3000 })
}

/**
* Adds new view to the store, with a randomly generated hash with UUID4 pattern
*/
Expand Down Expand Up @@ -396,8 +422,9 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => {
}
}

// If the user does not have it's own profiles yet, create them
// If the user does not have it's own profiles yet, try to fetch them from the vehicle, and if it fails, create default ones
if (savedProfiles.value.isEmpty()) {
importProfilesFromVehicle()
widgetProfiles.forEach((profile) => {
// @ts-ignore: structuredClone is a thing since a long time ago
const userProfile = structuredClone(profile)
Expand Down Expand Up @@ -483,5 +510,7 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => {
openWidgetConfigMenu,
toggleFullScreen,
isFullScreen,
importProfilesFromVehicle,
exportProfilesToVehicle,
}
})
Loading