diff --git a/app/frontend/src/app/servers/server-list/server-list.component.html b/app/frontend/src/app/servers/server-list/server-list.component.html index 5212f0d..0ee6a98 100644 --- a/app/frontend/src/app/servers/server-list/server-list.component.html +++ b/app/frontend/src/app/servers/server-list/server-list.component.html @@ -3,9 +3,12 @@

Servers

-
+

+
+

+
diff --git a/app/frontend/src/app/servers/server-list/server-list.component.ts b/app/frontend/src/app/servers/server-list/server-list.component.ts index 188cca1..bf4a35d 100644 --- a/app/frontend/src/app/servers/server-list/server-list.component.ts +++ b/app/frontend/src/app/servers/server-list/server-list.component.ts @@ -1,12 +1,11 @@ -import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { trigger, transition, animate, keyframes, style, query } from '@angular/animations'; import { ServersService } from '../servers.service'; import { SubscriptionComponent } from 'src/app/helpers/subscription-component'; import { ElectronService } from 'src/app/messaging/electron.service'; -import { AddOrUpdateConfigCommand } from '../../../../../servers/messages'; -import * as uuid from 'uuid'; import { IServerConfig } from '../../../../../servers/i-server-config'; -import { WebSocketServerConfigMemento } from '../../../../../websocket-servers/i-websocket-server-config'; +import { WorkspaceService } from 'src/app/workspace/workspace.service'; +import { SettingsPageComponent } from 'src/app/settings/settings-page/settings-page.component'; @Component({ selector: 'app-server-list', @@ -31,7 +30,8 @@ export class ServerListComponent extends SubscriptionComponent implements OnInit constructor( private servers: ServersService, - private electron: ElectronService + private electron: ElectronService, + private workspace: WorkspaceService ) { super(); this.recordSubscription(servers.Servers.subscribe(s => { @@ -46,4 +46,8 @@ export class ServerListComponent extends SubscriptionComponent implements OnInit private cancelAdd() { this.adding = false; } + private openSettings() { + let t = this.workspace.OpenNewTab(SettingsPageComponent); + this.workspace.SetTabTitle(t.id, 'Settings'); + } } diff --git a/app/frontend/src/app/servers/servers.module.ts b/app/frontend/src/app/servers/servers.module.ts index dc4bf1c..782181e 100644 --- a/app/frontend/src/app/servers/servers.module.ts +++ b/app/frontend/src/app/servers/servers.module.ts @@ -8,6 +8,7 @@ import { FormsModule } from '@angular/forms'; import { WorkspaceModule } from '../workspace/workspace.module'; import { ControlsModule } from '../controls/controls.module'; import { WebsocketServersModule } from '../websocket-servers/websocket-servers.module'; +import { SettingsModule } from '../settings/settings.module'; @NgModule({ declarations: [ @@ -23,6 +24,7 @@ import { WebsocketServersModule } from '../websocket-servers/websocket-servers.m FormsModule, ControlsModule, WorkspaceModule, + SettingsModule, WebsocketServersModule ], providers: [ diff --git a/app/frontend/src/app/servers/servers.service.ts b/app/frontend/src/app/servers/servers.service.ts index 9dac55f..1a43bf1 100644 --- a/app/frontend/src/app/servers/servers.service.ts +++ b/app/frontend/src/app/servers/servers.service.ts @@ -46,6 +46,8 @@ export class ServersService { if (opened) { let cfg = this.servers.value.find(_ => _.ID === opened.configID); this.currentServerType.next(cfg.GetMemento().TypeID); + } else { + this.currentServerType.next(''); } }); } diff --git a/app/frontend/src/app/settings/settings-page/settings-page.component.html b/app/frontend/src/app/settings/settings-page/settings-page.component.html new file mode 100644 index 0000000..c8f92f5 --- /dev/null +++ b/app/frontend/src/app/settings/settings-page/settings-page.component.html @@ -0,0 +1,10 @@ +
+
+

Updates

+
+
+

About

+

Version 0.1.0

+

License MIT

+
+
\ No newline at end of file diff --git a/app/frontend/src/app/settings/settings-page/settings-page.component.scss b/app/frontend/src/app/settings/settings-page/settings-page.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/app/frontend/src/app/settings/settings-page/settings-page.component.ts b/app/frontend/src/app/settings/settings-page/settings-page.component.ts new file mode 100644 index 0000000..e7b9cbc --- /dev/null +++ b/app/frontend/src/app/settings/settings-page/settings-page.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-settings-page', + templateUrl: './settings-page.component.html', + styleUrls: ['./settings-page.component.scss'] +}) +export class SettingsPageComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/app/frontend/src/app/settings/settings.module.ts b/app/frontend/src/app/settings/settings.module.ts new file mode 100644 index 0000000..0a49fe4 --- /dev/null +++ b/app/frontend/src/app/settings/settings.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SettingsPageComponent } from './settings-page/settings-page.component'; +import { FormsModule } from '@angular/forms'; +import { WorkspaceModule } from '../workspace/workspace.module'; + +@NgModule({ + declarations: [SettingsPageComponent], + exports: [SettingsPageComponent], + entryComponents: [SettingsPageComponent], + imports: [ + CommonModule, + FormsModule, + WorkspaceModule + ] +}) +export class SettingsModule { } diff --git a/app/frontend/src/app/updater/updater.module.ts b/app/frontend/src/app/updater/updater.module.ts new file mode 100644 index 0000000..3333810 --- /dev/null +++ b/app/frontend/src/app/updater/updater.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + declarations: [], + imports: [ + CommonModule + ] +}) +export class UpdaterModule { } diff --git a/app/frontend/src/app/updater/updater.service.ts b/app/frontend/src/app/updater/updater.service.ts new file mode 100644 index 0000000..0108c57 --- /dev/null +++ b/app/frontend/src/app/updater/updater.service.ts @@ -0,0 +1,66 @@ +import { Injectable, EventEmitter } from '@angular/core'; +import { Observable, BehaviorSubject } from 'rxjs'; +import { CalledOnce } from '../helpers/called-once.decorator'; +import { ElectronService } from '../messaging/electron.service'; +import { UpdaterEvent, UpdaterCommand } from '../../../../updater/messages'; + +@Injectable() +export class UpdaterService { + + updateVersion: Observable = new Observable(); + updaterStatus: Observable = new Observable(); + updateDownloadProgress: EventEmitter = new EventEmitter(); + + private statusSubject: BehaviorSubject = new BehaviorSubject(UpdaterStatus.NoUpdateAvailable); + private versionSubject: BehaviorSubject = new BehaviorSubject(''); + constructor( + private electron: ElectronService, + ) { + this.updaterStatus = this.statusSubject.asObservable(); + this.updateVersion = this.versionSubject.asObservable(); + } + @CalledOnce + init() { + this.electron.Receive('UpdaterEvent', (evt: UpdaterEvent) => { + if (evt.Event === 'update-available' && + (this.statusSubject.getValue() === UpdaterStatus.NoUpdateAvailable || this.statusSubject.getValue() === UpdaterStatus.CheckingUpdate)) { + this.versionSubject.next(evt.Arg.version); + this.statusSubject.next(UpdaterStatus.UpdateAvailable); + } else if (evt.Event === 'update-not-available') { + this.statusSubject.next(UpdaterStatus.NoUpdateAvailable); + } else if (evt.Event === 'downloading-update') { + if (this.statusSubject.getValue() !== UpdaterStatus.DownloadingUpdate) { + this.statusSubject.next(UpdaterStatus.DownloadingUpdate); + } + this.updateDownloadProgress.emit(Math.floor(evt.Arg.percentage)); + } else if (evt.Event === 'download-complete') { + this.statusSubject.next(UpdaterStatus.InstallingUpdate); + let that = this; + setTimeout(() => { + that.electron.Send(new UpdaterCommand('commence-install-update')); + }, 7 * 1000); + } else if (evt.Event === 'checking') { + if (evt.Arg.inProgress) { + this.statusSubject.next(UpdaterStatus.CheckingUpdate); + } + } + }); + } + checkUpdate() { + this.electron.Send(new UpdaterCommand('check-for-update')); + } + installUpdate() { + this.statusSubject.next(UpdaterStatus.DownloadingUpdate); + this.electron.Send(new UpdaterCommand('commence-download')); + } + +} + + +export enum UpdaterStatus { + NoUpdateAvailable, + CheckingUpdate, + UpdateAvailable, + DownloadingUpdate, + InstallingUpdate, +} diff --git a/app/main.ts b/app/main.ts index 7029788..8c7ff68 100644 --- a/app/main.ts +++ b/app/main.ts @@ -22,6 +22,7 @@ import { RequestRepository } from "./requests/request-repository"; import { RequestRepositoryMessagingAdapter } from "./requests/request-repository-messaging-adapter"; import { IMessenger } from "./messaging/i-messenger"; import { LoadRequestsCommand } from "./requests/messages"; +import { UpdaterService } from "./updater/updater"; let homeDir = os.homedir(); let appDir = homeDir + "/ReqUI/"; let mainWindow: BrowserWindow; @@ -140,6 +141,7 @@ function initializeComponents() { initializePersistence(); initializeServers(messenger, logger, persistence); initializeRequests(messenger, logger, persistence); + initializeUpdater(messenger); } function initializeMessaging(log: ILogger) { @@ -178,3 +180,7 @@ function registerApplicationMessages() { messenger.Send(new LoadRequestsCommand()); }); } + +function initializeUpdater(message: IMessenger) { + let updater = new UpdaterService(message); +} diff --git a/app/updater/messages.ts b/app/updater/messages.ts new file mode 100644 index 0000000..4e65618 --- /dev/null +++ b/app/updater/messages.ts @@ -0,0 +1,21 @@ +import { IMessage } from "../messaging/i-message"; + +// tslint:disable:max-classes-per-file + +export class UpdaterCommand implements IMessage { + public ID = 'UpdaterCommand' ; + public Command: string; + public constructor(command: string) { + this.Command = command; + } +} + +export class UpdaterEvent implements IMessage { + public ID = 'UpdaterEvent' ; + public Event: string; + public Arg: any; + public constructor(evt: string, arg?: any) { + this.Event = evt; + this.Arg = arg; + } +} diff --git a/app/updater/updater.ts b/app/updater/updater.ts new file mode 100644 index 0000000..00cdeac --- /dev/null +++ b/app/updater/updater.ts @@ -0,0 +1,61 @@ +import { BrowserWindow, app } from "electron"; +import { autoUpdater } from "electron-updater"; +import * as electronLog from "electron-log"; +import { IMessenger } from "../messaging/i-messenger"; +import { UpdaterCommand, UpdaterEvent } from "./messages"; + +autoUpdater.autoDownload = false; +autoUpdater.logger = electronLog; + +// beta channel for testing +autoUpdater.channel = 'beta'; + +export class UpdaterService { + private messenger: IMessenger; + + constructor(messenger: IMessenger) { + this.messenger = messenger; + this.messenger.Receive('UpdaterCommand', (arg: UpdaterCommand) => { + if (arg.Command === 'commence-install-update') { + setImmediate(() => { + app.removeAllListeners("window-all-closed"); + let browserWindows = BrowserWindow.getAllWindows(); + browserWindows.forEach((browserWindow) => { + browserWindow.close(); + }); + autoUpdater.quitAndInstall(); + }); + } else if (arg.Command === 'commence-download') { + autoUpdater.downloadUpdate(); + } else if (arg.Command === 'check-for-update') { + this.checkUpdate(); + } + }); + autoUpdater.on('update-available', (info) => { + this.messenger.Send(new UpdaterEvent('checking', false)); + this.messenger.Send(new UpdaterEvent('update-available', { version: info.version })); + }); + autoUpdater.on('update-not-available', () => { + this.messenger.Send(new UpdaterEvent('checking', false)); + this.messenger.Send(new UpdaterEvent('update-not-available')); + }); + autoUpdater.on('download-progress', (progress) => { + this.messenger.Send(new UpdaterEvent('downloading-update', { percentage: progress.percent })); + }); + autoUpdater.on('update-downloaded', () => { + this.messenger.Send(new UpdaterEvent('download-complete')); + }); + autoUpdater.on('checking-for-update', () => { + this.messenger.Send(new UpdaterEvent('checking', true)); + }); + setInterval(() => { + this.checkUpdate(); + }, 15 * 60 * 1000); + setTimeout(() => { + this.checkUpdate(); + }, 10 * 1000); + } + public checkUpdate() { + autoUpdater.checkForUpdates(); + } +}