diff --git a/src/components/EditMenu.vue b/src/components/EditMenu.vue index f67424c96..af3572047 100644 --- a/src/components/EditMenu.vue +++ b/src/components/EditMenu.vue @@ -43,6 +43,16 @@ +
+
diff --git a/src/libs/blueos.ts b/src/libs/blueos.ts new file mode 100644 index 000000000..3abdc7195 --- /dev/null +++ b/src/libs/blueos.ts @@ -0,0 +1,75 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +export const getBagOfHoldingFromVehicle = async ( + vehicleAddress: string, + bagName: string +): Promise> => { + 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> => { + 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 | undefined> => { + const cockpitVehicleStorage = await getCockpitStorageFromVehicle(vehicleAddress) + return cockpitVehicleStorage[storageKey] +} + +export const setBagOfHoldingOnVehicle = async ( + vehicleAddress: string, + bagName: string, + bagData: Record | any +): Promise => { + 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 | any +): Promise => { + 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 | any +): Promise => { + let previousVehicleStorage: Record = {} + try { + previousVehicleStorage = await getCockpitStorageFromVehicle(vehicleAddress) + } catch (error) { + console.error(error) + } + const newVehicleStorage = previousVehicleStorage + newVehicleStorage[storageKey] = storageData + + await setCockpitStorageOnVehicle(vehicleAddress, newVehicleStorage) +} diff --git a/src/stores/widgetManager.ts b/src/stores/widgetManager.ts index cdf1a483a..7c9262b1c 100644 --- a/src/stores/widgetManager.ts +++ b/src/stores/widgetManager.ts @@ -9,6 +9,7 @@ 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' @@ -16,7 +17,10 @@ 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) @@ -159,6 +163,28 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => { reader.readAsText(e.target.files[0]) } + const importProfilesFromVehicle = async (): Promise => { + 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 => { + 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 */ @@ -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) @@ -483,5 +510,7 @@ export const useWidgetManagerStore = defineStore('widget-manager', () => { openWidgetConfigMenu, toggleFullScreen, isFullScreen, + importProfilesFromVehicle, + exportProfilesToVehicle, } })