From 34f3f08bd67f0413c7a82865d2b619888a7f718c Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sun, 1 Dec 2024 20:47:16 +0100 Subject: [PATCH] feat: add option to hide other Klipper & Moonraker instances (#2029) --- src/assets/styles/utils.css | 4 + src/components/TheTopCornerMenu.vue | 216 ++++++------------ src/components/dialogs/ConfirmationDialog.vue | 52 +++++ src/components/mixins/services.ts | 21 ++ .../settings/SettingsUiSettingsTab.vue | 15 ++ src/components/ui/TopCornerMenuService.vue | 154 +++++++++++++ src/locales/en.json | 2 + src/store/gui/index.ts | 1 + src/store/gui/types.ts | 1 + src/store/server/types.ts | 4 + 10 files changed, 324 insertions(+), 146 deletions(-) create mode 100644 src/components/dialogs/ConfirmationDialog.vue create mode 100644 src/components/mixins/services.ts create mode 100644 src/components/ui/TopCornerMenuService.vue diff --git a/src/assets/styles/utils.css b/src/assets/styles/utils.css index 65f0b44cd..3a9199d19 100644 --- a/src/assets/styles/utils.css +++ b/src/assets/styles/utils.css @@ -28,6 +28,10 @@ min-width: auto !important; } +.minHeight30 { + min-height: 30px !important; +} + .minHeight36 { min-height: 36px; } diff --git a/src/components/TheTopCornerMenu.vue b/src/components/TheTopCornerMenu.vue index 0690a4439..c613da8c6 100644 --- a/src/components/TheTopCornerMenu.vue +++ b/src/components/TheTopCornerMenu.vue @@ -1,9 +1,3 @@ - - @@ -171,17 +117,10 @@ import { Mixins } from 'vue-property-decorator' import BaseMixin from '@/components/mixins/base' import { ServerPowerStateDevice } from '@/store/server/power/types' import Panel from '@/components/ui/Panel.vue' -import { - mdiAlert, - mdiCloseThick, - mdiPowerStandby, - mdiRestart, - mdiPlay, - mdiPower, - mdiStop, - mdiToggleSwitch, - mdiToggleSwitchOff, -} from '@mdi/js' +import { mdiCloseThick, mdiPowerStandby, mdiRestart, mdiPower, mdiToggleSwitch, mdiToggleSwitchOff } from '@mdi/js' +import TopCornerMenuService from '@/components/ui/TopCornerMenuService.vue' +import ConfirmationDialog from '@/components/dialogs/ConfirmationDialog.vue' +import ServiceMixins from '@/components/mixins/services' interface dialogPowerDeviceChange { show: boolean @@ -199,16 +138,13 @@ interface dialogConfirmation { } @Component({ - components: { Panel }, + components: { ConfirmationDialog, TopCornerMenuService, Panel }, }) -export default class TheTopCornerMenu extends Mixins(BaseMixin) { - mdiAlert = mdiAlert +export default class TheTopCornerMenu extends Mixins(BaseMixin, ServiceMixins) { mdiCloseThick = mdiCloseThick mdiPowerStandby = mdiPowerStandby mdiRestart = mdiRestart - mdiPlay = mdiPlay mdiPower = mdiPower - mdiStop = mdiStop mdiToggleSwitch = mdiToggleSwitch mdiToggleSwitchOff = mdiToggleSwitchOff @@ -229,64 +165,67 @@ export default class TheTopCornerMenu extends Mixins(BaseMixin) { } get services() { - const services = + let services = this.$store.state.server.system_info?.available_services?.filter( (name: string) => name !== 'klipper_mcu' ) ?? [] - services.sort() - return services - } - - get powerDevices() { - const devices = this.$store.getters['server/power/getDevices'] ?? [] - - return devices.filter((device: ServerPowerStateDevice) => !device.device.startsWith('_')) - } - get service_states() { - return this.$store.state.server.system_info?.service_state ?? {} - } + if (this.hideOtherInstances && this.klipperInstance !== '') { + services = services.filter( + (name: string) => + (!name.toLowerCase().startsWith('klipper-') && name.toLowerCase() !== 'klipper') || + name === this.klipperInstance + ) + } - getServiceState(name: string) { - if (name in this.service_states) return this.service_states[name].active_state + if (this.hideOtherInstances && this.moonrakerInstance !== '') { + services = services.filter( + (name: string) => + (!name.toLowerCase().startsWith('moonraker-') && name.toLowerCase() !== 'moonraker') || + name === this.moonrakerInstance + ) + } - return null + return services.sort() } - getServiceSubState(name: string) { - if (name in this.service_states) return this.service_states[name].sub_state + get powerDevices() { + const devices = this.$store.getters['server/power/getDevices'] ?? [] - return null + return devices.filter((device: ServerPowerStateDevice) => !device.device.startsWith('_')) } checkDialog(executableFunction: any, serviceName: string, action: string) { - if (this.printerIsPrinting) { - this.dialogConfirmation.executableFunction = executableFunction - this.dialogConfirmation.serviceName = serviceName + if (!this.printerIsPrinting) { + executableFunction(serviceName) + return + } - const actionUppercase = action.trim().charAt(0).toUpperCase() + action.trim().slice(1) - let titleKey = 'App.TopCornerMenu.ConfirmationDialog.Title.Service' + actionUppercase - let descriptionKey = 'App.TopCornerMenu.ConfirmationDialog.Description.Service' + actionUppercase - let buttonKey = 'App.TopCornerMenu.' + actionUppercase + this.dialogConfirmation.executableFunction = executableFunction + this.dialogConfirmation.serviceName = serviceName - if (serviceName === 'klipper' && ['stop', 'restart', 'firmwareRestart'].includes(action)) { - titleKey = - 'App.TopCornerMenu.ConfirmationDialog.Title.' + - (action !== 'stop' ? 'Klipper' : 'Service') + - actionUppercase - descriptionKey = 'App.TopCornerMenu.ConfirmationDialog.Description.Klipper' + actionUppercase + const actionUppercase = action.trim().charAt(0).toUpperCase() + action.trim().slice(1) + let titleKey = 'App.TopCornerMenu.ConfirmationDialog.Title.Service' + actionUppercase + let descriptionKey = 'App.TopCornerMenu.ConfirmationDialog.Description.Service' + actionUppercase + let buttonKey = 'App.TopCornerMenu.' + actionUppercase - if (action === 'firmwareRestart') buttonKey = 'App.TopCornerMenu.KlipperFirmwareRestart' - } else if (serviceName === 'host') { - titleKey = 'App.TopCornerMenu.ConfirmationDialog.Title.Host' + actionUppercase - descriptionKey = 'App.TopCornerMenu.ConfirmationDialog.Description.Host' + actionUppercase - } + if (serviceName === 'klipper' && ['stop', 'restart', 'firmwareRestart'].includes(action)) { + titleKey = + 'App.TopCornerMenu.ConfirmationDialog.Title.' + + (action !== 'stop' ? 'Klipper' : 'Service') + + actionUppercase + descriptionKey = 'App.TopCornerMenu.ConfirmationDialog.Description.Klipper' + actionUppercase + + if (action === 'firmwareRestart') buttonKey = 'App.TopCornerMenu.KlipperFirmwareRestart' + } else if (serviceName === 'host') { + titleKey = 'App.TopCornerMenu.ConfirmationDialog.Title.Host' + actionUppercase + descriptionKey = 'App.TopCornerMenu.ConfirmationDialog.Description.Host' + actionUppercase + } - this.dialogConfirmation.title = this.$t(titleKey).toString() - this.dialogConfirmation.description = this.$t(descriptionKey).toString() - this.dialogConfirmation.actionButtonText = this.$t(buttonKey).toString() - this.dialogConfirmation.show = true - } else executableFunction(serviceName) + this.dialogConfirmation.title = this.$t(titleKey).toString() + this.dialogConfirmation.description = this.$t(descriptionKey).toString() + this.dialogConfirmation.actionButtonText = this.$t(buttonKey).toString() + this.dialogConfirmation.show = true } executeDialog() { @@ -306,21 +245,6 @@ export default class TheTopCornerMenu extends Mixins(BaseMixin) { this.$socket.emit('printer.gcode.script', { script: 'FIRMWARE_RESTART' }) } - serviceStart(service: string) { - this.showMenu = false - this.$socket.emit('machine.services.start', { service: service }) - } - - serviceRestart(service: string) { - this.showMenu = false - this.$socket.emit('machine.services.restart', { service: service }) - } - - serviceStop(service: string) { - this.showMenu = false - this.$socket.emit('machine.services.stop', { service: service }) - } - changeSwitch(device: ServerPowerStateDevice, value: string) { this.dialogPowerDeviceChange.device = device.device this.dialogPowerDeviceChange.value = value diff --git a/src/components/dialogs/ConfirmationDialog.vue b/src/components/dialogs/ConfirmationDialog.vue new file mode 100644 index 000000000..e53a62080 --- /dev/null +++ b/src/components/dialogs/ConfirmationDialog.vue @@ -0,0 +1,52 @@ + + diff --git a/src/components/mixins/services.ts b/src/components/mixins/services.ts new file mode 100644 index 000000000..808f9d4d3 --- /dev/null +++ b/src/components/mixins/services.ts @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Component from 'vue-class-component' + +@Component +export default class ServiceMixins extends Vue { + get hideOtherInstances() { + return this.$store.state.gui.uiSettings.hideOtherInstances ?? false + } + + get instance_ids() { + return this.$store.state.server.system_info?.instance_ids ?? {} + } + + get klipperInstance() { + return this.instance_ids.klipper ?? '' + } + + get moonrakerInstance() { + return this.instance_ids.moonraker ?? '' + } +} diff --git a/src/components/settings/SettingsUiSettingsTab.vue b/src/components/settings/SettingsUiSettingsTab.vue index a05410af7..1434c512c 100644 --- a/src/components/settings/SettingsUiSettingsTab.vue +++ b/src/components/settings/SettingsUiSettingsTab.vue @@ -322,6 +322,13 @@ $t('Settings.UiSettingsTab.DashboardHistoryLimitLabel', { count: dashboardHistoryLimit }) " /> + + + + @@ -696,6 +703,14 @@ export default class SettingsUiSettingsTab extends Mixins(BaseMixin, ThemeMixin) this.$store.dispatch('gui/saveSetting', { name: 'uiSettings.dashboardHistoryLimit', value: newVal }) } + get hideOtherInstances() { + return this.$store.state.gui.uiSettings.hideOtherInstances ?? false + } + + set hideOtherInstances(newVal) { + this.$store.dispatch('gui/saveSetting', { name: 'uiSettings.hideOtherInstances', value: newVal }) + } + clearColorObject(color: any): string { if (typeof color === 'object' && 'hex' in color) color = color.hex if (color.length > 7) color = color.substr(0, 7) diff --git a/src/components/ui/TopCornerMenuService.vue b/src/components/ui/TopCornerMenuService.vue new file mode 100644 index 000000000..e87dcc78a --- /dev/null +++ b/src/components/ui/TopCornerMenuService.vue @@ -0,0 +1,154 @@ + + diff --git a/src/locales/en.json b/src/locales/en.json index c2d57aabf..3bec5b39f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1253,6 +1253,8 @@ "GcodeThumbnails": "G-Code thumbnails", "GcodeThumbnailsDescription": "Click on the button to get to the instructions.", "Guide": "Guide", + "HideOtherInstances": "Hide other instances", + "HideOtherInstancesDescription": "Hide other instances of Klipper & Moonraker in the Service Menu.", "HideSaveConfigButtonForBedMesh": "Hide SAVE_CONFIG button for bed_mesh changes", "HideSaveConfigButtonForBedMeshDescription": "Hide SAVE_CONFIG, if only bed_mesh changes are pending to be saved in Klipper.", "HideUpdateWarnings": "Hide Update Warnings", diff --git a/src/store/gui/index.ts b/src/store/gui/index.ts index a7f011147..30f34ded6 100644 --- a/src/store/gui/index.ts +++ b/src/store/gui/index.ts @@ -186,6 +186,7 @@ export const getDefaultState = (): GuiState => { dashboardFilesLimit: 5, dashboardFilesFilter: ['new', 'failed', 'completed'], dashboardHistoryLimit: 5, + hideOtherInstances: false, }, view: { blockFileUpload: false, diff --git a/src/store/gui/types.ts b/src/store/gui/types.ts index ed8f0dcda..920821188 100644 --- a/src/store/gui/types.ts +++ b/src/store/gui/types.ts @@ -128,6 +128,7 @@ export interface GuiState { dashboardFilesLimit: number dashboardFilesFilter: GuiStateUiSettingsDashboardFilesFilter[] dashboardHistoryLimit: number + hideOtherInstances: boolean } view: { blockFileUpload: boolean diff --git a/src/store/server/types.ts b/src/store/server/types.ts index 69d6fe399..6f1a354e1 100644 --- a/src/store/server/types.ts +++ b/src/store/server/types.ts @@ -33,6 +33,10 @@ export interface ServerState { [key: string]: ServerStateNetwork } system_uptime: number | null + instance_ids: { + moonraker: string + klipper: string + } } | null system_boot_at: Date | null moonraker_stats: {