From 472c831c91fdcb0a19bd93a4f9e2872d774c10df Mon Sep 17 00:00:00 2001 From: tiagohm Date: Tue, 9 Jan 2024 21:34:00 -0300 Subject: [PATCH] [desktop]: Open dialogs using Electron's BrowserWindow as modal --- desktop/app/main.ts | 80 +++++++++---- desktop/app/preload.js | 8 +- desktop/src/app/app.component.html | 10 +- desktop/src/app/app.component.ts | 5 +- desktop/src/app/camera/camera.component.ts | 26 ++--- .../app/filterwheel/filterwheel.component.ts | 34 +++--- .../app/flat-wizard/flat-wizard.component.ts | 7 +- desktop/src/app/framing/framing.component.ts | 2 +- .../src/app/sequencer/sequencer.component.ts | 8 +- .../shared/services/browser-window.service.ts | 108 ++++++++---------- .../src/shared/services/electron.service.ts | 6 +- desktop/src/shared/types/app.types.ts | 19 ++- desktop/src/typings.d.ts | 1 + 13 files changed, 169 insertions(+), 145 deletions(-) diff --git a/desktop/app/main.ts b/desktop/app/main.ts index dbba1cc73..610fe5ba7 100644 --- a/desktop/app/main.ts +++ b/desktop/app/main.ts @@ -7,12 +7,13 @@ import * as path from 'path' import { WebSocket } from 'ws' import { MessageEvent } from '../src/shared/types/api.types' -import { InternalEventType, JsonFile, NotificationEvent, OpenDirectory, OpenFile, OpenWindow } from '../src/shared/types/app.types' +import { CloseWindow, InternalEventType, JsonFile, NotificationEvent, OpenDirectory, OpenFile, OpenWindow } from '../src/shared/types/app.types' Object.assign(global, { WebSocket }) const store = new ElectronStore() const browserWindows = new Map() +const modalWindows = new Map void }>() let api: ChildProcessWithoutNullStreams | null = null let apiPort = 7000 let wsClient: Client @@ -23,8 +24,7 @@ const serve = args.some(e => e === '--serve') app.commandLine.appendSwitch('disable-http-cache') function createMainWindow() { - const splashWindow = browserWindows.get('splash') - splashWindow?.close() + browserWindows.get('splash')?.close() browserWindows.delete('splash') createWindow({ id: 'home', path: 'home', data: undefined }) @@ -62,10 +62,10 @@ function createMainWindow() { wsClient.activate() } -function createWindow(options: OpenWindow) { +function createWindow(options: OpenWindow, parent?: BrowserWindow) { let window = browserWindows.get(options.id) - if (window) { + if (window && !options.modal) { if (options.data) { console.info('window data changed. id=%s, data=%s', options.id, options.data) window.webContents.send('DATA.CHANGED', options.data) @@ -106,7 +106,7 @@ function createWindow(options: OpenWindow) { const icon = options.icon ?? 'nebulosa' const data = encodeURIComponent(JSON.stringify(options.data || {})) - const position = store.get(`window.${options.id}.position`, undefined) as { x: number, y: number } | undefined + const position = !options.modal ? store.get(`window.${options.id}.position`, undefined) as { x: number, y: number } | undefined : undefined if (position) { position.x = Math.max(0, Math.min(position.x, size.width)) @@ -116,6 +116,8 @@ function createWindow(options: OpenWindow) { window = new BrowserWindow({ title: 'Nebulosa', frame: false, + modal: options.modal, + parent, width, height, x: position?.x ?? undefined, y: position?.y ?? undefined, @@ -126,7 +128,7 @@ function createWindow(options: OpenWindow) { nodeIntegration: true, allowRunningInsecureContent: serve, contextIsolation: false, - additionalArguments: [`--port=${apiPort}`], + additionalArguments: [`--port=${apiPort}`, `--id=${options.id}`, `--modal=${options.modal ?? false}`], preload: path.join(__dirname, 'preload.js'), devTools: serve, }, @@ -179,6 +181,13 @@ function createWindow(options: OpenWindow) { break } } + + for (const [key, value] of modalWindows) { + if (value.window === window) { + modalWindows.delete(key) + break + } + } } }) @@ -219,7 +228,8 @@ function showNotification(event: NotificationEvent) { } function findWindowById(id: number) { - for (const [_, window] of browserWindows) if (window.id === id) return window + for (const [key, window] of browserWindows) if (window.id === id) return { window, key } + for (const [key, window] of modalWindows) if (window.window.id === id) return { window: window.window, key } return undefined } @@ -281,8 +291,24 @@ try { } }) - ipcMain.handle('WINDOW.OPEN', async (_, data: OpenWindow) => { - const newWindow = !browserWindows.has(data.id) + ipcMain.handle('WINDOW.OPEN', async (event, data: OpenWindow) => { + if (data.modal) { + const parent = findWindowById(event.sender.id) + const window = createWindow(data, parent?.window) + + const promise = new Promise((resolve) => { + modalWindows.set(data.id, { + window, resolve: (value) => { + window.close() + resolve(value) + } + }) + }) + + return promise + } + + const isNew = !browserWindows.has(data.id) const window = createWindow(data) @@ -293,7 +319,7 @@ try { } return new Promise((resolve) => { - if (newWindow) { + if (isNew) { window.webContents.once('did-finish-load', () => { resolve(true) }) @@ -306,7 +332,7 @@ try { ipcMain.handle('FILE.OPEN', async (event, data?: OpenFile) => { const ownerWindow = findWindowById(event.sender.id) - const value = await dialog.showOpenDialog(ownerWindow!, { + const value = await dialog.showOpenDialog(ownerWindow!.window, { filters: data?.filters, properties: ['openFile'], defaultPath: data?.defaultPath || undefined, @@ -317,7 +343,7 @@ try { ipcMain.handle('FILE.SAVE', async (event, data?: OpenFile) => { const ownerWindow = findWindowById(event.sender.id) - const value = await dialog.showSaveDialog(ownerWindow!, { + const value = await dialog.showSaveDialog(ownerWindow!.window, { filters: data?.filters, properties: ['createDirectory', 'showOverwriteConfirmation'], defaultPath: data?.defaultPath || undefined, @@ -352,7 +378,7 @@ try { ipcMain.handle('DIRECTORY.OPEN', async (event, data?: OpenDirectory) => { const ownerWindow = findWindowById(event.sender.id) - const value = await dialog.showOpenDialog(ownerWindow!, { + const value = await dialog.showOpenDialog(ownerWindow!.window, { properties: ['openDirectory'], defaultPath: data?.defaultPath || undefined, }) @@ -361,25 +387,25 @@ try { }) ipcMain.handle('WINDOW.PIN', (event) => { - const window = findWindowById(event.sender.id) + const window = findWindowById(event.sender.id)?.window window?.setAlwaysOnTop(true) return !!window }) ipcMain.handle('WINDOW.UNPIN', (event) => { - const window = findWindowById(event.sender.id) + const window = findWindowById(event.sender.id)?.window window?.setAlwaysOnTop(false) return !!window }) ipcMain.handle('WINDOW.MINIMIZE', (event) => { - const window = findWindowById(event.sender.id) + const window = findWindowById(event.sender.id)?.window window?.minimize() return !!window }) ipcMain.handle('WINDOW.MAXIMIZE', (event) => { - const window = findWindowById(event.sender.id) + const window = findWindowById(event.sender.id)?.window if (window?.isMaximized()) window.unmaximize() else window?.maximize() @@ -387,21 +413,25 @@ try { return window?.isMaximized() ?? false }) - ipcMain.handle('WINDOW.CLOSE', (event, id?: string) => { - if (id) { + ipcMain.handle('WINDOW.CLOSE', (event, data: CloseWindow) => { + if (data.id) { for (const [key, value] of browserWindows) { - if (key === id) { + if (key === data.id) { value.close() return true } } - - return false } else { const window = findWindowById(event.sender.id) - window?.close() - return !!window + + if (window) { + modalWindows.get(window.key)?.resolve(data.data) + window.window.close() + return true + } } + + return false }) const events: InternalEventType[] = ['WHEEL.RENAMED', 'LOCATION.CHANGED'] diff --git a/desktop/app/preload.js b/desktop/app/preload.js index dfe7137ba..6a80a8492 100644 --- a/desktop/app/preload.js +++ b/desktop/app/preload.js @@ -1,3 +1,7 @@ -const port = process.argv.find(e => e.startsWith('--port=')).split('=')[1] +function argWith(name) { + return process.argv.find(e => e.startsWith(`--${name}=`))?.split('=')?.[1] +} -window.apiPort = parseInt(port) +window.apiPort = parseInt(argWith('port')) +window.id = argWith('id') +window.modal = argWith('modal') === 'true' diff --git a/desktop/src/app/app.component.html b/desktop/src/app/app.component.html index e7e318c4e..36c098d32 100644 --- a/desktop/src/app/app.component.html +++ b/desktop/src/app/app.component.html @@ -13,14 +13,14 @@ - - - - + diff --git a/desktop/src/app/app.component.ts b/desktop/src/app/app.component.ts index 38cabb9bd..ab4b79e16 100644 --- a/desktop/src/app/app.component.ts +++ b/desktop/src/app/app.component.ts @@ -18,6 +18,7 @@ export class AppComponent implements AfterViewInit { pinned = false maximizable = false + readonly modal = window.modal subTitle? = '' backgroundColor = '#212121' topMenu: ExtendedMenuItem[] = [] @@ -64,7 +65,7 @@ export class AppComponent implements AfterViewInit { this.electron.send('WINDOW.MAXIMIZE') } - close() { - this.electron.send('WINDOW.CLOSE') + close(data?: any) { + this.electron.closeWindow({ data }) } } diff --git a/desktop/src/app/camera/camera.component.ts b/desktop/src/app/camera/camera.component.ts index dcf0e7099..18af649d2 100644 --- a/desktop/src/app/camera/camera.component.ts +++ b/desktop/src/app/camera/camera.component.ts @@ -1,13 +1,11 @@ -import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, Optional, ViewChild } from '@angular/core' +import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { MenuItem } from 'primeng/api' -import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog' import { CameraExposureComponent } from '../../shared/components/camera-exposure/camera-exposure.component' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' import { LocalStorageService } from '../../shared/services/local-storage.service' -import { PrimeService } from '../../shared/services/prime.service' import { Camera, CameraDialogInput, CameraDialogMode, CameraPreference, CameraStartCapture, EMPTY_CAMERA, EMPTY_CAMERA_START_CAPTURE, ExposureMode, ExposureTimeUnit, FrameType, cameraPreferenceKey } from '../../shared/types/camera.types' import { FilterWheel } from '../../shared/types/wheel.types' import { AppComponent } from '../app.component' @@ -120,15 +118,13 @@ export class CameraComponent implements AfterContentInit, OnDestroy { private readonly cameraExposure!: CameraExposureComponent constructor( + private app: AppComponent, private api: ApiService, private browserWindow: BrowserWindowService, private electron: ElectronService, private storage: LocalStorageService, private route: ActivatedRoute, ngZone: NgZone, - @Optional() private app?: AppComponent, - @Optional() private dialogRef?: DynamicDialogRef, - @Optional() config?: DynamicDialogConfig, ) { if (app) app.title = 'Camera' @@ -156,14 +152,17 @@ export class CameraComponent implements AfterContentInit, OnDestroy { }) } }) - - this.loadCameraStartCaptureForDialogMode(config?.data) } async ngAfterContentInit() { this.route.queryParams.subscribe(e => { - const camera = JSON.parse(decodeURIComponent(e.data)) as Camera - this.cameraChanged(camera) + const decodedData = JSON.parse(decodeURIComponent(e.data)) + + if (this.app.modal) { + this.loadCameraStartCaptureForDialogMode(decodedData) + } else { + this.cameraChanged(decodedData) + } }) } @@ -363,7 +362,7 @@ export class CameraComponent implements AfterContentInit, OnDestroy { } apply() { - this.dialogRef?.close(this.makeCameraStartCapture()) + this.app.close(this.makeCameraStartCapture()) } private loadPreference() { @@ -428,9 +427,8 @@ export class CameraComponent implements AfterContentInit, OnDestroy { } } - static async showAsDialog(prime: PrimeService, mode: CameraDialogMode, request: CameraStartCapture) { - const data: CameraDialogInput = { mode, request } - const result = await prime.open(CameraComponent, { header: 'Camera', width: 'calc(400px + 2.5rem)', data }) + static async showAsDialog(window: BrowserWindowService, mode: CameraDialogMode, request: CameraStartCapture) { + const result = await window.openCameraDialog({ data: { mode, request } }) if (result) { Object.assign(request, result) diff --git a/desktop/src/app/filterwheel/filterwheel.component.ts b/desktop/src/app/filterwheel/filterwheel.component.ts index ee0467ca9..145597328 100644 --- a/desktop/src/app/filterwheel/filterwheel.component.ts +++ b/desktop/src/app/filterwheel/filterwheel.component.ts @@ -1,12 +1,11 @@ -import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, Optional } from '@angular/core' +import { AfterContentInit, Component, HostListener, NgZone, OnDestroy } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { CheckboxChangeEvent } from 'primeng/checkbox' -import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog' import { Subject, Subscription, debounceTime } from 'rxjs' import { ApiService } from '../../shared/services/api.service' +import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' import { LocalStorageService } from '../../shared/services/local-storage.service' -import { PrimeService } from '../../shared/services/prime.service' import { CameraStartCapture, EMPTY_CAMERA_START_CAPTURE } from '../../shared/types/camera.types' import { EMPTY_WHEEL, FilterSlot, FilterWheel, WheelDialogInput, WheelDialogMode, WheelPreference, wheelPreferenceKey } from '../../shared/types/wheel.types' import { AppComponent } from '../app.component' @@ -52,14 +51,12 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { private subscription?: Subscription constructor( + private app: AppComponent, private api: ApiService, private electron: ElectronService, private storage: LocalStorageService, private route: ActivatedRoute, ngZone: NgZone, - @Optional() private app?: AppComponent, - @Optional() private dialogRef?: DynamicDialogRef, - @Optional() config?: DynamicDialogConfig, ) { if (app) app.title = 'Filter Wheel' @@ -87,18 +84,20 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { this.savePreference() this.electron.send('WHEEL.RENAMED', { wheel: this.wheel, filter }) }) - - if (config?.data) { - Object.assign(this.request, config.data.request) - this.mode = config.data.mode - this.wheelChanged(this.request.wheel) - } } async ngAfterContentInit() { this.route.queryParams.subscribe(e => { - const wheel = JSON.parse(decodeURIComponent(e.data)) as FilterWheel - this.wheelChanged(wheel) + const decodedData = JSON.parse(decodeURIComponent(e.data)) + + if (this.app.modal) { + const request = decodedData as WheelDialogInput + Object.assign(this.request, request.request) + this.mode = request.mode + this.wheelChanged(this.request.wheel) + } else { + this.wheelChanged(decodedData) + } }) } @@ -217,12 +216,11 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { } apply() { - this.dialogRef?.close(this.makeCameraStartCapture()) + this.app.close(this.makeCameraStartCapture()) } - static async showAsDialog(prime: PrimeService, mode: WheelDialogMode, request: CameraStartCapture) { - const data: WheelDialogInput = { mode, request } - const result = await prime.open(FilterWheelComponent, { header: 'Filter Wheel', width: 'calc(320px + 2.5rem)', data }) + static async showAsDialog(window: BrowserWindowService, mode: WheelDialogMode, request: CameraStartCapture) { + const result = await window.openWheelDialog({ data: { mode, request } }) if (result) { Object.assign(request, result) diff --git a/desktop/src/app/flat-wizard/flat-wizard.component.ts b/desktop/src/app/flat-wizard/flat-wizard.component.ts index ec0681759..5b2ddec4a 100644 --- a/desktop/src/app/flat-wizard/flat-wizard.component.ts +++ b/desktop/src/app/flat-wizard/flat-wizard.component.ts @@ -1,12 +1,10 @@ import { AfterViewInit, Component, HostListener, NgZone, OnDestroy, ViewChild } from '@angular/core' -import { ActivatedRoute } from '@angular/router' import { MessageService } from 'primeng/api' import { CameraExposureComponent } from '../../shared/components/camera-exposure/camera-exposure.component' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' import { LocalStorageService } from '../../shared/services/local-storage.service' -import { PrimeService } from '../../shared/services/prime.service' import { Camera, EMPTY_CAMERA_START_CAPTURE } from '../../shared/types/camera.types' import { FlatWizardRequest } from '../../shared/types/flat-wizard.types' import { FilterSlot, FilterWheel, WheelPreference, wheelPreferenceKey } from '../../shared/types/wheel.types' @@ -58,8 +56,6 @@ export class FlatWizardComponent implements AfterViewInit, OnDestroy { electron: ElectronService, private browserWindow: BrowserWindowService, private storage: LocalStorageService, - private route: ActivatedRoute, - private prime: PrimeService, private message: MessageService, ngZone: NgZone, ) { @@ -103,8 +99,7 @@ export class FlatWizardComponent implements AfterViewInit, OnDestroy { ngOnDestroy() { } async showCameraDialog() { - if (await CameraComponent.showAsDialog(this.prime, 'FLAT_WIZARD', this.request.captureRequest)) { - } + CameraComponent.showAsDialog(this.browserWindow, 'FLAT_WIZARD', this.request.captureRequest) } cameraChanged() { diff --git a/desktop/src/app/framing/framing.component.ts b/desktop/src/app/framing/framing.component.ts index b8cc2c5d9..a941ef600 100644 --- a/desktop/src/app/framing/framing.component.ts +++ b/desktop/src/app/framing/framing.component.ts @@ -81,7 +81,7 @@ export class FramingComponent implements AfterViewInit, OnDestroy { @HostListener('window:unload') ngOnDestroy() { this.closeFrameImage() - this.electron.send('WINDOW.CLOSE', this.frameId) + this.electron.closeWindow({ id: this.frameId }) } private frameFromData(data: FramingData) { diff --git a/desktop/src/app/sequencer/sequencer.component.ts b/desktop/src/app/sequencer/sequencer.component.ts index 99fc31b3f..3b3aa011b 100644 --- a/desktop/src/app/sequencer/sequencer.component.ts +++ b/desktop/src/app/sequencer/sequencer.component.ts @@ -1,13 +1,11 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop' import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, QueryList, ViewChildren } from '@angular/core' -import { ActivatedRoute } from '@angular/router' import { MessageService } from 'primeng/api' import { CameraExposureComponent } from '../../shared/components/camera-exposure/camera-exposure.component' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' import { LocalStorageService } from '../../shared/services/local-storage.service' -import { PrimeService } from '../../shared/services/prime.service' import { JsonFile } from '../../shared/types/app.types' import { Camera, CameraCaptureEvent, CameraStartCapture } from '../../shared/types/camera.types' import { Focuser } from '../../shared/types/focuser.types' @@ -72,9 +70,7 @@ export class SequencerComponent implements AfterContentInit, OnDestroy { private browserWindow: BrowserWindowService, private electron: ElectronService, private storage: LocalStorageService, - private route: ActivatedRoute, private message: MessageService, - private prime: PrimeService, ngZone: NgZone, ) { app.title = 'Sequencer' @@ -368,13 +364,13 @@ export class SequencerComponent implements AfterContentInit, OnDestroy { } async showCameraDialog(entry: CameraStartCapture) { - if (await CameraComponent.showAsDialog(this.prime, 'SEQUENCER', entry)) { + if (await CameraComponent.showAsDialog(this.browserWindow, 'SEQUENCER', entry)) { this.savePlan() } } async showWheelDialog(entry: CameraStartCapture) { - if (await FilterWheelComponent.showAsDialog(this.prime, 'SEQUENCER', entry)) { + if (await FilterWheelComponent.showAsDialog(this.browserWindow, 'SEQUENCER', entry)) { this.savePlan() } } diff --git a/desktop/src/shared/services/browser-window.service.ts b/desktop/src/shared/services/browser-window.service.ts index 1d70ab3e7..6767e9e98 100644 --- a/desktop/src/shared/services/browser-window.service.ts +++ b/desktop/src/shared/services/browser-window.service.ts @@ -3,56 +3,60 @@ import { v4 as uuidv4 } from 'uuid' import { SkyAtlasData } from '../../app/atlas/atlas.component' import { FramingData } from '../../app/framing/framing.component' import { ImageData } from '../../app/image/image.component' -import { OpenWindow, OpenWindowOptions } from '../types/app.types' -import { Camera } from '../types/camera.types' +import { OpenWindow, OpenWindowOptions, OpenWindowOptionsWithData } from '../types/app.types' +import { Camera, CameraDialogInput, CameraStartCapture } from '../types/camera.types' import { Device } from '../types/device.types' import { Focuser } from '../types/focuser.types' +import { ImageSource } from '../types/image.types' import { Mount } from '../types/mount.types' -import { FilterWheel } from '../types/wheel.types' +import { FilterWheel, WheelDialogInput } from '../types/wheel.types' import { ElectronService } from './electron.service' -import { ImageSource } from '../types/image.types' @Injectable({ providedIn: 'root' }) export class BrowserWindowService { constructor(private electron: ElectronService) { } - private async openWindow(data: OpenWindow) { - await this.electron.ipcRenderer.invoke('WINDOW.OPEN', data) + openWindow(data: Omit, 'modal'>): Promise { + return this.electron.ipcRenderer.invoke('WINDOW.OPEN', data) + } + + openModal(data: Omit, 'modal' | 'bringToFront' | 'requestFocus'>): Promise { + return this.electron.ipcRenderer.invoke('WINDOW.OPEN', { ...data, modal: true }) } - openMount(options: OpenWindowOptions) { - options.icon ||= 'telescope' - options.width ||= 400 - options.height ||= 469 + openMount(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'telescope', width: 400, height: 469 }) this.openWindow({ ...options, id: `mount.${options.data.name}`, path: 'mount' }) } - openCamera(options: OpenWindowOptions) { - options.icon ||= 'camera' - options.width ||= 400 - options.height ||= 476 - this.openWindow({ ...options, id: `camera.${options.data.name}`, path: 'camera' }) + openCamera(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'camera', width: 400, height: 476 }) + return this.openWindow({ ...options, id: `camera.${options.data.name}`, path: 'camera' }) } - openFocuser(options: OpenWindowOptions) { - options.icon ||= 'focus' - options.width ||= 360 - options.height ||= 203 + openCameraDialog(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'camera', width: 400, height: 424 }) + return this.openModal({ ...options, id: `camera.${options.data.request.camera!.name}.modal`, path: 'camera' }) + } + + openFocuser(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'focus', width: 360, height: 203 }) this.openWindow({ ...options, id: `focuser.${options.data.name}`, path: 'focuser' }) } - openWheel(options: OpenWindowOptions) { - options.icon ||= 'filter-wheel' - options.width ||= 300 - options.height ||= 283 + openWheel(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'filter-wheel', width: 300, height: 283 }) this.openWindow({ ...options, id: `wheel.${options.data.name}`, path: 'wheel' }) } - openGuider(options: Omit, 'data'> = {}) { - options.icon ||= 'guider' - options.width ||= 425 - options.height ||= 450 + openWheelDialog(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'filter-wheel', width: 300, height: 217 }) + return this.openModal({ ...options, id: `wheel.${options.data.request.camera!.name}.modal`, path: 'wheel' }) + } + + openGuider(options: OpenWindowOptions = {}) { + Object.assign(options, { icon: 'guider', width: 425, height: 450 }) this.openWindow({ ...options, id: 'guider', path: 'guider', data: undefined }) } @@ -70,61 +74,43 @@ export class BrowserWindowService { return id } - openINDI(options: OpenWindowOptions) { - options.icon ||= 'indi' - options.width ||= 760 - options.height ||= 420 - options.resizable = true + openINDI(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'indi', width: 760, height: 420, resizable: true }) this.openWindow({ ...options, id: 'indi', path: 'indi' }) } - openSkyAtlas(options: OpenWindowOptions) { - options.icon ||= 'atlas' - options.width ||= 450 - options.height ||= 523 + openSkyAtlas(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'atlas', width: 450, height: 523 }) this.openWindow({ ...options, id: 'atlas', path: 'atlas' }) } - openFraming(options: OpenWindowOptions) { - options.icon ||= 'framing' - options.width ||= 280 - options.height ||= 310 + openFraming(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'framing', width: 280, height: 310 }) this.openWindow({ ...options, id: 'framing', path: 'framing' }) } - openAlignment(options: Omit, 'data'> = {}) { - options.icon ||= 'star' - options.width ||= 400 - options.height ||= 280 + openAlignment(options: OpenWindowOptions = {}) { + Object.assign(options, { icon: 'star', width: 400, height: 280 }) this.openWindow({ ...options, id: 'alignment', path: 'alignment', data: undefined }) } - openSequencer(options: Omit, 'data'> = {}) { - options.icon ||= 'workflow' - options.width ||= 630 - options.height ||= 570 - options.resizable = true + openSequencer(options: OpenWindowOptions = {}) { + Object.assign(options, { icon: 'workflow', width: 630, height: 570, resizable: true }) this.openWindow({ ...options, id: 'sequencer', path: 'sequencer', data: undefined }) } - openFlatWizard(options: Omit, 'data'> = {}) { - options.icon ||= 'star' - options.width ||= 467 - options.height ||= 492 + openFlatWizard(options: OpenWindowOptions = {}) { + Object.assign(options, { icon: 'star', width: 410, height: 330 }) this.openWindow({ ...options, id: 'flat-wizard', path: 'flat-wizard', data: undefined }) } - openSettings(options: Omit, 'data'> = {}) { - options.icon ||= 'settings' - options.width ||= 580 - options.height ||= 445 + openSettings(options: OpenWindowOptions = {}) { + Object.assign(options, { icon: 'settings', width: 580, height: 445 }) this.openWindow({ ...options, id: 'settings', path: 'settings', data: undefined }) } - openCalibration(options: OpenWindowOptions) { - options.icon ||= 'stack' - options.width ||= 510 - options.height ||= 508 + openCalibration(options: OpenWindowOptionsWithData) { + Object.assign(options, { icon: 'stack', width: 510, height: 508 }) this.openWindow({ ...options, id: 'calibration', path: 'calibration' }) } diff --git a/desktop/src/shared/services/electron.service.ts b/desktop/src/shared/services/electron.service.ts index d27502163..5bf1758f5 100644 --- a/desktop/src/shared/services/electron.service.ts +++ b/desktop/src/shared/services/electron.service.ts @@ -9,7 +9,7 @@ import { ipcRenderer, webFrame } from 'electron' import * as fs from 'fs' import { DARVEvent } from '../types/alignment.types' import { ApiEventType, DeviceMessageEvent } from '../types/api.types' -import { InternalEventType, JsonFile, OpenDirectory, OpenFile, SaveJson } from '../types/app.types' +import { CloseWindow, InternalEventType, JsonFile, OpenDirectory, OpenFile, SaveJson } from '../types/app.types' import { Location } from '../types/atlas.types' import { Camera, CameraCaptureEvent } from '../types/camera.types' import { INDIMessageEvent } from '../types/device.types' @@ -155,4 +155,8 @@ export class ElectronService { readJson(path: string): Promise | false> { return this.send('JSON.READ', path) } + + closeWindow(data: CloseWindow) { + return this.send('WINDOW.CLOSE', data) + } } diff --git a/desktop/src/shared/types/app.types.ts b/desktop/src/shared/types/app.types.ts index 5dd3e1336..1caabe7e4 100644 --- a/desktop/src/shared/types/app.types.ts +++ b/desktop/src/shared/types/app.types.ts @@ -27,19 +27,30 @@ export const INTERNAL_EVENT_TYPES = [ ] as const export type InternalEventType = (typeof INTERNAL_EVENT_TYPES)[number] -export interface OpenWindow { - id: string - path: string + +export interface OpenWindowOptions { icon?: string resizable?: boolean width?: number | string height?: number | string bringToFront?: boolean requestFocus?: boolean +} + +export interface OpenWindowOptionsWithData extends OpenWindowOptions { data: T } -export type OpenWindowOptions = Omit, 'id' | 'path'> +export interface OpenWindow extends OpenWindowOptionsWithData { + id: string + path: string + modal?: boolean +} + +export interface CloseWindow { + id?: string + data?: T +} export interface OpenDirectory { defaultPath?: string diff --git a/desktop/src/typings.d.ts b/desktop/src/typings.d.ts index 287b79d8b..271e0b75c 100644 --- a/desktop/src/typings.d.ts +++ b/desktop/src/typings.d.ts @@ -8,4 +8,5 @@ interface Window { process: any require: any apiPort: number + modal: boolean }