From 0344ad2b24f8b043b5848a0fea8b5a120d6781db Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 27 Feb 2024 00:13:27 +0100 Subject: [PATCH] feat(ui): add support for multiple UI server configurations MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit closes #978 Signed-off-by: Jérôme Benoit --- ui/web/src/composables/UIClient.ts | 17 ++++-- ui/web/src/composables/Utils.ts | 9 +++ ui/web/src/composables/index.ts | 2 +- ui/web/src/main.ts | 67 +++++++++++++++-------- ui/web/src/types/ConfigurationType.ts | 2 +- ui/web/src/views/ChargingStationsView.vue | 56 ++++++++++++++++++- 6 files changed, 120 insertions(+), 33 deletions(-) diff --git a/ui/web/src/composables/UIClient.ts b/ui/web/src/composables/UIClient.ts index d569b93b..51d61f26 100644 --- a/ui/web/src/composables/UIClient.ts +++ b/ui/web/src/composables/UIClient.ts @@ -1,3 +1,4 @@ +import { useToast } from 'vue-toast-notification' import { ApplicationProtocol, AuthenticationType, @@ -161,15 +162,21 @@ export class UIClient { `${this.uiServerConfiguration.secure === true ? ApplicationProtocol.WSS : ApplicationProtocol.WS}://${this.uiServerConfiguration.host}:${this.uiServerConfiguration.port}`, protocols ) - this.ws.onopen = openEvent => { - console.info('WebSocket opened', openEvent) + this.ws.onopen = () => { + useToast().success( + `WebSocket to UI server '${this.uiServerConfiguration.host}' successfully opened` + ) } this.ws.onmessage = this.responseHandler.bind(this) this.ws.onerror = errorEvent => { - console.error('WebSocket error: ', errorEvent) + useToast().error(`Error in WebSocket to UI server '${this.uiServerConfiguration.host}'`) + console.error( + `Error in WebSocket to UI server '${this.uiServerConfiguration.host}'`, + errorEvent + ) } - this.ws.onclose = closeEvent => { - console.info('WebSocket closed: ', closeEvent) + this.ws.onclose = () => { + useToast().info(`WebSocket to UI server '${this.uiServerConfiguration.host}' closed`) } } diff --git a/ui/web/src/composables/Utils.ts b/ui/web/src/composables/Utils.ts index 451c4155..fffd7a54 100644 --- a/ui/web/src/composables/Utils.ts +++ b/ui/web/src/composables/Utils.ts @@ -32,3 +32,12 @@ export const convertToInt = (value: unknown): number => { } return changedValue } + +export const setToLocalStorage = (key: string, value: T): void => { + localStorage.setItem(key, JSON.stringify(value)) +} + +export const getFromLocalStorage = (key: string, defaultValue: T): T => { + const item = localStorage.getItem(key) + return item != null ? (JSON.parse(item) as T) : defaultValue +} diff --git a/ui/web/src/composables/index.ts b/ui/web/src/composables/index.ts index 31e231e8..bc62dcb5 100644 --- a/ui/web/src/composables/index.ts +++ b/ui/web/src/composables/index.ts @@ -1,2 +1,2 @@ export { UIClient } from './UIClient' -export { convertToBoolean, convertToInt } from './Utils' +export { convertToBoolean, convertToInt, getFromLocalStorage, setToLocalStorage } from './Utils' diff --git a/ui/web/src/main.ts b/ui/web/src/main.ts index 6dc93195..f3c9ef03 100644 --- a/ui/web/src/main.ts +++ b/ui/web/src/main.ts @@ -1,38 +1,57 @@ -import { createApp } from 'vue' +import { type App as AppType, createApp } from 'vue' import ToastPlugin from 'vue-toast-notification' import type { ConfigurationData, ResponsePayload } from '@/types' import { router } from '@/router' -import { UIClient } from '@/composables' +import { UIClient, getFromLocalStorage, setToLocalStorage } from '@/composables' import App from '@/App.vue' import 'vue-toast-notification/dist/theme-bootstrap.css' -const initializeApp = (config: ConfigurationData) => { - const app = createApp(App) +const app = createApp(App) + +const initializeApp = (app: AppType, config: ConfigurationData) => { app.config.errorHandler = (error, instance, info) => { console.error('Error:', error) console.info('Vue instance:', instance) console.info('Error info:', info) // TODO: add code for UI notifications or other error handling logic } - app.config.globalProperties.$configuration = config - app.config.globalProperties.$chargingStations = [] - app.config.globalProperties.$uiClient = UIClient.getInstance( - app.config.globalProperties.$configuration.uiServer - ) - app.config.globalProperties.$uiClient.registerWSEventListener('open', () => { - app.config.globalProperties.$uiClient - .listChargingStations() - .then((response: ResponsePayload) => { - app.config.globalProperties.$chargingStations = response.chargingStations - }) - .catch((error: Error) => { - // TODO: add code for UI notifications or other error handling logic - console.error('Error at fetching charging stations:', error) - }) - .finally(() => { - app.use(router).use(ToastPlugin).mount('#app') - }) - }) + if (!Array.isArray(config.uiServer)) { + config.uiServer = [config.uiServer] + } + if (app.config.globalProperties.$configuration == null) { + app.config.globalProperties.$configuration = config + } + if (!Array.isArray(app.config.globalProperties.$chargingStations)) { + app.config.globalProperties.$chargingStations = [] + } + if ( + getFromLocalStorage('uiServerConfigurationIndex', undefined) == null || + getFromLocalStorage('uiServerConfigurationIndex', 0) > + app.config.globalProperties.$configuration.uiServer.length - 1 + ) { + setToLocalStorage('uiServerConfigurationIndex', 0) + } + if (app.config.globalProperties.$uiClient == null) { + app.config.globalProperties.$uiClient = UIClient.getInstance( + app.config.globalProperties.$configuration.uiServer[ + getFromLocalStorage('uiServerConfigurationIndex', 0) + ] + ) + app.config.globalProperties.$uiClient.registerWSEventListener('open', () => { + app.config.globalProperties.$uiClient + .listChargingStations() + .then((response: ResponsePayload) => { + app.config.globalProperties.$chargingStations = response.chargingStations + }) + .catch((error: Error) => { + // TODO: add code for UI notifications or other error handling logic + console.error('Error at fetching charging stations:', error) + }) + .finally(() => { + app.use(router).use(ToastPlugin).mount('#app') + }) + }) + } } fetch('/config.json') @@ -46,7 +65,7 @@ fetch('/config.json') .json() .then(config => { try { - initializeApp(config) + initializeApp(app, config) } catch (error) { // TODO: add code for UI notifications or other error handling logic console.error('Error at initializing app:', error) diff --git a/ui/web/src/types/ConfigurationType.ts b/ui/web/src/types/ConfigurationType.ts index 5d0f7679..51bd2138 100644 --- a/ui/web/src/types/ConfigurationType.ts +++ b/ui/web/src/types/ConfigurationType.ts @@ -1,7 +1,7 @@ import type { AuthenticationType, Protocol, ProtocolVersion } from './UIProtocol' export type ConfigurationData = { - uiServer: UIServerConfigurationSection + uiServer: UIServerConfigurationSection | UIServerConfigurationSection[] } export type UIServerConfigurationSection = { diff --git a/ui/web/src/views/ChargingStationsView.vue b/ui/web/src/views/ChargingStationsView.vue index d5868083..fe660111 100644 --- a/ui/web/src/views/ChargingStationsView.vue +++ b/ui/web/src/views/ChargingStationsView.vue @@ -1,5 +1,38 @@